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