
Type: refactor Change-Id: I5235bf3e9aff58af6ba2c14e8c6529c4fc9ec86c Signed-off-by: Damjan Marion <damarion@cisco.com>
361 lines
8.9 KiB
C
361 lines
8.9 KiB
C
|
|
/*
|
|
* Copyright (c) 2016 Cisco and/or its affiliates.
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at:
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
#include <vnet/vnet.h>
|
|
#include <vnet/hash/hash.h>
|
|
#include <vlib/threads.h>
|
|
#include <vnet/feature/feature.h>
|
|
|
|
typedef struct
|
|
{
|
|
vnet_hash_fn_t hash_fn;
|
|
uword *workers_bitmap;
|
|
u32 *workers;
|
|
} per_inteface_handoff_data_t;
|
|
|
|
typedef struct
|
|
{
|
|
u32 cached_next_index;
|
|
u32 num_workers;
|
|
u32 first_worker_index;
|
|
|
|
per_inteface_handoff_data_t *if_data;
|
|
|
|
/* Worker handoff index */
|
|
u32 frame_queue_index;
|
|
} handoff_main_t;
|
|
|
|
extern handoff_main_t handoff_main;
|
|
|
|
#ifndef CLIB_MARCH_VARIANT
|
|
|
|
handoff_main_t handoff_main;
|
|
|
|
#endif /* CLIB_MARCH_VARIANT */
|
|
|
|
typedef struct
|
|
{
|
|
u32 sw_if_index;
|
|
u32 next_worker_index;
|
|
u32 buffer_index;
|
|
} worker_handoff_trace_t;
|
|
|
|
#define foreach_worker_handoff_error \
|
|
_(CONGESTION_DROP, "congestion drop")
|
|
|
|
typedef enum
|
|
{
|
|
#define _(sym,str) WORKER_HANDOFF_ERROR_##sym,
|
|
foreach_worker_handoff_error
|
|
#undef _
|
|
WORKER_HANDOFF_N_ERROR,
|
|
} worker_handoff_error_t;
|
|
|
|
static char *worker_handoff_error_strings[] = {
|
|
#define _(sym,string) string,
|
|
foreach_worker_handoff_error
|
|
#undef _
|
|
};
|
|
|
|
/* packet trace format function */
|
|
static u8 *
|
|
format_worker_handoff_trace (u8 * s, va_list * args)
|
|
{
|
|
CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
|
|
CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
|
|
worker_handoff_trace_t *t = va_arg (*args, worker_handoff_trace_t *);
|
|
|
|
s = format (s, "worker-handoff: sw_if_index %d, next_worker %d, buffer 0x%x",
|
|
t->sw_if_index, t->next_worker_index, t->buffer_index);
|
|
return s;
|
|
}
|
|
|
|
static void
|
|
worker_handoff_trace_frame (vlib_main_t *vm, vlib_node_runtime_t *node,
|
|
vlib_buffer_t **bufs, u16 *threads, u32 n_vectors)
|
|
{
|
|
worker_handoff_trace_t *t;
|
|
vlib_buffer_t **b;
|
|
u16 *ti;
|
|
|
|
b = bufs;
|
|
ti = threads;
|
|
|
|
while (n_vectors)
|
|
{
|
|
t = vlib_add_trace (vm, node, b[0], sizeof (*t));
|
|
t->sw_if_index = vnet_buffer (b[0])->sw_if_index[VLIB_RX];
|
|
t->next_worker_index = ti[0];
|
|
t->buffer_index = vlib_get_buffer_index (vm, b[0]);
|
|
|
|
b += 1;
|
|
ti += 1;
|
|
n_vectors -= 1;
|
|
}
|
|
}
|
|
|
|
VLIB_NODE_FN (worker_handoff_node) (vlib_main_t * vm,
|
|
vlib_node_runtime_t * node,
|
|
vlib_frame_t * frame)
|
|
{
|
|
handoff_main_t *hm = &handoff_main;
|
|
vlib_buffer_t *bufs[VLIB_FRAME_SIZE], **b;
|
|
u32 n_enq, n_left_from, *from;
|
|
u16 thread_indices[VLIB_FRAME_SIZE], *ti;
|
|
|
|
from = vlib_frame_vector_args (frame);
|
|
n_left_from = frame->n_vectors;
|
|
vlib_get_buffers (vm, from, bufs, n_left_from);
|
|
|
|
b = bufs;
|
|
ti = thread_indices;
|
|
|
|
while (n_left_from > 0)
|
|
{
|
|
per_inteface_handoff_data_t *ihd0;
|
|
u32 sw_if_index0, hash, index0;
|
|
void *data;
|
|
|
|
sw_if_index0 = vnet_buffer (b[0])->sw_if_index[VLIB_RX];
|
|
ihd0 = vec_elt_at_index (hm->if_data, sw_if_index0);
|
|
|
|
/* Compute ingress LB hash */
|
|
data = vlib_buffer_get_current (b[0]);
|
|
ihd0->hash_fn (&data, &hash, 1);
|
|
|
|
/* if input node did not specify next index, then packet
|
|
should go to ethernet-input */
|
|
|
|
if (PREDICT_TRUE (is_pow2 (vec_len (ihd0->workers))))
|
|
index0 = hash & (vec_len (ihd0->workers) - 1);
|
|
else
|
|
index0 = hash % vec_len (ihd0->workers);
|
|
|
|
ti[0] = hm->first_worker_index + ihd0->workers[index0];
|
|
|
|
/* next */
|
|
n_left_from -= 1;
|
|
ti += 1;
|
|
b += 1;
|
|
}
|
|
|
|
if (PREDICT_FALSE (node->flags & VLIB_NODE_FLAG_TRACE))
|
|
worker_handoff_trace_frame (vm, node, bufs, thread_indices,
|
|
frame->n_vectors);
|
|
|
|
n_enq = vlib_buffer_enqueue_to_thread (vm, node, hm->frame_queue_index, from,
|
|
thread_indices, frame->n_vectors, 1);
|
|
|
|
if (n_enq < frame->n_vectors)
|
|
vlib_node_increment_counter (vm, node->node_index,
|
|
WORKER_HANDOFF_ERROR_CONGESTION_DROP,
|
|
frame->n_vectors - n_enq);
|
|
return frame->n_vectors;
|
|
}
|
|
|
|
VLIB_REGISTER_NODE (worker_handoff_node) = {
|
|
.name = "worker-handoff",
|
|
.vector_size = sizeof (u32),
|
|
.format_trace = format_worker_handoff_trace,
|
|
.type = VLIB_NODE_TYPE_INTERNAL,
|
|
.n_errors = ARRAY_LEN(worker_handoff_error_strings),
|
|
.error_strings = worker_handoff_error_strings,
|
|
|
|
.n_next_nodes = 1,
|
|
.next_nodes = {
|
|
[0] = "error-drop",
|
|
},
|
|
};
|
|
|
|
#ifndef CLIB_MARCH_VARIANT
|
|
|
|
int
|
|
interface_handoff_enable_disable (vlib_main_t *vm, u32 sw_if_index,
|
|
uword *bitmap, u8 is_sym, int is_l4,
|
|
int enable_disable)
|
|
{
|
|
handoff_main_t *hm = &handoff_main;
|
|
vnet_sw_interface_t *sw;
|
|
vnet_main_t *vnm = vnet_get_main ();
|
|
per_inteface_handoff_data_t *d;
|
|
int i, rv = 0;
|
|
|
|
if (pool_is_free_index (vnm->interface_main.sw_interfaces, sw_if_index))
|
|
return VNET_API_ERROR_INVALID_SW_IF_INDEX;
|
|
|
|
sw = vnet_get_sw_interface (vnm, sw_if_index);
|
|
if (sw->type != VNET_SW_INTERFACE_TYPE_HARDWARE)
|
|
return VNET_API_ERROR_INVALID_SW_IF_INDEX;
|
|
|
|
if (clib_bitmap_last_set (bitmap) >= hm->num_workers)
|
|
return VNET_API_ERROR_INVALID_WORKER;
|
|
|
|
if (hm->frame_queue_index == ~0)
|
|
{
|
|
vlib_node_t *n = vlib_get_node_by_name (vm, (u8 *) "ethernet-input");
|
|
hm->frame_queue_index = vlib_frame_queue_main_init (n->index, 0);
|
|
}
|
|
|
|
vec_validate (hm->if_data, sw_if_index);
|
|
d = vec_elt_at_index (hm->if_data, sw_if_index);
|
|
|
|
vec_free (d->workers);
|
|
vec_free (d->workers_bitmap);
|
|
|
|
if (enable_disable)
|
|
{
|
|
d->workers_bitmap = bitmap;
|
|
clib_bitmap_foreach (i, bitmap)
|
|
{
|
|
vec_add1(d->workers, i);
|
|
}
|
|
|
|
if (is_sym)
|
|
{
|
|
if (is_l4)
|
|
return VNET_API_ERROR_UNIMPLEMENTED;
|
|
|
|
d->hash_fn = vnet_hash_function_from_name (
|
|
"handoff-eth-sym", VNET_HASH_FN_TYPE_ETHERNET);
|
|
}
|
|
else
|
|
{
|
|
if (is_l4)
|
|
d->hash_fn =
|
|
vnet_hash_default_function (VNET_HASH_FN_TYPE_ETHERNET);
|
|
else
|
|
d->hash_fn = vnet_hash_function_from_name (
|
|
"handoff-eth", VNET_HASH_FN_TYPE_ETHERNET);
|
|
}
|
|
}
|
|
|
|
vnet_feature_enable_disable ("device-input", "worker-handoff",
|
|
sw_if_index, enable_disable, 0, 0);
|
|
vnet_feature_enable_disable ("port-rx-eth", "worker-handoff", sw_if_index,
|
|
enable_disable, 0, 0);
|
|
return rv;
|
|
}
|
|
|
|
static clib_error_t *
|
|
set_interface_handoff_command_fn (vlib_main_t * vm,
|
|
unformat_input_t * input,
|
|
vlib_cli_command_t * cmd)
|
|
{
|
|
u32 sw_if_index = ~0, is_sym = 0, is_l4 = 0;
|
|
int enable_disable = 1;
|
|
uword *bitmap = 0;
|
|
int rv = 0;
|
|
|
|
while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
|
|
{
|
|
if (unformat (input, "disable"))
|
|
enable_disable = 0;
|
|
else if (unformat (input, "workers %U", unformat_bitmap_list, &bitmap))
|
|
;
|
|
else if (unformat (input, "%U", unformat_vnet_sw_interface,
|
|
vnet_get_main (), &sw_if_index))
|
|
;
|
|
else if (unformat (input, "symmetrical"))
|
|
is_sym = 1;
|
|
else if (unformat (input, "asymmetrical"))
|
|
is_sym = 0;
|
|
else if (unformat (input, "l4"))
|
|
is_l4 = 1;
|
|
else
|
|
break;
|
|
}
|
|
|
|
if (sw_if_index == ~0)
|
|
return clib_error_return (0, "Please specify an interface...");
|
|
|
|
if (bitmap == 0)
|
|
return clib_error_return (0, "Please specify list of workers...");
|
|
|
|
rv = interface_handoff_enable_disable (vm, sw_if_index, bitmap, is_sym,
|
|
is_l4, enable_disable);
|
|
|
|
switch (rv)
|
|
{
|
|
case 0:
|
|
break;
|
|
|
|
case VNET_API_ERROR_INVALID_SW_IF_INDEX:
|
|
return clib_error_return (0, "Invalid interface");
|
|
break;
|
|
|
|
case VNET_API_ERROR_INVALID_WORKER:
|
|
return clib_error_return (0, "Invalid worker(s)");
|
|
break;
|
|
|
|
case VNET_API_ERROR_UNIMPLEMENTED:
|
|
return clib_error_return (0,
|
|
"Device driver doesn't support redirection");
|
|
break;
|
|
|
|
default:
|
|
return clib_error_return (0, "unknown return value %d", rv);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
VLIB_CLI_COMMAND (set_interface_handoff_command, static) = {
|
|
.path = "set interface handoff",
|
|
.short_help = "set interface handoff <interface-name> workers <workers-list>"
|
|
" [symmetrical|asymmetrical]",
|
|
.function = set_interface_handoff_command_fn,
|
|
};
|
|
|
|
clib_error_t *
|
|
handoff_init (vlib_main_t * vm)
|
|
{
|
|
handoff_main_t *hm = &handoff_main;
|
|
vlib_thread_main_t *tm = vlib_get_thread_main ();
|
|
clib_error_t *error;
|
|
uword *p;
|
|
|
|
if ((error = vlib_call_init_function (vm, threads_init)))
|
|
return error;
|
|
|
|
vlib_thread_registration_t *tr;
|
|
/* Only the standard vnet worker threads are supported */
|
|
p = hash_get_mem (tm->thread_registrations_by_name, "workers");
|
|
if (p)
|
|
{
|
|
tr = (vlib_thread_registration_t *) p[0];
|
|
if (tr)
|
|
{
|
|
hm->num_workers = tr->count;
|
|
hm->first_worker_index = tr->first_index;
|
|
}
|
|
}
|
|
|
|
hm->frame_queue_index = ~0;
|
|
|
|
return 0;
|
|
}
|
|
|
|
VLIB_INIT_FUNCTION (handoff_init);
|
|
|
|
#endif /* CLIB_MARCH_VARIANT */
|
|
/*
|
|
* fd.io coding-style-patch-verification: ON
|
|
*
|
|
* Local Variables:
|
|
* eval: (c-set-style "gnu")
|
|
* End:
|
|
*/
|