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