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