FD.io VPP  v19.01.3-6-g70449b9b9
Vector Packet Processing
handoff.c
Go to the documentation of this file.
1 
2 /*
3  * Copyright (c) 2016 Cisco and/or its affiliates.
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at:
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include <vnet/vnet.h>
18 #include <vppinfra/xxhash.h>
19 #include <vlib/threads.h>
20 #include <vnet/handoff.h>
21 #include <vnet/feature/feature.h>
22 
23 typedef struct
24 {
28 
29 typedef struct
30 {
34 
36 
37  /* Worker handoff index */
39 
40  u64 (*hash_fn) (ethernet_header_t *);
42 
44 
45 typedef struct
46 {
51 
52 #define foreach_worker_handoff_error \
53  _(CONGESTION_DROP, "congestion drop")
54 
55 typedef enum
56 {
57 #define _(sym,str) WORKER_HANDOFF_ERROR_##sym,
59 #undef _
62 
63 static char *worker_handoff_error_strings[] = {
64 #define _(sym,string) string,
66 #undef _
67 };
68 
69 /* packet trace format function */
70 static u8 *
71 format_worker_handoff_trace (u8 * s, va_list * args)
72 {
73  CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
74  CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
75  worker_handoff_trace_t *t = va_arg (*args, worker_handoff_trace_t *);
76 
77  s =
78  format (s, "worker-handoff: sw_if_index %d, next_worker %d, buffer 0x%x",
80  return s;
81 }
82 
84 
85 static uword
87  vlib_node_runtime_t * node, vlib_frame_t * frame)
88 {
90  vlib_buffer_t *bufs[VLIB_FRAME_SIZE], **b;
91  u32 n_enq, n_left_from, *from;
92  u16 thread_indices[VLIB_FRAME_SIZE], *ti;
93 
94  from = vlib_frame_vector_args (frame);
95  n_left_from = frame->n_vectors;
96  vlib_get_buffers (vm, from, bufs, n_left_from);
97 
98  b = bufs;
99  ti = thread_indices;
100 
101  while (n_left_from > 0)
102  {
103  u32 sw_if_index0;
104  u32 hash;
105  u64 hash_key;
107  u32 index0;
108 
109 
110  sw_if_index0 = vnet_buffer (b[0])->sw_if_index[VLIB_RX];
111  ASSERT (hm->if_data);
112  ihd0 = vec_elt_at_index (hm->if_data, sw_if_index0);
113 
114  /*
115  * Force unknown traffic onto worker 0,
116  * and into ethernet-input. $$$$ add more hashes.
117  */
118 
119  /* Compute ingress LB hash */
120  hash_key = hm->hash_fn ((ethernet_header_t *)
121  vlib_buffer_get_current (b[0]));
122  hash = (u32) clib_xxhash (hash_key);
123 
124  /* if input node did not specify next index, then packet
125  should go to ethernet-input */
126 
127  if (PREDICT_TRUE (is_pow2 (vec_len (ihd0->workers))))
128  index0 = hash & (vec_len (ihd0->workers) - 1);
129  else
130  index0 = hash % vec_len (ihd0->workers);
131 
132  ti[0] = hm->first_worker_index + ihd0->workers[index0];
133 
135  && (b[0]->flags & VLIB_BUFFER_IS_TRACED)))
136  {
138  vlib_add_trace (vm, node, b[0], sizeof (*t));
139  t->sw_if_index = sw_if_index0;
140  t->next_worker_index = ti[0];
141  t->buffer_index = vlib_get_buffer_index (vm, b[0]);
142  }
143 
144  /* next */
145  n_left_from -= 1;
146  ti += 1;
147  b += 1;
148  }
149 
150  n_enq = vlib_buffer_enqueue_to_thread (vm, hm->frame_queue_index, from,
151  thread_indices, frame->n_vectors, 1);
152 
153  if (n_enq < frame->n_vectors)
155  WORKER_HANDOFF_ERROR_CONGESTION_DROP,
156  frame->n_vectors - n_enq);
157  return frame->n_vectors;
158 }
159 
160 /* *INDENT-OFF* */
162  .function = worker_handoff_node_fn,
163  .name = "worker-handoff",
164  .vector_size = sizeof (u32),
165  .format_trace = format_worker_handoff_trace,
166  .type = VLIB_NODE_TYPE_INTERNAL,
168  .error_strings = worker_handoff_error_strings,
169 
170  .n_next_nodes = 1,
171  .next_nodes = {
172  [0] = "error-drop",
173  },
174 };
175 
177 /* *INDENT-ON* */
178 
179 int
181  uword * bitmap, int enable_disable)
182 {
185  vnet_main_t *vnm = vnet_get_main ();
187  int i, rv = 0;
188 
189  if (pool_is_free_index (vnm->interface_main.sw_interfaces, sw_if_index))
190  return VNET_API_ERROR_INVALID_SW_IF_INDEX;
191 
192  sw = vnet_get_sw_interface (vnm, sw_if_index);
194  return VNET_API_ERROR_INVALID_SW_IF_INDEX;
195 
196  if (clib_bitmap_last_set (bitmap) >= hm->num_workers)
197  return VNET_API_ERROR_INVALID_WORKER;
198 
199  if (hm->frame_queue_index == ~0)
200  {
201  vlib_node_t *n = vlib_get_node_by_name (vm, (u8 *) "ethernet-input");
203  }
204 
205  vec_validate (hm->if_data, sw_if_index);
206  d = vec_elt_at_index (hm->if_data, sw_if_index);
207 
208  vec_free (d->workers);
210 
211  if (enable_disable)
212  {
213  d->workers_bitmap = bitmap;
214  /* *INDENT-OFF* */
215  clib_bitmap_foreach (i, bitmap,
216  ({
217  vec_add1(d->workers, i);
218  }));
219  /* *INDENT-ON* */
220  }
221 
222  vnet_feature_enable_disable ("device-input", "worker-handoff",
223  sw_if_index, enable_disable, 0, 0);
224  return rv;
225 }
226 
227 static clib_error_t *
229  unformat_input_t * input,
230  vlib_cli_command_t * cmd)
231 {
233  u32 sw_if_index = ~0;
234  int enable_disable = 1;
235  uword *bitmap = 0;
236  u32 sym = ~0;
237 
238  int rv = 0;
239 
241  {
242  if (unformat (input, "disable"))
243  enable_disable = 0;
244  else if (unformat (input, "workers %U", unformat_bitmap_list, &bitmap))
245  ;
246  else if (unformat (input, "%U", unformat_vnet_sw_interface,
247  vnet_get_main (), &sw_if_index))
248  ;
249  else if (unformat (input, "symmetrical"))
250  sym = 1;
251  else if (unformat (input, "asymmetrical"))
252  sym = 0;
253  else
254  break;
255  }
256 
257  if (sw_if_index == ~0)
258  return clib_error_return (0, "Please specify an interface...");
259 
260  if (bitmap == 0)
261  return clib_error_return (0, "Please specify list of workers...");
262 
263  rv =
264  interface_handoff_enable_disable (vm, sw_if_index, bitmap,
265  enable_disable);
266 
267  switch (rv)
268  {
269  case 0:
270  break;
271 
272  case VNET_API_ERROR_INVALID_SW_IF_INDEX:
273  return clib_error_return (0, "Invalid interface");
274  break;
275 
276  case VNET_API_ERROR_INVALID_WORKER:
277  return clib_error_return (0, "Invalid worker(s)");
278  break;
279 
280  case VNET_API_ERROR_UNIMPLEMENTED:
281  return clib_error_return (0,
282  "Device driver doesn't support redirection");
283  break;
284 
285  default:
286  return clib_error_return (0, "unknown return value %d", rv);
287  }
288 
289  if (sym == 1)
290  hm->hash_fn = eth_get_sym_key;
291  else if (sym == 0)
292  hm->hash_fn = eth_get_key;
293 
294  return 0;
295 }
296 
297 /* *INDENT-OFF* */
298 VLIB_CLI_COMMAND (set_interface_handoff_command, static) = {
299  .path = "set interface handoff",
300  .short_help =
301  "set interface handoff <interface-name> workers <workers-list> [symmetrical|asymmetrical]",
303 };
304 /* *INDENT-ON* */
305 
306 clib_error_t *
308 {
311  clib_error_t *error;
312  uword *p;
313 
314  if ((error = vlib_call_init_function (vm, threads_init)))
315  return error;
316 
318  /* Only the standard vnet worker threads are supported */
319  p = hash_get_mem (tm->thread_registrations_by_name, "workers");
320  if (p)
321  {
322  tr = (vlib_thread_registration_t *) p[0];
323  if (tr)
324  {
325  hm->num_workers = tr->count;
327  }
328  }
329 
330  hm->hash_fn = eth_get_key;
331  hm->frame_queue_index = ~0;
332 
333  return 0;
334 }
335 
337 
338 /*
339  * fd.io coding-style-patch-verification: ON
340  *
341  * Local Variables:
342  * eval: (c-set-style "gnu")
343  * End:
344  */
#define vec_validate(V, I)
Make sure vector is long enough for given index (no header, unspecified alignment) ...
Definition: vec.h:439
u32 cached_next_index
Definition: handoff.c:31
#define CLIB_UNUSED(x)
Definition: clib.h:82
handoff_main_t handoff_main
Definition: handoff.c:43
u64(* hash_fn)(ethernet_header_t *)
Definition: handoff.c:40
u32 num_workers
Definition: handoff.c:32
vnet_main_t * vnet_get_main(void)
Definition: misc.c:47
vnet_interface_main_t interface_main
Definition: vnet.h:56
#define PREDICT_TRUE(x)
Definition: clib.h:112
unsigned long u64
Definition: types.h:89
u32 index
Definition: node.h:304
clib_error_t * threads_init(vlib_main_t *vm)
Definition: threads.c:1818
u32 vlib_frame_queue_main_init(u32 node_index, u32 frame_queue_nelts)
Definition: threads.c:1743
#define vec_add1(V, E)
Add 1 element to end of vector (unspecified alignment).
Definition: vec.h:525
static u64 clib_xxhash(u64 key)
Definition: xxhash.h:58
worker_handoff_error_t
Definition: handoff.c:55
int i
static vnet_sw_interface_t * vnet_get_sw_interface(vnet_main_t *vnm, u32 sw_if_index)
u8 * format(u8 *s, const char *fmt,...)
Definition: format.c:419
unformat_function_t unformat_vnet_sw_interface
unsigned char u8
Definition: types.h:56
#define VLIB_INIT_FUNCTION(x)
Definition: init.h:163
u32 sw_if_index
Definition: vxlan_gbp.api:37
static u8 * format_worker_handoff_trace(u8 *s, va_list *args)
Definition: handoff.c:71
clib_error_t * handoff_init(vlib_main_t *vm)
Definition: handoff.c:307
#define vec_elt_at_index(v, i)
Get vector value at index i checking that i is in bounds.
#define clib_error_return(e, args...)
Definition: error.h:99
unsigned int u32
Definition: types.h:88
#define vlib_call_init_function(vm, x)
Definition: init.h:260
#define VLIB_FRAME_SIZE
Definition: node.h:401
static u32 vlib_get_buffer_index(vlib_main_t *vm, void *p)
Translate buffer pointer into buffer index.
Definition: buffer_funcs.h:158
u32 first_worker_index
Definition: handoff.c:33
#define VLIB_NODE_FUNCTION_MULTIARCH(node, fn)
Definition: node.h:223
#define clib_bitmap_foreach(i, ai, body)
Macro to iterate across set bits in a bitmap.
Definition: bitmap.h:361
static uword clib_bitmap_last_set(uword *ai)
Return the higest numbered set bit in a bitmap.
Definition: bitmap.h:423
vlib_node_registration_t handoff_node
Definition: handoff.c:83
struct _unformat_input_t unformat_input_t
unsigned short u16
Definition: types.h:57
static void * vlib_buffer_get_current(vlib_buffer_t *b)
Get pointer to current data to process.
Definition: buffer.h:214
#define PREDICT_FALSE(x)
Definition: clib.h:111
u32 frame_queue_index
Definition: handoff.c:38
u32 node_index
Node index.
Definition: node.h:518
per_inteface_handoff_data_t * if_data
Definition: handoff.c:35
static void vlib_node_increment_counter(vlib_main_t *vm, u32 node_index, u32 counter_index, u64 increment)
Definition: node_funcs.h:1150
static u64 eth_get_sym_key(ethernet_header_t *h0)
Definition: handoff.h:123
static char * worker_handoff_error_strings[]
Definition: handoff.c:63
#define VLIB_REGISTER_NODE(x,...)
Definition: node.h:169
#define UNFORMAT_END_OF_INPUT
Definition: format.h:144
u16 n_vectors
Definition: node.h:420
vlib_main_t * vm
Definition: buffer.c:301
#define vec_free(V)
Free vector&#39;s memory (no header).
Definition: vec.h:341
#define pool_is_free_index(P, I)
Use free bitmap to query whether given index is free.
Definition: pool.h:283
#define ARRAY_LEN(x)
Definition: clib.h:62
vlib_node_t * vlib_get_node_by_name(vlib_main_t *vm, u8 *name)
Definition: node.c:45
#define VLIB_CLI_COMMAND(x,...)
Definition: cli.h:155
static clib_error_t * set_interface_handoff_command_fn(vlib_main_t *vm, unformat_input_t *input, vlib_cli_command_t *cmd)
Definition: handoff.c:228
#define ASSERT(truth)
uword * thread_registrations_by_name
Definition: threads.h:288
#define foreach_worker_handoff_error
Definition: handoff.c:52
static uword is_pow2(uword x)
Definition: clib.h:235
static void * vlib_add_trace(vlib_main_t *vm, vlib_node_runtime_t *r, vlib_buffer_t *b, u32 n_data_bytes)
Definition: trace_funcs.h:57
struct _vlib_node_registration vlib_node_registration_t
static uword worker_handoff_node_fn(vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame)
Definition: handoff.c:86
#define vec_len(v)
Number of elements in vector (rvalue-only, NULL tolerant)
u64 uword
Definition: types.h:112
vnet_sw_interface_t * sw_interfaces
Definition: interface.h:830
static void * vlib_frame_vector_args(vlib_frame_t *f)
Get pointer to frame vector data.
Definition: node_funcs.h:244
static_always_inline u32 vlib_buffer_enqueue_to_thread(vlib_main_t *vm, u32 frame_queue_index, u32 *buffer_indices, u16 *thread_indices, u32 n_packets, int drop_on_congestion)
Definition: buffer_node.h:487
#define hash_get_mem(h, key)
Definition: hash.h:269
static u64 eth_get_key(ethernet_header_t *h0)
Definition: handoff.h:189
#define vnet_buffer(b)
Definition: buffer.h:368
vnet_sw_interface_type_t type
Definition: interface.h:704
static vlib_thread_main_t * vlib_get_thread_main()
Definition: global_funcs.h:32
u16 flags
Copy of main node flags.
Definition: node.h:531
static_always_inline void vlib_get_buffers(vlib_main_t *vm, u32 *bi, vlib_buffer_t **b, int count)
Translate array of buffer indices into buffer pointers.
Definition: buffer_funcs.h:145
#define VLIB_NODE_FLAG_TRACE
Definition: node.h:326
u32 flags
buffer flags: VLIB_BUFFER_FREE_LIST_INDEX_MASK: bits used to store free list index, VLIB_BUFFER_IS_TRACED: trace this buffer.
Definition: buffer.h:117
int interface_handoff_enable_disable(vlib_main_t *vm, u32 sw_if_index, uword *bitmap, int enable_disable)
Definition: handoff.c:180
uword unformat(unformat_input_t *i, const char *fmt,...)
Definition: unformat.c:972
Definition: defs.h:46
int vnet_feature_enable_disable(const char *arc_name, const char *node_name, u32 sw_if_index, int enable_disable, void *feature_config, u32 n_feature_config_bytes)
Definition: feature.c:274
static uword unformat_check_input(unformat_input_t *i)
Definition: format.h:170
vlib_node_registration_t worker_handoff_node
(constructor) VLIB_REGISTER_NODE (worker_handoff_node)
Definition: handoff.c:161