nsim: basic reorder support
[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 _(REORDERED, "Packets reordered")
59
60 typedef enum
61 {
62 #define _(sym,str) NSIM_ERROR_##sym,
63   foreach_nsim_error
64 #undef _
65     NSIM_N_ERROR,
66 } nsim_error_t;
67
68 #ifndef CLIB_MARCH_VARIANT
69 static char *nsim_error_strings[] = {
70 #define _(sym,string) string,
71   foreach_nsim_error
72 #undef _
73 };
74 #endif /* CLIB_MARCH_VARIANT */
75
76 typedef enum
77 {
78   NSIM_NEXT_DROP,
79   NSIM_N_NEXT,
80 } nsim_next_t;
81
82 static void
83 nsim_set_actions (nsim_main_t * nsm, vlib_buffer_t ** b,
84                   nsim_node_ctx_t * ctx, u32 n_actions)
85 {
86   int i;
87
88   memset (ctx->action, 0, n_actions * sizeof (ctx->action[0]));
89
90   if (PREDICT_FALSE (nsm->drop_fraction != 0.0))
91     {
92       for (i = 0; i < n_actions; i++)
93         if (random_f64 (&nsm->seed) <= nsm->drop_fraction)
94           ctx->action[i] |= NSIM_ACTION_DROP;
95     }
96
97   if (PREDICT_FALSE (nsm->reorder_fraction != 0.0))
98     {
99       for (i = 0; i < n_actions; i++)
100         if (random_f64 (&nsm->seed) <= nsm->reorder_fraction)
101           ctx->action[i] |= NSIM_ACTION_REORDER;
102     }
103 }
104
105 static void
106 nsim_trace_buffer (vlib_main_t * vm, vlib_node_runtime_t * node,
107                    vlib_buffer_t * b, nsim_node_ctx_t * ctx, u32 is_drop)
108 {
109   if (b->flags & VLIB_BUFFER_IS_TRACED)
110     {
111       nsim_trace_t *t = vlib_add_trace (vm, node, b, sizeof (*t));
112       t->expires = ctx->expires;
113       t->is_drop = is_drop;
114       t->is_lost = ctx->action[0] & NSIM_ACTION_DROP;
115       t->tx_sw_if_index = vnet_buffer (b)->sw_if_index[VLIB_TX];
116     }
117 }
118
119 always_inline void
120 nsim_buffer_fwd_lookup (nsim_main_t * nsm, vlib_buffer_t * b,
121                         u32 * next, u8 is_cross_connect)
122 {
123   if (is_cross_connect)
124     {
125       vnet_buffer (b)->sw_if_index[VLIB_TX] =
126         (vnet_buffer (b)->sw_if_index[VLIB_RX] == nsm->sw_if_index0) ?
127         nsm->sw_if_index1 : nsm->sw_if_index0;
128       *next =
129         (vnet_buffer (b)->sw_if_index[VLIB_TX] == nsm->sw_if_index0) ?
130         nsm->output_next_index0 : nsm->output_next_index1;
131     }
132   else                          /* output feature, even easier... */
133     {
134       u32 sw_if_index = vnet_buffer (b)->sw_if_index[VLIB_TX];
135       *next = nsm->output_next_index_by_sw_if_index[sw_if_index];
136     }
137 }
138
139 always_inline void
140 nsim_dispatch_buffer (vlib_main_t * vm, vlib_node_runtime_t * node,
141                       nsim_main_t * nsm, nsim_wheel_t * wp, vlib_buffer_t * b,
142                       u32 bi, nsim_node_ctx_t * ctx, u8 is_cross_connect,
143                       u8 is_trace)
144 {
145   if (PREDICT_TRUE (!(ctx->action[0] & NSIM_ACTION_DROP)))
146     {
147       if (PREDICT_FALSE (ctx->action[0] & NSIM_ACTION_REORDER))
148         {
149           u32 next;
150           ctx->reord[0] = bi;
151           vnet_get_config_data (&ctx->fcm->config_main,
152                                 &b->current_config_index, &next, 0);
153           ctx->reord_nexts[0] = next;
154           ctx->reord += 1;
155           ctx->reord_nexts += 1;
156           goto trace;
157         }
158
159       nsim_wheel_entry_t *ep = wp->entries + wp->tail;
160       wp->tail++;
161       if (wp->tail == wp->wheel_size)
162         wp->tail = 0;
163       wp->cursize++;
164
165       ep->tx_time = ctx->expires;
166       ep->rx_sw_if_index = vnet_buffer (b)->sw_if_index[VLIB_RX];
167       nsim_buffer_fwd_lookup (nsm, b, &ep->output_next_index,
168                               is_cross_connect);
169       ep->tx_sw_if_index = vnet_buffer (b)->sw_if_index[VLIB_TX];
170       ep->buffer_index = bi;
171       ctx->n_buffered += 1;
172     }
173   else
174     {
175       ctx->n_loss += 1;
176       ctx->drop[0] = bi;
177       ctx->drop += 1;
178     }
179
180 trace:
181
182   if (PREDICT_FALSE (is_trace))
183     nsim_trace_buffer (vm, node, b, ctx, 0);
184
185   ctx->action += 1;
186 }
187
188 always_inline uword
189 nsim_inline (vlib_main_t * vm,
190              vlib_node_runtime_t * node, vlib_frame_t * frame, int is_trace,
191              int is_cross_connect)
192 {
193   nsim_main_t *nsm = &nsim_main;
194   u32 n_left_from, *from, drops[VLIB_FRAME_SIZE], reorders[VLIB_FRAME_SIZE];
195   nsim_wheel_t *wp = nsm->wheel_by_thread[vm->thread_index];
196   vlib_buffer_t *bufs[VLIB_FRAME_SIZE], **b;
197   u16 reorders_nexts[VLIB_FRAME_SIZE];
198   u8 actions[VLIB_FRAME_SIZE];
199   nsim_node_ctx_t ctx;
200
201   ASSERT (wp);
202
203   from = vlib_frame_vector_args (frame);
204   n_left_from = frame->n_vectors;
205
206   vlib_get_buffers (vm, from, bufs, n_left_from);
207   b = bufs;
208
209   ctx.fcm = vnet_feature_get_config_main (nsm->arc_index);
210   ctx.n_loss = 0;
211   ctx.n_buffered = 0;
212   ctx.drop = drops;
213   ctx.reord = reorders;
214   ctx.reord_nexts = reorders_nexts;
215   ctx.action = actions;
216   ctx.expires = vlib_time_now (vm) + nsm->delay;
217
218   nsim_set_actions (nsm, b, &ctx, n_left_from);
219
220   while (n_left_from >= 8)
221     {
222       vlib_prefetch_buffer_header (b[4], STORE);
223       vlib_prefetch_buffer_header (b[5], STORE);
224       vlib_prefetch_buffer_header (b[6], STORE);
225       vlib_prefetch_buffer_header (b[7], STORE);
226
227       if (PREDICT_FALSE (wp->cursize + 4 >= wp->wheel_size))
228         goto slow_path;
229
230       nsim_dispatch_buffer (vm, node, nsm, wp, b[0], from[0], &ctx,
231                             is_cross_connect, is_trace);
232       nsim_dispatch_buffer (vm, node, nsm, wp, b[1], from[1], &ctx,
233                             is_cross_connect, is_trace);
234       nsim_dispatch_buffer (vm, node, nsm, wp, b[2], from[2], &ctx,
235                             is_cross_connect, is_trace);
236       nsim_dispatch_buffer (vm, node, nsm, wp, b[3], from[3], &ctx,
237                             is_cross_connect, is_trace);
238
239       b += 4;
240       from += 4;
241       n_left_from -= 4;
242     }
243
244 slow_path:
245
246   while (n_left_from > 0)
247     {
248       /* Drop if out of wheel space and not drop or reorder */
249       if (PREDICT_TRUE (wp->cursize < wp->wheel_size
250                         || (ctx.action[0] & NSIM_ACTION_DROP)
251                         || (ctx.action[0] & NSIM_ACTION_REORDER)))
252         {
253           nsim_dispatch_buffer (vm, node, nsm, wp, b[0], from[0], &ctx,
254                                 is_cross_connect, is_trace);
255         }
256       else
257         {
258           ctx.drop[0] = from[0];
259           ctx.drop += 1;
260           if (PREDICT_FALSE (is_trace))
261             nsim_trace_buffer (vm, node, b[0], &ctx, 1);
262           ctx.action += 1;
263         }
264
265       b += 1;
266       from += 1;
267       n_left_from -= 1;
268     }
269
270   if (PREDICT_FALSE (ctx.drop > drops))
271     {
272       u32 n_left_to_drop = ctx.drop - drops;
273       vlib_buffer_free (vm, drops, n_left_to_drop);
274       vlib_node_increment_counter (vm, node->node_index, NSIM_ERROR_LOSS,
275                                    ctx.n_loss);
276       vlib_node_increment_counter (vm, node->node_index, NSIM_ERROR_DROPPED,
277                                    n_left_to_drop - ctx.n_loss);
278     }
279   if (PREDICT_FALSE (ctx.reord > reorders))
280     {
281       u32 n_reordered = ctx.reord - reorders;
282       vlib_buffer_enqueue_to_next (vm, node, reorders, reorders_nexts,
283                                    n_reordered);
284       vlib_node_increment_counter (vm, node->node_index, NSIM_ERROR_REORDERED,
285                                    n_reordered);
286     }
287   vlib_node_increment_counter (vm, node->node_index,
288                                NSIM_ERROR_BUFFERED, ctx.n_buffered);
289   return frame->n_vectors;
290 }
291
292 VLIB_NODE_FN (nsim_node) (vlib_main_t * vm, vlib_node_runtime_t * node,
293                           vlib_frame_t * frame)
294 {
295   if (PREDICT_FALSE (node->flags & VLIB_NODE_FLAG_TRACE))
296     return nsim_inline (vm, node, frame,
297                         1 /* is_trace */ , 1 /* is_cross_connect */ );
298   else
299     return nsim_inline (vm, node, frame,
300                         0 /* is_trace */ , 1 /* is_cross_connect */ );
301 }
302
303 /* *INDENT-OFF* */
304 #ifndef CLIB_MARCH_VARIANT
305 VLIB_REGISTER_NODE (nsim_node) =
306 {
307   .name = "nsim",
308   .vector_size = sizeof (u32),
309   .format_trace = format_nsim_trace,
310   .type = VLIB_NODE_TYPE_INTERNAL,
311
312   .n_errors = ARRAY_LEN(nsim_error_strings),
313   .error_strings = nsim_error_strings,
314
315   .n_next_nodes = NSIM_N_NEXT,
316
317   /* edit / add dispositions here */
318   .next_nodes = {
319         [NSIM_NEXT_DROP] = "error-drop",
320   },
321 };
322 #endif /* CLIB_MARCH_VARIANT */
323 /* *INDENT-ON* */
324
325 VLIB_NODE_FN (nsim_feature_node) (vlib_main_t * vm,
326                                   vlib_node_runtime_t * node,
327                                   vlib_frame_t * frame)
328 {
329   if (PREDICT_FALSE (node->flags & VLIB_NODE_FLAG_TRACE))
330     return nsim_inline (vm, node, frame,
331                         1 /* is_trace */ , 0 /* is_cross_connect */ );
332   else
333     return nsim_inline (vm, node, frame,
334                         0 /* is_trace */ , 0 /* is_cross_connect */ );
335 }
336
337 /* *INDENT-OFF* */
338 #ifndef CLIB_MARCH_VARIANT
339 VLIB_REGISTER_NODE (nsim_feature_node) =
340 {
341   .name = "nsim-output-feature",
342   .vector_size = sizeof (u32),
343   .format_trace = format_nsim_trace,
344   .type = VLIB_NODE_TYPE_INTERNAL,
345
346   .n_errors = ARRAY_LEN(nsim_error_strings),
347   .error_strings = nsim_error_strings,
348
349   .n_next_nodes = NSIM_N_NEXT,
350
351   /* edit / add dispositions here */
352   .next_nodes = {
353         [NSIM_NEXT_DROP] = "error-drop",
354   },
355 };
356 #endif /* CLIB_MARCH_VARIANT */
357 /* *INDENT-ON* */
358
359 /*
360  * fd.io coding-style-patch-verification: ON
361  *
362  * Local Variables:
363  * eval: (c-set-style "gnu")
364  * End:
365  */