Line data Source code
1 : /*
2 : * mdata.c - Buffer metadata change tracker
3 : *
4 : * Copyright (c) 2019 Cisco and/or its affiliates.
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 :
18 : #include <vnet/vnet.h>
19 : #include <vnet/plugin/plugin.h>
20 : #include <mdata/mdata.h>
21 :
22 : #include <vlibapi/api.h>
23 : #include <vlibmemory/api.h>
24 : #include <vppinfra/callback_data.h>
25 : #include <vpp/app/version.h>
26 : #include <stdbool.h>
27 :
28 : #include <mdata/mdata.api_enum.h>
29 : #include <mdata/mdata.api_types.h>
30 :
31 : #define REPLY_MSG_ID_BASE mmp->msg_id_base
32 : #include <vlibapi/api_helper_macros.h>
33 :
34 : mdata_main_t mdata_main;
35 :
36 : /** @file mdata.c
37 : * buffer metadata change tracker
38 : */
39 :
40 : static mdata_t mdata_none;
41 :
42 : /** Metadata tracking callback
43 : before_or_after: 0 => before, 1=> after
44 : */
45 : static void
46 0 : mdata_trace_callback (vlib_node_runtime_perf_callback_data_t * data,
47 : vlib_node_runtime_perf_callback_args_t * args)
48 : {
49 : int i;
50 0 : mdata_main_t *mm = &mdata_main;
51 : vlib_buffer_t *bufs[VLIB_FRAME_SIZE], **b;
52 : u32 *from;
53 : u32 n_left_from;
54 : mdata_t *before, *modifies;
55 : u8 *after;
56 0 : vlib_main_t *vm = args->vm;
57 0 : vlib_frame_t *frame = args->frame;
58 0 : vlib_node_runtime_t *node = args->node;
59 :
60 0 : if (PREDICT_FALSE (args->call_type == VLIB_NODE_RUNTIME_PERF_RESET))
61 0 : return;
62 :
63 : /* Input nodes don't have frames, etc. */
64 0 : if (frame == 0)
65 0 : return;
66 :
67 0 : n_left_from = frame->n_vectors;
68 :
69 0 : if (n_left_from == 0)
70 0 : return;
71 :
72 0 : from = vlib_frame_vector_args (frame);
73 :
74 0 : vlib_get_buffers (vm, from, bufs, n_left_from);
75 0 : b = bufs;
76 :
77 0 : if (args->call_type == VLIB_NODE_RUNTIME_PERF_AFTER)
78 0 : goto after_pass;
79 :
80 : /* Resize the per-thread "before" vector to cover the current frame */
81 0 : vec_reset_length (mm->before_per_thread[vm->thread_index]);
82 0 : vec_validate (mm->before_per_thread[vm->thread_index], n_left_from - 1);
83 0 : before = mm->before_per_thread[vm->thread_index];
84 0 : before->node_index = ~0;
85 :
86 : /* Before we call the dispatch fn, copy metadata. */
87 0 : while (n_left_from > 0)
88 : {
89 0 : clib_memcpy_fast (before->mdata, b[0], sizeof (before->mdata));
90 0 : b++;
91 0 : before++;
92 0 : n_left_from--;
93 : }
94 0 : return;
95 :
96 0 : after_pass:
97 :
98 : /* Recover the metadata copy we saved a moment ago */
99 0 : before = mm->before_per_thread[vm->thread_index];
100 :
101 : /* We'd better have the same number of buffers... */
102 0 : ASSERT (n_left_from == vec_len (before));
103 0 : ASSERT (node->node_index);
104 :
105 0 : clib_spinlock_lock_if_init (&mm->modify_lock);
106 :
107 : /*
108 : * Resize the per-node accumulator vector as needed
109 : * Paint the "no data" patter across any nodes we haven't seen yet
110 : */
111 0 : vec_validate_init_empty (mm->modifies, node->node_index, mdata_none);
112 0 : modifies = vec_elt_at_index (mm->modifies, node->node_index);
113 0 : modifies->node_index = node->node_index;
114 0 : before = mm->before_per_thread[vm->thread_index];
115 :
116 : /* Walk the frame */
117 0 : while (n_left_from > 0)
118 : {
119 0 : after = (u8 *) b[0];
120 :
121 : /* Compare metadata before and after node dispatch fn */
122 0 : for (i = 0; i < ARRAY_LEN (before->mdata); i++)
123 : {
124 : /* Mark mdata octet changed */
125 0 : if (before->mdata[i] != after[i])
126 0 : modifies->mdata[i] = 0xff;
127 : }
128 :
129 0 : b++;
130 0 : before++;
131 0 : n_left_from--;
132 : }
133 :
134 0 : clib_spinlock_unlock_if_init (&mm->modify_lock);
135 : }
136 :
137 : int
138 0 : mdata_enable_disable (mdata_main_t * mmp, int enable_disable)
139 : {
140 0 : int rv = 0;
141 0 : vlib_thread_main_t *thread_main = vlib_get_thread_main ();
142 : int i;
143 :
144 0 : if (mmp->modify_lock == 0 && thread_main->n_vlib_mains > 1)
145 0 : clib_spinlock_init (&mmp->modify_lock);
146 :
147 0 : if (vec_len (mmp->before_per_thread) == 0)
148 : {
149 0 : mdata_none.node_index = ~0;
150 0 : vec_validate (mmp->before_per_thread, vlib_get_n_threads () - 1);
151 : }
152 :
153 : /* Reset the per-node accumulator, see vec_validate_init_empty above */
154 0 : vec_reset_length (mmp->modifies);
155 :
156 0 : for (i = 0; i < vlib_get_n_threads (); i++)
157 : {
158 0 : vlib_main_t *ovm = vlib_get_main_by_index (i);
159 0 : if (ovm == 0)
160 0 : continue;
161 :
162 0 : clib_callback_data_enable_disable (
163 : &ovm->vlib_node_runtime_perf_callbacks, mdata_trace_callback,
164 : enable_disable);
165 : }
166 :
167 0 : return rv;
168 : }
169 :
170 : static clib_error_t *
171 0 : mdata_enable_disable_command_fn (vlib_main_t * vm,
172 : unformat_input_t * input,
173 : vlib_cli_command_t * cmd)
174 : {
175 0 : mdata_main_t *mmp = &mdata_main;
176 0 : int enable_disable = 1;
177 :
178 : int rv;
179 :
180 0 : while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
181 : {
182 0 : if (unformat (input, "disable") || unformat (input, "off"))
183 0 : enable_disable = 0;
184 0 : if (unformat (input, "enable") || unformat (input, "on"))
185 0 : enable_disable = 1;
186 : else
187 : break;
188 : }
189 :
190 0 : rv = mdata_enable_disable (mmp, enable_disable);
191 :
192 0 : switch (rv)
193 : {
194 0 : case 0:
195 0 : break;
196 :
197 0 : default:
198 0 : return clib_error_return (0, "mdata_enable_disable returned %d", rv);
199 : }
200 0 : return 0;
201 : }
202 :
203 : /*?
204 : * This command enables or disables buffer metadata change tracking
205 : *
206 : * @cliexpar
207 : * To enable buffer metadata change tracking:
208 : * @cliexstart{buffer metadata tracking on}
209 : * Tracking enabled
210 : * @cliexend
211 : *
212 : * @cliexstart{buffer metadata tracking off}
213 : * Tracking disabled
214 : * @cliexend
215 : ?*/
216 :
217 : /* *INDENT-OFF* */
218 103749 : VLIB_CLI_COMMAND (mdata_enable_disable_command, static) =
219 : {
220 : .path = "buffer metadata tracking",
221 : .short_help = "buffer metadata tracking [on][off]",
222 : .function = mdata_enable_disable_command_fn,
223 : };
224 : /* *INDENT-ON* */
225 :
226 : /* API message handler */
227 0 : static void vl_api_mdata_enable_disable_t_handler
228 : (vl_api_mdata_enable_disable_t * mp)
229 : {
230 : vl_api_mdata_enable_disable_reply_t *rmp;
231 0 : mdata_main_t *mmp = &mdata_main;
232 : int rv;
233 :
234 0 : rv = mdata_enable_disable (mmp, (int) (mp->enable_disable));
235 :
236 0 : REPLY_MACRO (VL_API_MDATA_ENABLE_DISABLE_REPLY);
237 : }
238 :
239 : /* API definitions */
240 : #include <mdata/mdata.api.c>
241 :
242 : static clib_error_t *
243 559 : mdata_init (vlib_main_t * vm)
244 : {
245 559 : mdata_main_t *mmp = &mdata_main;
246 559 : clib_error_t *error = 0;
247 :
248 559 : mmp->vlib_main = vm;
249 559 : mmp->vnet_main = vnet_get_main ();
250 :
251 : /* Add our API messages to the global name_crc hash table */
252 559 : mmp->msg_id_base = setup_message_id_table ();
253 :
254 559 : return error;
255 : }
256 :
257 1119 : VLIB_INIT_FUNCTION (mdata_init);
258 :
259 : /* *INDENT-OFF* */
260 : VLIB_PLUGIN_REGISTER () =
261 : {
262 : .version = VPP_BUILD_VER,
263 : .description = "Buffer metadata change tracker."
264 : };
265 : /* *INDENT-ON* */
266 :
267 :
268 : #define foreach_primary_metadata_field \
269 : _(current_data) \
270 : _(current_length) \
271 : _(flags) \
272 : _(flow_id) \
273 : _(ref_count) \
274 : _(buffer_pool_index) \
275 : _(error) \
276 : _(next_buffer) \
277 : _(current_config_index) \
278 : _(punt_reason)
279 :
280 : #define foreach_opaque_metadata_field \
281 : _(sw_if_index[0]) \
282 : _(sw_if_index[1]) \
283 : _(l2_hdr_offset) \
284 : _(l3_hdr_offset) \
285 : _(l4_hdr_offset) \
286 : _(feature_arc_index) \
287 : _(ip.adj_index[0]) \
288 : _(ip.adj_index[1]) \
289 : _(ip.flow_hash) \
290 : _(ip.save_protocol) \
291 : _(ip.fib_index) \
292 : _(ip.icmp.type) \
293 : _(ip.icmp.code) \
294 : _(ip.icmp.data) \
295 : _(ip.reass.next_index) \
296 : _(ip.reass.error_next_index) \
297 : _(ip.reass.owner_thread_index) \
298 : _(ip.reass.ip_proto) \
299 : _(ip.reass.l4_src_port) \
300 : _(ip.reass.l4_dst_port) \
301 : _(ip.reass.estimated_mtu) \
302 : _(ip.reass.fragment_first) \
303 : _(ip.reass.fragment_last) \
304 : _(ip.reass.range_first) \
305 : _(ip.reass.range_last) \
306 : _(ip.reass.next_range_bi) \
307 : _(ip.reass.ip6_frag_hdr_offset) \
308 : _(mpls.ttl) \
309 : _(mpls.exp) \
310 : _(mpls.first) \
311 : _(mpls.save_rewrite_length) \
312 : _(mpls.mpls_hdr_length) \
313 : _(mpls.bier.n_bytes) \
314 : _(l2.feature_bitmap) \
315 : _(l2.bd_index) \
316 : _(l2.l2fib_sn) \
317 : _(l2.l2_len) \
318 : _(l2.shg) \
319 : _(l2.bd_age) \
320 : _(l2t.next_index) \
321 : _(l2t.session_index) \
322 : _(l2_classify.table_index) \
323 : _(l2_classify.opaque_index) \
324 : _(l2_classify.hash) \
325 : _(policer.index) \
326 : _(ipsec.sad_index) \
327 : _(ipsec.protect_index) \
328 : _(map.mtu) \
329 : _(map_t.map_domain_index) \
330 : _(map_t.v6.saddr) \
331 : _(map_t.v6.daddr) \
332 : _(map_t.v6.frag_offset) \
333 : _(map_t.v6.l4_offset) \
334 : _(map_t.v6.l4_protocol) \
335 : _(map_t.checksum_offset) \
336 : _(map_t.mtu) \
337 : _(ip_frag.mtu) \
338 : _(ip_frag.next_index) \
339 : _(ip_frag.flags) \
340 : _(cop.current_config_index) \
341 : _(lisp.overlay_afi) \
342 : _(tcp.connection_index) \
343 : _(tcp.seq_number) \
344 : _(tcp.next_node_opaque) \
345 : _(tcp.seq_end) \
346 : _(tcp.ack_number) \
347 : _(tcp.hdr_offset) \
348 : _(tcp.data_offset) \
349 : _(tcp.data_len) \
350 : _(tcp.flags) \
351 : _(snat.flags)
352 :
353 : #define foreach_opaque2_metadata_field \
354 : _ (qos.bits) \
355 : _ (qos.source) \
356 : _ (loop_counter) \
357 : _ (gso_size) \
358 : _ (gso_l4_hdr_sz)
359 :
360 : static u8 *
361 0 : format_buffer_metadata_changes (u8 * s, va_list * args)
362 : {
363 0 : mdata_main_t *mm = va_arg (*args, mdata_main_t *);
364 0 : int verbose = va_arg (*args, int);
365 : mdata_t *modifies;
366 : vlib_buffer_t *b;
367 : vnet_buffer_opaque_t *o;
368 : vnet_buffer_opaque2_t *o2;
369 : vlib_node_t *node;
370 : int i, j;
371 : int printed;
372 :
373 0 : clib_spinlock_lock_if_init (&mm->modify_lock);
374 :
375 0 : for (i = 0; i < vec_len (mm->modifies); i++)
376 : {
377 0 : modifies = vec_elt_at_index (mm->modifies, i);
378 0 : node = vlib_get_node (mm->vlib_main, i);
379 :
380 : /* No data for this node? */
381 0 : if (modifies->node_index == ~0)
382 : {
383 0 : if (verbose)
384 0 : s = format (s, "\n%v: no data\n", node->name);
385 0 : continue;
386 : }
387 :
388 : /* We visited the node, but it may not have changed any metadata... */
389 0 : for (j = 0; j < ARRAY_LEN (modifies->mdata); j++)
390 : {
391 0 : if (modifies->mdata[j])
392 0 : goto found;
393 : }
394 0 : s = format (s, "\n%v: no metadata changes\n", node->name);
395 0 : continue;
396 :
397 0 : found:
398 : /* Fields which the node modifies will be non-zero */
399 0 : b = (vlib_buffer_t *) (modifies->mdata);
400 :
401 : /* Dump primary metadata changes */
402 0 : s = format (s, "\n%v: ", node->name);
403 :
404 0 : printed = 0;
405 : #define _(n) if (b->n) {s = format (s, "%s ", #n); printed = 1;}
406 0 : foreach_primary_metadata_field;
407 : #undef _
408 :
409 0 : if (printed == 0)
410 0 : s = format (s, "no vlib_buffer_t metadata changes");
411 :
412 0 : vec_add1 (s, '\n');
413 :
414 : /*
415 : * Dump opaque union changes.
416 : * Hopefully this will give folks a clue about opaque
417 : * union data conflicts. That's the point of the exercise...
418 : */
419 0 : o = vnet_buffer (b);
420 0 : printed = 0;
421 0 : s = format (s, " vnet_buffer_t: ");
422 :
423 : #define _(n) if (o->n) {s = format (s, "%s ", #n); printed = 1;}
424 0 : foreach_opaque_metadata_field;
425 : #undef _
426 :
427 0 : if (printed == 0)
428 0 : s = format (s, "no changes");
429 :
430 0 : vec_add1 (s, '\n');
431 :
432 0 : o2 = vnet_buffer2 (b);
433 0 : printed = 0;
434 0 : s = format (s, " vnet_buffer2_t: ");
435 :
436 : #define _(n) if (o2->n) {s = format (s, "%s ", #n); printed = 1;}
437 0 : foreach_opaque2_metadata_field;
438 : #undef _
439 0 : if (printed == 0)
440 0 : s = format (s, "no changes");
441 :
442 0 : vec_add1 (s, '\n');
443 :
444 : }
445 :
446 0 : clib_spinlock_unlock_if_init (&mm->modify_lock);
447 :
448 0 : return s;
449 : }
450 :
451 : static clib_error_t *
452 0 : show_metadata_command_fn (vlib_main_t * vm,
453 : unformat_input_t * input, vlib_cli_command_t * cmd)
454 : {
455 0 : int verbose = 0;
456 :
457 0 : while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
458 : {
459 0 : if (unformat (input, "verbose %=", &verbose, 1))
460 : ;
461 : else
462 0 : break;
463 : }
464 :
465 0 : vlib_cli_output (vm, "%U", format_buffer_metadata_changes, &mdata_main,
466 : verbose);
467 0 : return 0;
468 : }
469 :
470 : /*?
471 : * This command displays buffer metadata change information
472 : * @cliexpar
473 : * How to display buffer metadata change information
474 : * @cliexstart{show buffer metadata}
475 : * ethernet-input: current_data current_length flags error
476 : * vnet_buffer_t: l2_hdr_offset l3_hdr_offset
477 : * vnet_buffer2_t: no changes
478 : * @cliexend
479 : ?*/
480 :
481 : /* *INDENT-OFF* */
482 103749 : VLIB_CLI_COMMAND (show_metadata_command, static) =
483 : {
484 : .path = "show buffer metadata",
485 : .short_help = "show buffer metadata",
486 : .function = show_metadata_command_fn,
487 : };
488 : /* *INDENT-OFF* */
489 :
490 : /*
491 : * fd.io coding-style-patch-verification: ON
492 : *
493 : * Local Variables:
494 : * eval: (c-set-style "gnu")
495 : * End:
496 : */
|