FD.io VPP  v18.01-8-g0eacf49
Vector Packet Processing
ssvm_eth.c
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2015 Cisco and/or its affiliates.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at:
6  *
7  * http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 #include "ssvm_eth.h"
16 
18 
19 #define foreach_ssvm_eth_tx_func_error \
20 _(RING_FULL, "Tx packet drops (ring full)") \
21 _(NO_BUFFERS, "Tx packet drops (no buffers)") \
22 _(ADMIN_DOWN, "Tx packet drops (admin down)")
23 
24 typedef enum
25 {
26 #define _(f,s) SSVM_ETH_TX_ERROR_##f,
28 #undef _
31 
34 
35 int
36 ssvm_eth_create (ssvm_eth_main_t * em, u8 * name, int is_master)
37 {
38  ssvm_private_t *intfc;
39  void *oldheap;
40  clib_error_t *e;
44  u32 *elt_indices;
45  u8 enet_addr[6];
46  int i, rv;
47 
48  vec_add2 (em->intfcs, intfc, 1);
49 
50  intfc->ssvm_size = em->segment_size;
51  intfc->i_am_master = 1;
52  intfc->name = name;
53  intfc->my_pid = getpid ();
54  if (is_master == 0)
55  {
56  rv = ssvm_slave_init (intfc, 20 /* timeout in seconds */ );
57  if (rv < 0)
58  return rv;
59  goto create_vnet_interface;
60  }
61 
62  intfc->requested_va = em->next_base_va;
63  em->next_base_va += em->segment_size;
64  rv = ssvm_master_init (intfc, intfc - em->intfcs /* master index */ );
65 
66  if (rv < 0)
67  return rv;
68 
69  /* OK, segment created, set up queues and so forth. */
70 
71  sh = intfc->sh;
72  oldheap = ssvm_push_heap (sh);
73 
74  q = unix_shared_memory_queue_init (em->queue_elts, sizeof (u32),
75  0 /* consumer pid not interesting */ ,
76  0 /* signal not sent */ );
77  sh->opaque[TO_MASTER_Q_INDEX] = (void *) q;
78  q = unix_shared_memory_queue_init (em->queue_elts, sizeof (u32),
79  0 /* consumer pid not interesting */ ,
80  0 /* signal not sent */ );
81  sh->opaque[TO_SLAVE_Q_INDEX] = (void *) q;
82 
83  /*
84  * Preallocate the requested number of buffer chunks
85  * There must be a better way to do this, etc.
86  * Add some slop to avoid pool reallocation, which will not go well
87  */
88  elts = 0;
89  elt_indices = 0;
90 
92  vec_validate_aligned (elt_indices, em->nbuffers - 1, CLIB_CACHE_LINE_BYTES);
93 
94  for (i = 0; i < em->nbuffers; i++)
95  elt_indices[i] = i;
96 
97  sh->opaque[CHUNK_POOL_INDEX] = (void *) elts;
98  sh->opaque[CHUNK_POOL_FREELIST_INDEX] = (void *) elt_indices;
99  sh->opaque[CHUNK_POOL_NFREE] = (void *) (uword) em->nbuffers;
100 
101  ssvm_pop_heap (oldheap);
102 
103 create_vnet_interface:
104 
105  sh = intfc->sh;
106 
107  memset (enet_addr, 0, sizeof (enet_addr));
108  enet_addr[0] = 2;
109  enet_addr[1] = 0xFE;
110  enet_addr[2] = is_master;
111  enet_addr[5] = sh->master_index;
112 
114  (em->vnet_main, ssvm_eth_device_class.index, intfc - em->intfcs,
115  /* ethernet address */ enet_addr,
117 
118  if (e)
119  {
120  clib_error_report (e);
121  /* $$$$ unmap offending region? */
122  return VNET_API_ERROR_INVALID_INTERFACE;
123  }
124 
125  /* Declare link up */
128 
129  /* Let the games begin... */
130  if (is_master)
131  sh->ready = 1;
132  return 0;
133 }
134 
135 static clib_error_t *
137 {
138  u8 *name;
139  int is_master = 1;
140  int i, rv;
142 
144  {
145  if (unformat (input, "base-va %llx", &em->next_base_va))
146  ;
147  else if (unformat (input, "segment-size %lld", &em->segment_size))
148  em->segment_size = 1ULL << (max_log2 (em->segment_size));
149  else if (unformat (input, "nbuffers %lld", &em->nbuffers))
150  ;
151  else if (unformat (input, "queue-elts %lld", &em->queue_elts))
152  ;
153  else if (unformat (input, "slave"))
154  is_master = 0;
155  else if (unformat (input, "%s", &name))
156  vec_add1 (em->names, name);
157  else
158  break;
159  }
160 
161  /* No configured instances, we're done... */
162  if (vec_len (em->names) == 0)
163  return 0;
164 
165  for (i = 0; i < vec_len (em->names); i++)
166  {
167  rv = ssvm_eth_create (em, em->names[i], is_master);
168  if (rv < 0)
169  return clib_error_return (0, "ssvm_eth_create '%s' failed, error %d",
170  em->names[i], rv);
171  }
172 
174  VLIB_NODE_STATE_POLLING);
175 
176  return 0;
177 }
178 
179 VLIB_CONFIG_FUNCTION (ssvm_config, "ssvm_eth");
180 
181 
182 static clib_error_t *
184 {
186 
189  clib_warning ("ssvm_eth_queue_elt_t size %d not a multiple of %d",
190  sizeof (ssvm_eth_queue_elt_t), CLIB_CACHE_LINE_BYTES);
191 
192  em->vlib_main = vm;
193  em->vnet_main = vnet_get_main ();
194  em->elog_main = &vm->elog_main;
195 
196  /* default config param values... */
197 
198  em->next_base_va = 0x600000000ULL;
199  /*
200  * Allocate 2 full superframes in each dir (256 x 2 x 2 x 2048 bytes),
201  * 2mb; double that so we have plenty of space... 4mb
202  */
203  em->segment_size = 8 << 20;
204  em->nbuffers = 1024;
205  em->queue_elts = 512;
206  return 0;
207 }
208 
210 
212 #define _(n,s) s,
214 #undef _
215 };
216 
217 static u8 *
218 format_ssvm_eth_device_name (u8 * s, va_list * args)
219 {
220  u32 i = va_arg (*args, u32);
221 
222  s = format (s, "ssvmEthernet%d", i);
223  return s;
224 }
225 
226 static u8 *
227 format_ssvm_eth_device (u8 * s, va_list * args)
228 {
229  s = format (s, "SSVM Ethernet");
230  return s;
231 }
232 
233 static u8 *
234 format_ssvm_eth_tx_trace (u8 * s, va_list * args)
235 {
236  s = format (s, "Unimplemented...");
237  return s;
238 }
239 
240 
241 static uword
243  vlib_node_runtime_t * node, vlib_frame_t * f)
244 {
246  vnet_interface_output_runtime_t *rd = (void *) node->runtime_data;
248  ssvm_shared_header_t *sh = intfc->sh;
250  u32 *from;
251  u32 n_left;
252  ssvm_eth_queue_elt_t *elts, *elt, *prev_elt;
253  u32 my_pid = intfc->my_pid;
254  vlib_buffer_t *b0;
255  u32 bi0;
256  u32 size_this_buffer;
257  u32 chunks_this_buffer;
258  u8 i_am_master = intfc->i_am_master;
259  u32 elt_index;
260  int is_ring_full, interface_down;
261  int i;
262  volatile u32 *queue_lock;
263  u32 n_to_alloc = VLIB_FRAME_SIZE;
264  u32 n_allocated, n_present_in_cache, n_available;
265  u32 *elt_indices;
266 
267  if (i_am_master)
269  else
271 
272  queue_lock = (u32 *) q;
273 
274  from = vlib_frame_vector_args (f);
275  n_left = f->n_vectors;
276  is_ring_full = 0;
277  interface_down = 0;
278 
279  n_present_in_cache = vec_len (em->chunk_cache);
280 
281  /* admin / link up/down check */
282  if (sh->opaque[MASTER_ADMIN_STATE_INDEX] == 0 ||
284  {
285  interface_down = 1;
286  goto out;
287  }
288 
289  ssvm_lock (sh, my_pid, 1);
290 
291  elts = (ssvm_eth_queue_elt_t *) (sh->opaque[CHUNK_POOL_INDEX]);
292  elt_indices = (u32 *) (sh->opaque[CHUNK_POOL_FREELIST_INDEX]);
293  n_available = (u32) pointer_to_uword (sh->opaque[CHUNK_POOL_NFREE]);
294 
295  if (n_present_in_cache < n_left * 2)
296  {
297  vec_validate (em->chunk_cache, n_to_alloc + n_present_in_cache - 1);
298 
299  n_allocated = n_to_alloc < n_available ? n_to_alloc : n_available;
300 
301  if (PREDICT_TRUE (n_allocated > 0))
302  {
303  clib_memcpy (&em->chunk_cache[n_present_in_cache],
304  &elt_indices[n_available - n_allocated],
305  sizeof (u32) * n_allocated);
306  }
307 
308  n_present_in_cache += n_allocated;
309  n_available -= n_allocated;
310  sh->opaque[CHUNK_POOL_NFREE] = uword_to_pointer (n_available, void *);
311  _vec_len (em->chunk_cache) = n_present_in_cache;
312  }
313 
314  ssvm_unlock (sh);
315 
316  while (n_left)
317  {
318  bi0 = from[0];
319  b0 = vlib_get_buffer (vm, bi0);
320 
321  size_this_buffer = vlib_buffer_length_in_chain (vm, b0);
322  chunks_this_buffer = (size_this_buffer + (SSVM_BUFFER_SIZE - 1))
324 
325  /* If we're not going to be able to enqueue the buffer, tail drop. */
326  if (q->cursize >= q->maxsize)
327  {
328  is_ring_full = 1;
329  break;
330  }
331 
332  prev_elt = 0;
333  elt_index = ~0;
334  for (i = 0; i < chunks_this_buffer; i++)
335  {
336  if (PREDICT_FALSE (n_present_in_cache == 0))
337  goto out;
338 
339  elt_index = em->chunk_cache[--n_present_in_cache];
340  elt = elts + elt_index;
341 
342  elt->type = SSVM_PACKET_TYPE;
343  elt->flags = 0;
347  elt->current_data_hint = b0->current_data;
348  elt->owner = !i_am_master;
349  elt->tag = 1;
350 
351  clib_memcpy (elt->data, b0->data + b0->current_data,
352  b0->current_length);
353 
354  if (PREDICT_FALSE (prev_elt != 0))
355  prev_elt->next_index = elt - elts;
356 
357  if (PREDICT_FALSE (i < (chunks_this_buffer - 1)))
358  {
361  b0 = vlib_get_buffer (vm, b0->next_buffer);
362  }
363  prev_elt = elt;
364  }
365 
366  while (__sync_lock_test_and_set (queue_lock, 1))
367  ;
368 
369  unix_shared_memory_queue_add_raw (q, (u8 *) & elt_index);
371  *queue_lock = 0;
372 
373  from++;
374  n_left--;
375  }
376 
377 out:
378  if (PREDICT_FALSE (n_left))
379  {
380  if (is_ring_full)
381  vlib_error_count (vm, node->node_index, SSVM_ETH_TX_ERROR_RING_FULL,
382  n_left);
383  else if (interface_down)
384  vlib_error_count (vm, node->node_index, SSVM_ETH_TX_ERROR_ADMIN_DOWN,
385  n_left);
386  else
387  vlib_error_count (vm, node->node_index, SSVM_ETH_TX_ERROR_NO_BUFFERS,
388  n_left);
389 
390  vlib_buffer_free (vm, from, n_left);
391  }
392  else
394 
395  if (PREDICT_TRUE (vec_len (em->chunk_cache)))
396  _vec_len (em->chunk_cache) = n_present_in_cache;
397 
398  return f->n_vectors;
399 }
400 
401 static void
403 {
404  /* Nothing for now */
405 }
406 
407 static clib_error_t *
409  u32 flags)
410 {
411  vnet_hw_interface_t *hif = vnet_get_hw_interface (vnm, hw_if_index);
412  uword is_up = (flags & VNET_SW_INTERFACE_FLAG_ADMIN_UP) != 0;
414  ssvm_private_t *intfc = vec_elt_at_index (em->intfcs, hif->dev_instance);
416 
417  /* publish link-state in shared-memory, to discourage buffer-wasting */
418  sh = intfc->sh;
419  if (intfc->i_am_master)
420  sh->opaque[MASTER_ADMIN_STATE_INDEX] = (void *) is_up;
421  else
422  sh->opaque[SLAVE_ADMIN_STATE_INDEX] = (void *) is_up;
423 
424  return 0;
425 }
426 
427 static clib_error_t *
429  u32 hw_if_index,
430  struct vnet_sw_interface_t *st, int is_add)
431 {
432  /* Nothing for now */
433  return 0;
434 }
435 
436 /*
437  * Dynamically redirect all pkts from a specific interface
438  * to the specified node
439  */
440 static void
442  u32 node_index)
443 {
445  vnet_hw_interface_t *hw = vnet_get_hw_interface (vnm, hw_if_index);
447 
448  /* Shut off redirection */
449  if (node_index == ~0)
450  {
451  intfc->per_interface_next_index = node_index;
452  return;
453  }
454 
455  intfc->per_interface_next_index =
456  vlib_node_add_next (em->vlib_main, ssvm_eth_input_node.index, node_index);
457 }
458 
459 static u32
461 {
462  /* nothing for now */
463  return 0;
464 }
465 
466 /* *INDENT-OFF* */
468  .name = "ssvm-eth",
469  .tx_function = ssvm_eth_interface_tx,
470  .tx_function_n_errors = SSVM_ETH_TX_N_ERROR,
471  .tx_function_error_strings = ssvm_eth_tx_func_error_strings,
472  .format_device_name = format_ssvm_eth_device_name,
473  .format_device = format_ssvm_eth_device,
474  .format_tx_trace = format_ssvm_eth_tx_trace,
475  .clear_counters = ssvm_eth_clear_hw_interface_counters,
476  .admin_up_down_function = ssvm_eth_interface_admin_up_down,
477  .subif_add_del_function = ssvm_eth_subif_add_del_function,
478  .rx_redirect_to_node = ssvm_eth_set_interface_next_node,
479 };
480 
483 /* *INDENT-ON* */
484 
485 /*
486  * fd.io coding-style-patch-verification: ON
487  *
488  * Local Variables:
489  * eval: (c-set-style "gnu")
490  * End:
491  */
u64 segment_size
Definition: ssvm_eth.h:72
#define vec_validate(V, I)
Make sure vector is long enough for given index (no header, unspecified alignment) ...
Definition: vec.h:432
u64 ssvm_size
Definition: ssvm.h:77
vmrglw vmrglh hi
sll srl srl sll sra u16x4 i
Definition: vector_sse2.h:337
elog_main_t * elog_main
Definition: ssvm_eth.h:82
uword requested_va
Definition: ssvm.h:81
u32 vlib_hw_if_index
Definition: ssvm.h:79
clib_error_t * vnet_hw_interface_set_flags(vnet_main_t *vnm, u32 hw_if_index, u32 flags)
Definition: interface.c:538
int ssvm_master_init(ssvm_private_t *ssvm, u32 master_index)
Definition: ssvm.c:19
static void vlib_buffer_free(vlib_main_t *vm, u32 *buffers, u32 n_buffers)
Free buffers Frees the entire buffer chain for each buffer.
Definition: buffer_funcs.h:356
u8 runtime_data[0]
Function dependent node-runtime data.
Definition: node.h:464
vnet_main_t * vnet_get_main(void)
Definition: misc.c:47
#define PREDICT_TRUE(x)
Definition: clib.h:106
volatile u32 ready
Definition: ssvm.h:68
static void vlib_error_count(vlib_main_t *vm, uword node_index, uword counter, uword increment)
Definition: error_funcs.h:57
static vnet_hw_interface_t * vnet_get_hw_interface(vnet_main_t *vnm, u32 hw_if_index)
void * opaque[SSVM_N_OPAQUE]
Definition: ssvm.h:65
#define vec_add1(V, E)
Add 1 element to end of vector (unspecified alignment).
Definition: vec.h:518
#define SSVM_BUFFER_NEXT_PRESENT
Definition: ssvm_eth.h:47
#define vec_add2(V, P, N)
Add N elements to end of vector V, return pointer to new elements in P.
Definition: vec.h:557
static char * ssvm_eth_tx_func_error_strings[]
Definition: ssvm_eth.c:211
ssvm_shared_header_t * sh
Definition: ssvm.h:76
u8 * format(u8 *s, const char *fmt,...)
Definition: format.c:419
#define VNET_HW_INTERFACE_FLAG_LINK_UP
Definition: interface.h:394
#define vec_validate_aligned(V, I, A)
Make sure vector is long enough for given index (no header, specified alignment)
Definition: vec.h:443
static uword vlib_buffer_length_in_chain(vlib_main_t *vm, vlib_buffer_t *b)
Get length in bytes of the buffer chain.
Definition: buffer_funcs.h:107
static uword vlib_node_add_next(vlib_main_t *vm, uword node, uword next_node)
Definition: node_funcs.h:1108
static void ssvm_eth_set_interface_next_node(vnet_main_t *vnm, u32 hw_if_index, u32 node_index)
Definition: ssvm_eth.c:441
u32 * chunk_cache
Definition: ssvm_eth.h:67
static u8 * format_ssvm_eth_device_name(u8 *s, va_list *args)
Definition: ssvm_eth.c:218
#define VLIB_BUFFER_NEXT_PRESENT
Definition: buffer.h:95
i16 current_data
signed offset in data[], pre_data[] that we are currently processing.
Definition: buffer.h:68
#define VLIB_INIT_FUNCTION(x)
Definition: init.h:111
int ssvm_eth_create(ssvm_eth_main_t *em, u8 *name, int is_master)
Definition: ssvm_eth.c:36
#define vec_elt_at_index(v, i)
Get vector value at index i checking that i is in bounds.
static void * ssvm_push_heap(ssvm_shared_header_t *sh)
Definition: ssvm.h:134
#define clib_error_return(e, args...)
Definition: error.h:99
VNET_DEVICE_CLASS(ssvm_eth_device_class)
vlib_node_registration_t ssvm_eth_input_node
(constructor) VLIB_REGISTER_NODE (ssvm_eth_input_node)
Definition: node.c:17
vnet_device_class_t ssvm_eth_device_class
static void ssvm_pop_heap(void *oldheap)
Definition: ssvm.h:142
static uword pointer_to_uword(const void *p)
Definition: types.h:131
vnet_main_t * vnet_main
Definition: ssvm_eth.h:81
static uword ssvm_eth_interface_tx(vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *f)
Definition: ssvm_eth.c:242
static u8 * format_ssvm_eth_tx_trace(u8 *s, va_list *args)
Definition: ssvm_eth.c:234
#define pool_elt_at_index(p, i)
Returns pointer to element at given index.
Definition: pool.h:459
u16 current_length
Nbytes between current data and the end of this buffer.
Definition: buffer.h:72
static void ssvm_unlock(ssvm_shared_header_t *h)
Definition: ssvm.h:114
static void ssvm_eth_clear_hw_interface_counters(u32 instance)
Definition: ssvm_eth.c:402
struct _unformat_input_t unformat_input_t
vlib_main_t * vlib_main
Definition: ssvm_eth.h:80
u32 per_interface_next_index
Definition: ssvm.h:83
int ssvm_slave_init(ssvm_private_t *ssvm, int timeout_in_seconds)
Definition: ssvm.c:115
#define PREDICT_FALSE(x)
Definition: clib.h:105
#define VLIB_CONFIG_FUNCTION(x, n,...)
Definition: init.h:119
ssvm_private_t * intfcs
Definition: ssvm_eth.h:64
#define VLIB_FRAME_SIZE
Definition: node.h:328
static u8 * format_ssvm_eth_device(u8 *s, va_list *args)
Definition: ssvm_eth.c:227
u32 node_index
Node index.
Definition: node.h:437
#define uword_to_pointer(u, type)
Definition: types.h:136
#define UNFORMAT_END_OF_INPUT
Definition: format.h:143
u16 n_vectors
Definition: node.h:344
vlib_main_t * vm
Definition: buffer.c:283
#define SSVM_BUFFER_SIZE
Definition: ssvm_eth.h:38
#define clib_warning(format, args...)
Definition: error.h:59
#define clib_memcpy(a, b, c)
Definition: string.h:75
elog_main_t elog_main
Definition: main.h:155
u32 my_pid
Definition: ssvm.h:78
static clib_error_t * ssvm_eth_subif_add_del_function(vnet_main_t *vnm, u32 hw_if_index, struct vnet_sw_interface_t *st, int is_add)
Definition: ssvm_eth.c:428
static clib_error_t * ssvm_eth_init(vlib_main_t *vm)
Definition: ssvm_eth.c:183
#define VNET_SW_INTERFACE_FLAG_ADMIN_UP
Definition: interface.h:576
#define ASSERT(truth)
unsigned int u32
Definition: types.h:88
unix_shared_memory_queue_t * unix_shared_memory_queue_init(int nels, int elsize, int consumer_pid, int signal_when_queue_non_empty)
u32 next_buffer
Next buffer for this linked-list of buffers.
Definition: buffer.h:109
static clib_error_t * ssvm_eth_interface_admin_up_down(vnet_main_t *vnm, u32 hw_if_index, u32 flags)
Definition: ssvm_eth.c:408
clib_error_t * ethernet_register_interface(vnet_main_t *vnm, u32 dev_class_index, u32 dev_instance, u8 *address, u32 *hw_if_index_return, ethernet_flag_change_function_t flag_change)
Definition: interface.c:273
#define clib_error_report(e)
Definition: error.h:113
static void vlib_node_set_state(vlib_main_t *vm, u32 node_index, vlib_node_state_t new_state)
Set node dispatch state.
Definition: node_funcs.h:147
u64 next_base_va
Definition: ssvm_eth.h:71
u8 * name
Definition: ssvm.h:80
u8 data[SSVM_BUFFER_SIZE]
Definition: ssvm_eth.h:56
u64 uword
Definition: types.h:112
int unix_shared_memory_queue_add_raw(unix_shared_memory_queue_t *q, u8 *elem)
u32 total_length_not_including_first_buffer
Only valid for first buffer in chain.
Definition: buffer.h:142
ssvm_eth_tx_func_error_t
Definition: ssvm_eth.c:24
ssvm_eth_main_t ssvm_eth_main
Definition: ssvm_eth.c:17
#define vec_len(v)
Number of elements in vector (rvalue-only, NULL tolerant)
unsigned char u8
Definition: types.h:56
static uword max_log2(uword x)
Definition: clib.h:236
static void * vlib_frame_vector_args(vlib_frame_t *f)
Get pointer to frame vector data.
Definition: node_funcs.h:267
u16 total_length_not_including_first_buffer
Definition: ssvm_eth.h:52
u8 data[0]
Packet data.
Definition: buffer.h:159
#define SSVM_PACKET_TYPE
Definition: ssvm_eth.h:40
#define CLIB_MEMORY_BARRIER()
Definition: clib.h:109
static clib_error_t * ssvm_config(vlib_main_t *vm, unformat_input_t *input)
Definition: ssvm_eth.c:136
static u32 ssvm_eth_flag_change(vnet_main_t *vnm, vnet_hw_interface_t *hi, u32 flags)
Definition: ssvm_eth.c:460
u32 flags
Definition: vhost-user.h:77
#define CLIB_CACHE_LINE_BYTES
Definition: cache.h:67
u32 flags
buffer flags: VLIB_BUFFER_FREE_LIST_INDEX_MASK: bits used to store free list index, VLIB_BUFFER_IS_TRACED: trace this buffer.
Definition: buffer.h:75
#define foreach_ssvm_eth_tx_func_error
Definition: ssvm_eth.c:19
static vlib_buffer_t * vlib_get_buffer(vlib_main_t *vm, u32 buffer_index)
Translate buffer index into buffer pointer.
Definition: buffer_funcs.h:57
int i_am_master
Definition: ssvm.h:82
#define VLIB_DEVICE_TX_FUNCTION_MULTIARCH(dev, fn)
Definition: interface.h:232
static void ssvm_lock(ssvm_shared_header_t *h, u32 my_pid, u32 tag)
Definition: ssvm.h:88
uword unformat(unformat_input_t *i, const char *fmt,...)
Definition: unformat.c:972
static uword unformat_check_input(unformat_input_t *i)
Definition: format.h:169
struct _unix_shared_memory_queue unix_shared_memory_queue_t