FD.io VPP  v19.08-23-g4b943d6
Vector Packet Processing
node.c
Go to the documentation of this file.
1 /*
2  * node.c - skeleton vpp engine plug-in dual-loop node skeleton
3  *
4  * Copyright (c) <current-year> <your-organization>
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at:
8  *
9  * http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */
17 #include <vlib/vlib.h>
18 #include <vnet/vnet.h>
19 #include <vnet/pg/pg.h>
20 #include <vppinfra/error.h>
21 #include <nsim/nsim.h>
22 
23 typedef struct
24 {
27  int is_drop;
28  int is_lost;
29 } nsim_trace_t;
30 
31 #ifndef CLIB_MARCH_VARIANT
32 
33 /* packet trace format function */
34 static u8 *
35 format_nsim_trace (u8 * s, va_list * args)
36 {
37  CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
38  CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
39  nsim_trace_t *t = va_arg (*args, nsim_trace_t *);
40 
41  if (t->is_drop)
42  s = format (s, "NSIM: dropped, %s", t->is_lost ?
43  "simulated network loss" : "no space in ring");
44  else
45  s = format (s, "NSIM: tx time %.6f sw_if_index %d",
46  t->expires, t->tx_sw_if_index);
47 
48  return s;
49 }
50 
52 #endif /* CLIB_MARCH_VARIANT */
53 
54 #define foreach_nsim_error \
55 _(BUFFERED, "Packets buffered") \
56 _(DROPPED, "Packets dropped due to lack of space") \
57 _(LOSS, "Network loss simulation drop packets")
58 
59 typedef enum
60 {
61 #define _(sym,str) NSIM_ERROR_##sym,
63 #undef _
65 } nsim_error_t;
66 
67 #ifndef CLIB_MARCH_VARIANT
68 static char *nsim_error_strings[] = {
69 #define _(sym,string) string,
71 #undef _
72 };
73 #endif /* CLIB_MARCH_VARIANT */
74 
75 typedef enum
76 {
79 } nsim_next_t;
80 
83  vlib_node_runtime_t * node, vlib_frame_t * frame, int is_trace,
84  int is_cross_connect)
85 {
86  nsim_main_t *nsm = &nsim_main;
87  u32 n_left_from, *from;
88  u32 *to_next, n_left_to_next;
89  u32 drops[VLIB_FRAME_SIZE], *drop;
90  vlib_buffer_t *bufs[VLIB_FRAME_SIZE], **b;
91  u8 is_drop[4];
92  u16 nexts[VLIB_FRAME_SIZE], *next;
93  u32 my_thread_index = vm->thread_index;
94  nsim_wheel_t *wp = nsm->wheel_by_thread[my_thread_index];
95  f64 now = vlib_time_now (vm);
96  f64 expires = now + nsm->delay;
97  f64 rnd[4];
98  u32 no_buffer_error = node->errors[NSIM_ERROR_DROPPED];
99  u32 loss_error = node->errors[NSIM_ERROR_LOSS];
100  u32 buffered = 0;
101  nsim_wheel_entry_t *ep = 0;
102 
103  ASSERT (wp);
104 
105  from = vlib_frame_vector_args (frame);
106  n_left_from = frame->n_vectors;
107 
108  vlib_get_buffers (vm, from, bufs, n_left_from);
109  b = bufs;
110  next = nexts;
111  drop = drops;
112 
113  while (n_left_from >= 8)
114  {
115  vlib_prefetch_buffer_header (b[4], STORE);
116  vlib_prefetch_buffer_header (b[5], STORE);
117  vlib_prefetch_buffer_header (b[6], STORE);
118  vlib_prefetch_buffer_header (b[7], STORE);
119 
120  memset (&is_drop, 0, sizeof (is_drop));
121  next[0] = next[1] = next[2] = next[3] = NSIM_NEXT_DROP;
122  if (PREDICT_FALSE (wp->cursize + 4 >= wp->wheel_size))
123  goto slow_path;
124  if (PREDICT_FALSE (nsm->drop_fraction != 0.0))
125  {
126  rnd[0] = random_f64 (&nsm->seed);
127  rnd[1] = random_f64 (&nsm->seed);
128  rnd[2] = random_f64 (&nsm->seed);
129  rnd[3] = random_f64 (&nsm->seed);
130 
131  if (rnd[0] <= nsm->drop_fraction)
132  {
133  b[0]->error = loss_error;
134  is_drop[0] = 1;
135  }
136  if (rnd[1] <= nsm->drop_fraction)
137  {
138  b[1]->error = loss_error;
139  is_drop[1] = 1;
140  }
141  if (rnd[2] <= nsm->drop_fraction)
142  {
143  b[2]->error = loss_error;
144  is_drop[2] = 1;
145  }
146  if (rnd[3] <= nsm->drop_fraction)
147  {
148  b[3]->error = loss_error;
149  is_drop[3] = 1;
150  }
151  }
152 
153  if (PREDICT_TRUE (is_drop[0] == 0))
154  {
155  ep = wp->entries + wp->tail;
156  wp->tail++;
157  if (wp->tail == wp->wheel_size)
158  wp->tail = 0;
159  wp->cursize++;
160 
161  ep->tx_time = expires;
162  ep->rx_sw_if_index = vnet_buffer (b[0])->sw_if_index[VLIB_RX];
163  if (is_cross_connect)
164  {
165  ep->tx_sw_if_index = vnet_buffer (b[0])->sw_if_index[VLIB_TX] =
166  (vnet_buffer (b[0])->sw_if_index[VLIB_RX] ==
167  nsm->sw_if_index0) ? nsm->sw_if_index1 : nsm->sw_if_index0;
168  ep->output_next_index =
169  (ep->tx_sw_if_index ==
170  nsm->sw_if_index0) ? nsm->
171  output_next_index0 : nsm->output_next_index1;
172  }
173  else /* output feature, even easier... */
174  {
175  ep->tx_sw_if_index = vnet_buffer (b[0])->sw_if_index[VLIB_TX];
176  ep->output_next_index =
178  }
179  ep->buffer_index = from[0];
180  buffered++;
181  }
182 
183  if (is_trace)
184  {
185  if (b[1]->flags & VLIB_BUFFER_IS_TRACED)
186  {
187  nsim_trace_t *t = vlib_add_trace (vm, node, b[1], sizeof (*t));
188  t->expires = expires;
189  t->is_drop = is_drop[1];
190  t->is_lost = b[1]->error == loss_error;
191  t->tx_sw_if_index = (is_drop[1] == 0) ? ep->tx_sw_if_index : 0;
192  }
193  }
194 
195  if (PREDICT_TRUE (is_drop[1] == 0))
196  {
197  ep = wp->entries + wp->tail;
198  wp->tail++;
199  if (wp->tail == wp->wheel_size)
200  wp->tail = 0;
201  wp->cursize++;
202 
203  ep->tx_time = expires;
204  ep->rx_sw_if_index = vnet_buffer (b[1])->sw_if_index[VLIB_RX];
205  if (is_cross_connect)
206  {
207  ep->tx_sw_if_index = vnet_buffer (b[1])->sw_if_index[VLIB_TX] =
208  (vnet_buffer (b[1])->sw_if_index[VLIB_RX] ==
209  nsm->sw_if_index0) ? nsm->sw_if_index1 : nsm->sw_if_index0;
210  ep->output_next_index =
211  (ep->tx_sw_if_index ==
212  nsm->sw_if_index0) ? nsm->
213  output_next_index0 : nsm->output_next_index1;
214  }
215  else /* output feature, even easier... */
216  {
217  ep->tx_sw_if_index = vnet_buffer (b[1])->sw_if_index[VLIB_TX];
218  ep->output_next_index =
220  }
221  ep->buffer_index = from[1];
222  buffered++;
223  }
224 
225  if (is_trace)
226  {
227  if (b[2]->flags & VLIB_BUFFER_IS_TRACED)
228  {
229  nsim_trace_t *t = vlib_add_trace (vm, node, b[2], sizeof (*t));
230  t->expires = expires;
231  t->is_drop = is_drop[2];
232  t->is_lost = b[2]->error == loss_error;
233  t->tx_sw_if_index = (is_drop[2] == 0) ? ep->tx_sw_if_index : 0;
234  }
235  }
236  if (PREDICT_TRUE (is_drop[2] == 0))
237  {
238  ep = wp->entries + wp->tail;
239  wp->tail++;
240  if (wp->tail == wp->wheel_size)
241  wp->tail = 0;
242  wp->cursize++;
243 
244  ep->tx_time = expires;
245  ep->rx_sw_if_index = vnet_buffer (b[2])->sw_if_index[VLIB_RX];
246  if (is_cross_connect)
247  {
248  ep->tx_sw_if_index = vnet_buffer (b[2])->sw_if_index[VLIB_TX] =
249  (vnet_buffer (b[2])->sw_if_index[VLIB_RX] ==
250  nsm->sw_if_index0) ? nsm->sw_if_index1 : nsm->sw_if_index0;
251  ep->output_next_index =
252  (ep->tx_sw_if_index ==
253  nsm->sw_if_index0) ? nsm->
254  output_next_index0 : nsm->output_next_index1;
255  }
256  else /* output feature, even easier... */
257  {
258  ep->tx_sw_if_index = vnet_buffer (b[2])->sw_if_index[VLIB_TX];
259  ep->output_next_index =
261  }
262  ep->buffer_index = from[2];
263  buffered++;
264  }
265 
266  if (is_trace)
267  {
268  if (b[2]->flags & VLIB_BUFFER_IS_TRACED)
269  {
270  nsim_trace_t *t = vlib_add_trace (vm, node, b[2], sizeof (*t));
271  t->expires = expires;
272  t->is_drop = is_drop[2];
273  t->is_lost = b[2]->error == loss_error;
274  t->tx_sw_if_index = (is_drop[2] == 0) ? ep->tx_sw_if_index : 0;
275  }
276  }
277  if (PREDICT_TRUE (is_drop[3] == 0))
278  {
279  ep = wp->entries + wp->tail;
280  wp->tail++;
281  if (wp->tail == wp->wheel_size)
282  wp->tail = 0;
283  wp->cursize++;
284 
285  ep->tx_time = expires;
286  ep->rx_sw_if_index = vnet_buffer (b[3])->sw_if_index[VLIB_RX];
287  if (is_cross_connect)
288  {
289  ep->tx_sw_if_index = vnet_buffer (b[3])->sw_if_index[VLIB_TX] =
290  (vnet_buffer (b[3])->sw_if_index[VLIB_RX] ==
291  nsm->sw_if_index0) ? nsm->sw_if_index1 : nsm->sw_if_index0;
292  ep->output_next_index =
293  (ep->tx_sw_if_index ==
294  nsm->sw_if_index0) ? nsm->
295  output_next_index0 : nsm->output_next_index1;
296  }
297  else /* output feature, even easier... */
298  {
299  ep->tx_sw_if_index = vnet_buffer (b[3])->sw_if_index[VLIB_TX];
300  ep->output_next_index =
302  }
303  ep->buffer_index = from[3];
304  buffered++;
305  }
306 
307  if (is_trace)
308  {
309  if (b[3]->flags & VLIB_BUFFER_IS_TRACED)
310  {
311  nsim_trace_t *t = vlib_add_trace (vm, node, b[3], sizeof (*t));
312  t->expires = expires;
313  t->is_drop = is_drop[3];
314  t->is_lost = b[3]->error == loss_error;
315  t->tx_sw_if_index = (is_drop[3] == 0) ? ep->tx_sw_if_index : 0;
316  }
317  }
318 
319  if (PREDICT_FALSE (is_drop[0]))
320  *drop++ = from[0];
321  if (PREDICT_FALSE (is_drop[1]))
322  *drop++ = from[1];
323  if (PREDICT_FALSE (is_drop[2]))
324  *drop++ = from[2];
325  if (PREDICT_FALSE (is_drop[3]))
326  *drop++ = from[3];
327 
328  b += 4;
329  next += 4;
330  from += 4;
331  n_left_from -= 4;
332  }
333 
334 slow_path:
335 
336  while (n_left_from > 0)
337  {
338  next[0] = NSIM_NEXT_DROP;
339  is_drop[0] = 0;
340  if (PREDICT_TRUE (wp->cursize < wp->wheel_size))
341  {
342  if (PREDICT_FALSE (nsm->drop_fraction != 0.0))
343  {
344  /* Get a random number on the closed interval [0,1] */
345  rnd[0] = random_f64 (&nsm->seed);
346  /* Drop the pkt? */
347  if (rnd[0] <= nsm->drop_fraction)
348  {
349  b[0]->error = loss_error;
350  is_drop[0] = 1;
351  goto do_trace;
352  }
353  }
354 
355  ep = wp->entries + wp->tail;
356  wp->tail++;
357  if (wp->tail == wp->wheel_size)
358  wp->tail = 0;
359  wp->cursize++;
360 
361  ep->tx_time = expires;
362  ep->rx_sw_if_index = vnet_buffer (b[0])->sw_if_index[VLIB_RX];
363  if (is_cross_connect)
364  {
365  ep->tx_sw_if_index = vnet_buffer (b[0])->sw_if_index[VLIB_TX] =
366  (vnet_buffer (b[0])->sw_if_index[VLIB_RX] ==
367  nsm->sw_if_index0) ? nsm->sw_if_index1 : nsm->sw_if_index0;
368  ep->output_next_index =
369  (ep->tx_sw_if_index ==
370  nsm->sw_if_index0) ? nsm->
371  output_next_index0 : nsm->output_next_index1;
372  }
373  else /* output feature, even easier... */
374  {
375  ep->tx_sw_if_index = vnet_buffer (b[0])->sw_if_index[VLIB_TX];
376  ep->output_next_index =
378  }
379  ep->buffer_index = from[0];
380  buffered++;
381  }
382  else /* out of wheel space, drop pkt */
383  {
384  b[0]->error = no_buffer_error;
385  is_drop[0] = 1;
386  }
387 
388  do_trace:
389  if (is_trace)
390  {
391  if (b[0]->flags & VLIB_BUFFER_IS_TRACED)
392  {
393  nsim_trace_t *t = vlib_add_trace (vm, node, b[0], sizeof (*t));
394  t->expires = expires;
395  t->is_drop = is_drop[0];
396  t->is_lost = b[0]->error == loss_error;
397  t->tx_sw_if_index = (is_drop[0] == 0) ? ep->tx_sw_if_index : 0;
398  }
399  }
400 
401  b += 1;
402  next += 1;
403  if (PREDICT_FALSE (is_drop[0]))
404  {
405  drop[0] = from[0];
406  drop++;
407  }
408  from++;
409  n_left_from -= 1;
410  }
411  if (PREDICT_FALSE (drop > drops))
412  {
413  u32 n_left_to_drop = drop - drops;
414  drop = drops;
415 
416  while (n_left_to_drop > 0)
417  {
418  u32 this_copy_size;
419  vlib_get_next_frame (vm, node, NSIM_NEXT_DROP, to_next,
420  n_left_to_next);
421  this_copy_size = clib_min (n_left_to_drop, n_left_to_next);
422  clib_memcpy_fast (to_next, drop, this_copy_size * sizeof (u32));
423  n_left_to_next -= this_copy_size;
424  vlib_put_next_frame (vm, node, NSIM_NEXT_DROP, n_left_to_next);
425  drop += this_copy_size;
426  n_left_to_drop -= this_copy_size;
427  }
428  }
430  NSIM_ERROR_BUFFERED, buffered);
431  return frame->n_vectors;
432 }
433 
435  vlib_frame_t * frame)
436 {
437  if (PREDICT_FALSE (node->flags & VLIB_NODE_FLAG_TRACE))
438  return nsim_inline (vm, node, frame,
439  1 /* is_trace */ , 1 /* is_cross_connect */ );
440  else
441  return nsim_inline (vm, node, frame,
442  0 /* is_trace */ , 1 /* is_cross_connect */ );
443 }
444 
445 /* *INDENT-OFF* */
446 #ifndef CLIB_MARCH_VARIANT
448 {
449  .name = "nsim",
450  .vector_size = sizeof (u32),
451  .format_trace = format_nsim_trace,
453 
454  .n_errors = ARRAY_LEN(nsim_error_strings),
455  .error_strings = nsim_error_strings,
456 
457  .n_next_nodes = NSIM_N_NEXT,
458 
459  /* edit / add dispositions here */
460  .next_nodes = {
461  [NSIM_NEXT_DROP] = "error-drop",
462  },
463 };
464 #endif /* CLIB_MARCH_VARIANT */
465 /* *INDENT-ON* */
466 
468  vlib_node_runtime_t * node,
469  vlib_frame_t * frame)
470 {
471  if (PREDICT_FALSE (node->flags & VLIB_NODE_FLAG_TRACE))
472  return nsim_inline (vm, node, frame,
473  1 /* is_trace */ , 0 /* is_cross_connect */ );
474  else
475  return nsim_inline (vm, node, frame,
476  0 /* is_trace */ , 0 /* is_cross_connect */ );
477 }
478 
479 /* *INDENT-OFF* */
480 #ifndef CLIB_MARCH_VARIANT
482 {
483  .name = "nsim-output-feature",
484  .vector_size = sizeof (u32),
485  .format_trace = format_nsim_trace,
487 
488  .n_errors = ARRAY_LEN(nsim_error_strings),
489  .error_strings = nsim_error_strings,
490 
491  .n_next_nodes = NSIM_N_NEXT,
492 
493  /* edit / add dispositions here */
494  .next_nodes = {
495  [NSIM_NEXT_DROP] = "error-drop",
496  },
497 };
498 #endif /* CLIB_MARCH_VARIANT */
499 /* *INDENT-ON* */
500 
501 /*
502  * fd.io coding-style-patch-verification: ON
503  *
504  * Local Variables:
505  * eval: (c-set-style "gnu")
506  * End:
507  */
u32 * output_next_index_by_sw_if_index
Definition: nsim.h:58
u32 flags
Definition: vhost_user.h:141
#define clib_min(x, y)
Definition: clib.h:295
#define CLIB_UNUSED(x)
Definition: clib.h:82
#define PREDICT_TRUE(x)
Definition: clib.h:112
Definition: nsim.h:28
#define clib_memcpy_fast(a, b, c)
Definition: string.h:81
static f64 vlib_time_now(vlib_main_t *vm)
Definition: main.h:258
u32 thread_index
Definition: main.h:197
u8 * format(u8 *s, const char *fmt,...)
Definition: format.c:424
#define VLIB_NODE_FN(node)
Definition: node.h:201
vlib_node_registration_t nsim_node
(constructor) VLIB_REGISTER_NODE (nsim_node)
Definition: node.c:51
vlib_error_t * errors
Vector of errors for this node.
Definition: node.h:468
unsigned char u8
Definition: types.h:56
u32 tx_sw_if_index
Definition: node.c:26
double f64
Definition: types.h:142
u32 tx_sw_if_index
Definition: nsim.h:32
u32 cursize
Definition: nsim.h:41
static u32 slow_path(dslite_main_t *dm, dslite_session_key_t *in2out_key, dslite_session_t **sp, u32 next, u8 *error, u32 thread_index)
Definition: dslite_in2out.c:35
#define always_inline
Definition: clib.h:98
#define vlib_prefetch_buffer_header(b, type)
Prefetch buffer metadata.
Definition: buffer.h:203
unsigned int u32
Definition: types.h:88
f64 expires
Definition: node.c:25
#define VLIB_FRAME_SIZE
Definition: node.h:376
static char * nsim_error_strings[]
Definition: node.c:68
int is_drop
Definition: node.c:27
vl_api_fib_path_type_t type
Definition: fib_types.api:123
vlib_error_t error
Error code for buffers to be enqueued to error handler.
Definition: buffer.h:136
nsim_next_t
Definition: node.c:75
#define foreach_nsim_error
Definition: node.c:54
unsigned short u16
Definition: types.h:57
#define PREDICT_FALSE(x)
Definition: clib.h:111
u32 tail
Definition: nsim.h:43
u32 node_index
Node index.
Definition: node.h:494
#define vlib_get_next_frame(vm, node, next_index, vectors, n_vectors_left)
Get pointer to next frame vector data by (vlib_node_runtime_t, next_index).
Definition: node_funcs.h:338
static void vlib_node_increment_counter(vlib_main_t *vm, u32 node_index, u32 counter_index, u64 increment)
Definition: node_funcs.h:1150
u32 sw_if_index1
Definition: nsim.h:54
vlib_node_registration_t nsim_feature_node
(constructor) VLIB_REGISTER_NODE (nsim_feature_node)
Definition: node.c:481
#define VLIB_REGISTER_NODE(x,...)
Definition: node.h:169
u16 n_vectors
Definition: node.h:395
vlib_main_t * vm
Definition: buffer.c:312
nsim_wheel_entry_t * entries
Definition: nsim.h:44
#define ARRAY_LEN(x)
Definition: clib.h:62
void vlib_put_next_frame(vlib_main_t *vm, vlib_node_runtime_t *r, u32 next_index, u32 n_vectors_left)
Release pointer to next frame vector data.
Definition: main.c:458
#define ASSERT(truth)
static uword nsim_inline(vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame, int is_trace, int is_cross_connect)
Definition: node.c:82
nsim_wheel_t ** wheel_by_thread
Definition: nsim.h:64
static f64 random_f64(u32 *seed)
Generate f64 random number in the interval [0,1].
Definition: random.h:145
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:55
struct _vlib_node_registration vlib_node_registration_t
Definition: defs.h:47
nsim_error_t
Definition: node.c:59
u32 seed
Definition: nsim.h:61
u32 sw_if_index0
Definition: nsim.h:54
f64 tx_time
Definition: nsim.h:30
u32 buffer_index
Definition: nsim.h:34
VLIB buffer representation.
Definition: buffer.h:102
u64 uword
Definition: types.h:112
static void * vlib_frame_vector_args(vlib_frame_t *f)
Get pointer to frame vector data.
Definition: node_funcs.h:244
int is_lost
Definition: node.c:28
f64 delay
Definition: nsim.h:67
#define vnet_buffer(b)
Definition: buffer.h:361
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:244
#define VLIB_NODE_FLAG_TRACE
Definition: node.h:301
u32 output_next_index
Definition: nsim.h:33
f64 drop_fraction
Definition: nsim.h:70
static u8 * format_nsim_trace(u8 *s, va_list *args)
Definition: node.c:35
u32 wheel_size
Definition: nsim.h:40
u32 output_next_index1
Definition: nsim.h:55
nsim_main_t nsim_main
Definition: nsim.c:59
u32 rx_sw_if_index
Definition: nsim.h:31
Definition: defs.h:46