nsim: fix quad-loop packet trace
[vpp.git] / src / plugins / nsim / node.c
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 {
25   f64 expires;
26   u32 tx_sw_if_index;
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
51 vlib_node_registration_t nsim_node;
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,
62   foreach_nsim_error
63 #undef _
64     NSIM_N_ERROR,
65 } nsim_error_t;
66
67 #ifndef CLIB_MARCH_VARIANT
68 static char *nsim_error_strings[] = {
69 #define _(sym,string) string,
70   foreach_nsim_error
71 #undef _
72 };
73 #endif /* CLIB_MARCH_VARIANT */
74
75 typedef enum
76 {
77   NSIM_NEXT_DROP,
78   NSIM_N_NEXT,
79 } nsim_next_t;
80
81 always_inline uword
82 nsim_inline (vlib_main_t * vm,
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 =
177                 nsm->output_next_index_by_sw_if_index[ep->tx_sw_if_index];
178             }
179           ep->buffer_index = from[0];
180           buffered++;
181         }
182       if (is_trace)
183         {
184           if (b[0]->flags & VLIB_BUFFER_IS_TRACED)
185             {
186               nsim_trace_t *t = vlib_add_trace (vm, node, b[0], sizeof (*t));
187               t->expires = expires;
188               t->is_drop = is_drop[0];
189               t->is_lost = b[0]->error == loss_error;
190               t->tx_sw_if_index = (is_drop[0] == 0) ? ep->tx_sw_if_index : 0;
191             }
192         }
193
194       if (PREDICT_TRUE (is_drop[1] == 0))
195         {
196           ep = wp->entries + wp->tail;
197           wp->tail++;
198           if (wp->tail == wp->wheel_size)
199             wp->tail = 0;
200           wp->cursize++;
201
202           ep->tx_time = expires;
203           ep->rx_sw_if_index = vnet_buffer (b[1])->sw_if_index[VLIB_RX];
204           if (is_cross_connect)
205             {
206               ep->tx_sw_if_index = vnet_buffer (b[1])->sw_if_index[VLIB_TX] =
207                 (vnet_buffer (b[1])->sw_if_index[VLIB_RX] ==
208                  nsm->sw_if_index0) ? nsm->sw_if_index1 : nsm->sw_if_index0;
209               ep->output_next_index =
210                 (ep->tx_sw_if_index ==
211                  nsm->sw_if_index0) ? nsm->
212                 output_next_index0 : nsm->output_next_index1;
213             }
214           else                  /* output feature, even easier... */
215             {
216               ep->tx_sw_if_index = vnet_buffer (b[1])->sw_if_index[VLIB_TX];
217               ep->output_next_index =
218                 nsm->output_next_index_by_sw_if_index[ep->tx_sw_if_index];
219             }
220           ep->buffer_index = from[1];
221           buffered++;
222         }
223
224       if (is_trace)
225         {
226           if (b[1]->flags & VLIB_BUFFER_IS_TRACED)
227             {
228               nsim_trace_t *t = vlib_add_trace (vm, node, b[1], sizeof (*t));
229               t->expires = expires;
230               t->is_drop = is_drop[1];
231               t->is_lost = b[1]->error == loss_error;
232               t->tx_sw_if_index = (is_drop[1] == 0) ? ep->tx_sw_if_index : 0;
233             }
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 =
260                 nsm->output_next_index_by_sw_if_index[ep->tx_sw_if_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
278       if (PREDICT_TRUE (is_drop[3] == 0))
279         {
280           ep = wp->entries + wp->tail;
281           wp->tail++;
282           if (wp->tail == wp->wheel_size)
283             wp->tail = 0;
284           wp->cursize++;
285
286           ep->tx_time = expires;
287           ep->rx_sw_if_index = vnet_buffer (b[3])->sw_if_index[VLIB_RX];
288           if (is_cross_connect)
289             {
290               ep->tx_sw_if_index = vnet_buffer (b[3])->sw_if_index[VLIB_TX] =
291                 (vnet_buffer (b[3])->sw_if_index[VLIB_RX] ==
292                  nsm->sw_if_index0) ? nsm->sw_if_index1 : nsm->sw_if_index0;
293               ep->output_next_index =
294                 (ep->tx_sw_if_index ==
295                  nsm->sw_if_index0) ? nsm->
296                 output_next_index0 : nsm->output_next_index1;
297             }
298           else                  /* output feature, even easier... */
299             {
300               ep->tx_sw_if_index = vnet_buffer (b[3])->sw_if_index[VLIB_TX];
301               ep->output_next_index =
302                 nsm->output_next_index_by_sw_if_index[ep->tx_sw_if_index];
303             }
304           ep->buffer_index = from[3];
305           buffered++;
306         }
307
308       if (is_trace)
309         {
310           if (b[3]->flags & VLIB_BUFFER_IS_TRACED)
311             {
312               nsim_trace_t *t = vlib_add_trace (vm, node, b[3], sizeof (*t));
313               t->expires = expires;
314               t->is_drop = is_drop[3];
315               t->is_lost = b[3]->error == loss_error;
316               t->tx_sw_if_index = (is_drop[3] == 0) ? ep->tx_sw_if_index : 0;
317             }
318         }
319
320       if (PREDICT_FALSE (is_drop[0]))
321         *drop++ = from[0];
322       if (PREDICT_FALSE (is_drop[1]))
323         *drop++ = from[1];
324       if (PREDICT_FALSE (is_drop[2]))
325         *drop++ = from[2];
326       if (PREDICT_FALSE (is_drop[3]))
327         *drop++ = from[3];
328
329       b += 4;
330       next += 4;
331       from += 4;
332       n_left_from -= 4;
333     }
334
335 slow_path:
336
337   while (n_left_from > 0)
338     {
339       next[0] = NSIM_NEXT_DROP;
340       is_drop[0] = 0;
341       if (PREDICT_TRUE (wp->cursize < wp->wheel_size))
342         {
343           if (PREDICT_FALSE (nsm->drop_fraction != 0.0))
344             {
345               /* Get a random number on the closed interval [0,1] */
346               rnd[0] = random_f64 (&nsm->seed);
347               /* Drop the pkt? */
348               if (rnd[0] <= nsm->drop_fraction)
349                 {
350                   b[0]->error = loss_error;
351                   is_drop[0] = 1;
352                   goto do_trace;
353                 }
354             }
355
356           ep = wp->entries + wp->tail;
357           wp->tail++;
358           if (wp->tail == wp->wheel_size)
359             wp->tail = 0;
360           wp->cursize++;
361
362           ep->tx_time = expires;
363           ep->rx_sw_if_index = vnet_buffer (b[0])->sw_if_index[VLIB_RX];
364           if (is_cross_connect)
365             {
366               ep->tx_sw_if_index = vnet_buffer (b[0])->sw_if_index[VLIB_TX] =
367                 (vnet_buffer (b[0])->sw_if_index[VLIB_RX] ==
368                  nsm->sw_if_index0) ? nsm->sw_if_index1 : nsm->sw_if_index0;
369               ep->output_next_index =
370                 (ep->tx_sw_if_index ==
371                  nsm->sw_if_index0) ? nsm->
372                 output_next_index0 : nsm->output_next_index1;
373             }
374           else                  /* output feature, even easier... */
375             {
376               ep->tx_sw_if_index = vnet_buffer (b[0])->sw_if_index[VLIB_TX];
377               ep->output_next_index =
378                 nsm->output_next_index_by_sw_if_index[ep->tx_sw_if_index];
379             }
380           ep->buffer_index = from[0];
381           buffered++;
382         }
383       else                      /* out of wheel space, drop pkt */
384         {
385           b[0]->error = no_buffer_error;
386           is_drop[0] = 1;
387         }
388
389     do_trace:
390       if (is_trace)
391         {
392           if (b[0]->flags & VLIB_BUFFER_IS_TRACED)
393             {
394               nsim_trace_t *t = vlib_add_trace (vm, node, b[0], sizeof (*t));
395               t->expires = expires;
396               t->is_drop = is_drop[0];
397               t->is_lost = b[0]->error == loss_error;
398               t->tx_sw_if_index = (is_drop[0] == 0) ? ep->tx_sw_if_index : 0;
399             }
400         }
401
402       b += 1;
403       next += 1;
404       if (PREDICT_FALSE (is_drop[0]))
405         {
406           drop[0] = from[0];
407           drop++;
408         }
409       from++;
410       n_left_from -= 1;
411     }
412   if (PREDICT_FALSE (drop > drops))
413     {
414       u32 n_left_to_drop = drop - drops;
415       drop = drops;
416
417       while (n_left_to_drop > 0)
418         {
419           u32 this_copy_size;
420           vlib_get_next_frame (vm, node, NSIM_NEXT_DROP, to_next,
421                                n_left_to_next);
422           this_copy_size = clib_min (n_left_to_drop, n_left_to_next);
423           clib_memcpy_fast (to_next, drop, this_copy_size * sizeof (u32));
424           n_left_to_next -= this_copy_size;
425           vlib_put_next_frame (vm, node, NSIM_NEXT_DROP, n_left_to_next);
426           drop += this_copy_size;
427           n_left_to_drop -= this_copy_size;
428         }
429     }
430   vlib_node_increment_counter (vm, node->node_index,
431                                NSIM_ERROR_BUFFERED, buffered);
432   return frame->n_vectors;
433 }
434
435 VLIB_NODE_FN (nsim_node) (vlib_main_t * vm, vlib_node_runtime_t * node,
436                           vlib_frame_t * frame)
437 {
438   if (PREDICT_FALSE (node->flags & VLIB_NODE_FLAG_TRACE))
439     return nsim_inline (vm, node, frame,
440                         1 /* is_trace */ , 1 /* is_cross_connect */ );
441   else
442     return nsim_inline (vm, node, frame,
443                         0 /* is_trace */ , 1 /* is_cross_connect */ );
444 }
445
446 /* *INDENT-OFF* */
447 #ifndef CLIB_MARCH_VARIANT
448 VLIB_REGISTER_NODE (nsim_node) =
449 {
450   .name = "nsim",
451   .vector_size = sizeof (u32),
452   .format_trace = format_nsim_trace,
453   .type = VLIB_NODE_TYPE_INTERNAL,
454
455   .n_errors = ARRAY_LEN(nsim_error_strings),
456   .error_strings = nsim_error_strings,
457
458   .n_next_nodes = NSIM_N_NEXT,
459
460   /* edit / add dispositions here */
461   .next_nodes = {
462         [NSIM_NEXT_DROP] = "error-drop",
463   },
464 };
465 #endif /* CLIB_MARCH_VARIANT */
466 /* *INDENT-ON* */
467
468 VLIB_NODE_FN (nsim_feature_node) (vlib_main_t * vm,
469                                   vlib_node_runtime_t * node,
470                                   vlib_frame_t * frame)
471 {
472   if (PREDICT_FALSE (node->flags & VLIB_NODE_FLAG_TRACE))
473     return nsim_inline (vm, node, frame,
474                         1 /* is_trace */ , 0 /* is_cross_connect */ );
475   else
476     return nsim_inline (vm, node, frame,
477                         0 /* is_trace */ , 0 /* is_cross_connect */ );
478 }
479
480 /* *INDENT-OFF* */
481 #ifndef CLIB_MARCH_VARIANT
482 VLIB_REGISTER_NODE (nsim_feature_node) =
483 {
484   .name = "nsim-output-feature",
485   .vector_size = sizeof (u32),
486   .format_trace = format_nsim_trace,
487   .type = VLIB_NODE_TYPE_INTERNAL,
488
489   .n_errors = ARRAY_LEN(nsim_error_strings),
490   .error_strings = nsim_error_strings,
491
492   .n_next_nodes = NSIM_N_NEXT,
493
494   /* edit / add dispositions here */
495   .next_nodes = {
496         [NSIM_NEXT_DROP] = "error-drop",
497   },
498 };
499 #endif /* CLIB_MARCH_VARIANT */
500 /* *INDENT-ON* */
501
502 /*
503  * fd.io coding-style-patch-verification: ON
504  *
505  * Local Variables:
506  * eval: (c-set-style "gnu")
507  * End:
508  */