3a7415c08676d103f33d6c4bb2b83592d50f51e7
[vpp.git] / src / vlibmemory / memory_vlib.c
1 /*
2  *------------------------------------------------------------------
3  * memory_vlib.c
4  *
5  * Copyright (c) 2009 Cisco and/or its affiliates.
6  * Licensed under the Apache License, Version 2.0 (the "License");
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at:
9  *
10  *     http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  *------------------------------------------------------------------
18  */
19
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <string.h>
23 #include <unistd.h>
24 #include <sys/types.h>
25 #include <signal.h>
26 #include <pthread.h>
27 #include <vppinfra/vec.h>
28 #include <vppinfra/hash.h>
29 #include <vppinfra/pool.h>
30 #include <vppinfra/format.h>
31 #include <vppinfra/byte_order.h>
32 #include <vppinfra/elog.h>
33 #include <stdarg.h>
34 #include <vlib/vlib.h>
35 #include <vlib/unix/unix.h>
36 #include <vlibapi/api.h>
37 #include <vlibmemory/api.h>
38
39 #define TRACE_VLIB_MEMORY_QUEUE 0
40
41 #include <vlibmemory/vl_memory_msg_enum.h>      /* enumerate all vlib messages */
42
43 #define vl_typedefs             /* define message structures */
44 #include <vlibmemory/vl_memory_api_h.h>
45 #undef vl_typedefs
46
47 /* instantiate all the print functions we know about */
48 #define vl_print(handle, ...) vlib_cli_output (handle, __VA_ARGS__)
49 #define vl_printfun
50 #include <vlibmemory/vl_memory_api_h.h>
51 #undef vl_printfun
52
53 static inline void *
54 vl_api_memclnt_create_t_print (vl_api_memclnt_create_t * a, void *handle)
55 {
56   vl_print (handle, "vl_api_memclnt_create_t:\n");
57   vl_print (handle, "name: %s\n", a->name);
58   vl_print (handle, "input_queue: 0x%wx\n", a->input_queue);
59   vl_print (handle, "context: %u\n", (unsigned) a->context);
60   vl_print (handle, "ctx_quota: %ld\n", (long) a->ctx_quota);
61   return handle;
62 }
63
64 static inline void *
65 vl_api_memclnt_delete_t_print (vl_api_memclnt_delete_t * a, void *handle)
66 {
67   vl_print (handle, "vl_api_memclnt_delete_t:\n");
68   vl_print (handle, "index: %u\n", (unsigned) a->index);
69   vl_print (handle, "handle: 0x%wx\n", a->handle);
70   return handle;
71 }
72
73 static inline void *
74 vl_api_trace_plugin_msg_ids_t_print (vl_api_trace_plugin_msg_ids_t * a,
75                                      void *handle)
76 {
77   vl_print (handle, "vl_api_trace_plugin_msg_ids: %s first %u last %u\n",
78             a->plugin_name,
79             clib_host_to_net_u16 (a->first_msg_id),
80             clib_host_to_net_u16 (a->last_msg_id));
81   return handle;
82 }
83
84 /* instantiate all the endian swap functions we know about */
85 #define vl_endianfun
86 #include <vlibmemory/vl_memory_api_h.h>
87 #undef vl_endianfun
88
89 void vl_socket_api_send (vl_api_registration_t * rp, u8 * elem)
90   __attribute__ ((weak));
91
92 void
93 vl_socket_api_send (vl_api_registration_t * rp, u8 * elem)
94 {
95   static int count;
96
97   if (count++ < 5)
98     clib_warning ("need to link against -lvlibsocket, msg not sent!");
99 }
100
101 void
102 vl_msg_api_send (vl_api_registration_t * rp, u8 * elem)
103 {
104   if (PREDICT_FALSE (rp->registration_type > REGISTRATION_TYPE_SHMEM))
105     {
106       vl_socket_api_send (rp, elem);
107     }
108   else
109     {
110       vl_msg_api_send_shmem (rp->vl_input_queue, elem);
111     }
112 }
113
114 u8 *
115 vl_api_serialize_message_table (api_main_t * am, u8 * vector)
116 {
117   serialize_main_t _sm, *sm = &_sm;
118   hash_pair_t *hp;
119   u32 nmsg = hash_elts (am->msg_index_by_name_and_crc);
120
121   serialize_open_vector (sm, vector);
122
123   /* serialize the count */
124   serialize_integer (sm, nmsg, sizeof (u32));
125
126   /* *INDENT-OFF* */
127   hash_foreach_pair (hp, am->msg_index_by_name_and_crc,
128   ({
129     serialize_likely_small_unsigned_integer (sm, hp->value[0]);
130     serialize_cstring (sm, (char *) hp->key);
131   }));
132   /* *INDENT-ON* */
133
134   return serialize_close_vector (sm);
135 }
136
137 /*
138  * vl_api_memclnt_create_t_handler
139  */
140 void
141 vl_api_memclnt_create_t_handler (vl_api_memclnt_create_t * mp)
142 {
143   vl_api_registration_t **regpp;
144   vl_api_registration_t *regp;
145   vl_api_memclnt_create_reply_t *rp;
146   svm_region_t *svm;
147   unix_shared_memory_queue_t *q;
148   int rv = 0;
149   void *oldheap;
150   api_main_t *am = &api_main;
151   u8 *serialized_message_table = 0;
152
153   /*
154    * This is tortured. Maintain a vlib-address-space private
155    * pool of client registrations. We use the shared-memory virtual
156    * address of client structure as a handle, to allow direct
157    * manipulation of context quota vbls from the client library.
158    *
159    * This scheme causes trouble w/ API message trace replay, since
160    * some random VA from clib_mem_alloc() certainly won't
161    * occur in the Linux sim. The (very) few places
162    * that care need to use the pool index.
163    *
164    * Putting the registration object(s) into a pool in shared memory and
165    * using the pool index as a handle seems like a great idea.
166    * Unfortunately, each and every reference to that pool would need
167    * to be protected by a mutex:
168    *
169    *     Client                      VLIB
170    *     ------                      ----
171    *     convert pool index to
172    *     pointer.
173    *     <deschedule>
174    *                                 expand pool
175    *                                 <deschedule>
176    *     kaboom!
177    */
178
179   pool_get (am->vl_clients, regpp);
180
181   svm = am->vlib_rp;
182
183   if (am->serialized_message_table_in_shmem == 0)
184     serialized_message_table = vl_api_serialize_message_table (am, 0);
185
186   pthread_mutex_lock (&svm->mutex);
187   oldheap = svm_push_data_heap (svm);
188   *regpp = clib_mem_alloc (sizeof (vl_api_registration_t));
189
190   regp = *regpp;
191   memset (regp, 0, sizeof (*regp));
192   regp->registration_type = REGISTRATION_TYPE_SHMEM;
193   regp->vl_api_registration_pool_index = regpp - am->vl_clients;
194
195   q = regp->vl_input_queue = (unix_shared_memory_queue_t *) (uword)
196     mp->input_queue;
197
198   regp->name = format (0, "%s", mp->name);
199   vec_add1 (regp->name, 0);
200   if (serialized_message_table)
201     am->serialized_message_table_in_shmem =
202       vec_dup (serialized_message_table);
203
204   pthread_mutex_unlock (&svm->mutex);
205   svm_pop_heap (oldheap);
206
207   vec_free (serialized_message_table);
208
209   rp = vl_msg_api_alloc (sizeof (*rp));
210   rp->_vl_msg_id = ntohs (VL_API_MEMCLNT_CREATE_REPLY);
211   rp->handle = (uword) regp;
212   rp->index = vl_msg_api_handle_from_index_and_epoch
213     (regp->vl_api_registration_pool_index,
214      am->shmem_hdr->application_restarts);
215   rp->context = mp->context;
216   rp->response = ntohl (rv);
217   rp->message_table = (u64) am->serialized_message_table_in_shmem;
218
219   vl_msg_api_send_shmem (q, (u8 *) & rp);
220 }
221
222 /* Application callback to clean up leftover registrations from this client */
223 int vl_api_memclnt_delete_callback (u32 client_index) __attribute__ ((weak));
224
225 int
226 vl_api_memclnt_delete_callback (u32 client_index)
227 {
228   return 0;
229 }
230
231 /*
232  * vl_api_memclnt_delete_t_handler
233  */
234 void
235 vl_api_memclnt_delete_t_handler (vl_api_memclnt_delete_t * mp)
236 {
237   vl_api_registration_t **regpp;
238   vl_api_registration_t *regp;
239   vl_api_memclnt_delete_reply_t *rp;
240   svm_region_t *svm;
241   void *oldheap;
242   api_main_t *am = &api_main;
243   u32 handle, client_index, epoch;
244
245   handle = mp->index;
246
247   if (vl_api_memclnt_delete_callback (handle))
248     return;
249
250   epoch = vl_msg_api_handle_get_epoch (handle);
251   client_index = vl_msg_api_handle_get_index (handle);
252
253   if (epoch != (am->shmem_hdr->application_restarts & VL_API_EPOCH_MASK))
254     {
255       clib_warning
256         ("Stale clnt delete index %d old epoch %d cur epoch %d",
257          client_index, epoch,
258          (am->shmem_hdr->application_restarts & VL_API_EPOCH_MASK));
259       return;
260     }
261
262   regpp = am->vl_clients + client_index;
263
264   if (!pool_is_free (am->vl_clients, regpp))
265     {
266       regp = *regpp;
267       svm = am->vlib_rp;
268
269       /* $$$ check the input queue for e.g. punted sf's */
270
271       rp = vl_msg_api_alloc (sizeof (*rp));
272       rp->_vl_msg_id = ntohs (VL_API_MEMCLNT_DELETE_REPLY);
273       rp->handle = mp->handle;
274       rp->response = 1;
275
276       vl_msg_api_send_shmem (regp->vl_input_queue, (u8 *) & rp);
277
278       if (client_index != regp->vl_api_registration_pool_index)
279         {
280           clib_warning ("mismatch client_index %d pool_index %d",
281                         client_index, regp->vl_api_registration_pool_index);
282           vl_msg_api_free (rp);
283           return;
284         }
285
286       /* No dangling references, please */
287       *regpp = 0;
288
289       pool_put_index (am->vl_clients, regp->vl_api_registration_pool_index);
290
291       pthread_mutex_lock (&svm->mutex);
292       oldheap = svm_push_data_heap (svm);
293       /* Poison the old registration */
294       memset (regp, 0xF1, sizeof (*regp));
295       clib_mem_free (regp);
296       pthread_mutex_unlock (&svm->mutex);
297       svm_pop_heap (oldheap);
298     }
299   else
300     {
301       clib_warning ("unknown client ID %d", mp->index);
302     }
303 }
304
305 void
306 vl_api_get_first_msg_id_t_handler (vl_api_get_first_msg_id_t * mp)
307 {
308   vl_api_get_first_msg_id_reply_t *rmp;
309   unix_shared_memory_queue_t *q;
310   uword *p;
311   api_main_t *am = &api_main;
312   vl_api_msg_range_t *rp;
313   u8 name[64];
314   u16 first_msg_id = ~0;
315   int rv = -7;                  /* VNET_API_ERROR_INVALID_VALUE */
316
317   q = vl_api_client_index_to_input_queue (mp->client_index);
318   if (!q)
319     return;
320
321   if (am->msg_range_by_name == 0)
322     goto out;
323
324   strncpy ((char *) name, (char *) mp->name, ARRAY_LEN (name) - 1);
325
326   p = hash_get_mem (am->msg_range_by_name, name);
327   if (p == 0)
328     goto out;
329
330   rp = vec_elt_at_index (am->msg_ranges, p[0]);
331
332   first_msg_id = rp->first_msg_id;
333   rv = 0;
334
335 out:
336
337   rmp = vl_msg_api_alloc (sizeof (*rmp));
338   rmp->_vl_msg_id = ntohs (VL_API_GET_FIRST_MSG_ID_REPLY);
339   rmp->context = mp->context;
340   rmp->retval = ntohl (rv);
341   rmp->first_msg_id = ntohs (first_msg_id);
342   vl_msg_api_send_shmem (q, (u8 *) & rmp);
343 }
344
345 #define foreach_vlib_api_msg                    \
346 _(MEMCLNT_CREATE, memclnt_create)               \
347 _(MEMCLNT_DELETE, memclnt_delete)               \
348 _(GET_FIRST_MSG_ID, get_first_msg_id)
349
350 /*
351  * vl_api_init
352  */
353 static int
354 memory_api_init (char *region_name)
355 {
356   int rv;
357   vl_msg_api_msg_config_t cfg;
358   vl_msg_api_msg_config_t *c = &cfg;
359
360   memset (c, 0, sizeof (*c));
361
362   if ((rv = vl_map_shmem (region_name, 1 /* is_vlib */ )) < 0)
363     return rv;
364
365 #define _(N,n) do {                                             \
366     c->id = VL_API_##N;                                         \
367     c->name = #n;                                               \
368     c->handler = vl_api_##n##_t_handler;                        \
369     c->cleanup = vl_noop_handler;                               \
370     c->endian = vl_api_##n##_t_endian;                          \
371     c->print = vl_api_##n##_t_print;                            \
372     c->size = sizeof(vl_api_##n##_t);                           \
373     c->traced = 1; /* trace, so these msgs print */             \
374     c->replay = 0; /* don't replay client create/delete msgs */ \
375     c->message_bounce = 0; /* don't bounce this message */      \
376     vl_msg_api_config(c);} while (0);
377
378   foreach_vlib_api_msg;
379 #undef _
380
381   return 0;
382 }
383
384 #define foreach_histogram_bucket                \
385 _(400)                                          \
386 _(200)                                          \
387 _(100)                                          \
388 _(10)
389
390 typedef enum
391 {
392 #define _(n) SLEEP_##n##_US,
393   foreach_histogram_bucket
394 #undef _
395     SLEEP_N_BUCKETS,
396 } histogram_index_t;
397
398 static u64 vector_rate_histogram[SLEEP_N_BUCKETS];
399
400 static void memclnt_queue_callback (vlib_main_t * vm);
401
402 /*
403  * Callback to send ourselves a plugin numbering-space trace msg
404  */
405 static void
406 send_one_plugin_msg_ids_msg (u8 * name, u16 first_msg_id, u16 last_msg_id)
407 {
408   vl_api_trace_plugin_msg_ids_t *mp;
409   api_main_t *am = &api_main;
410   vl_shmem_hdr_t *shmem_hdr = am->shmem_hdr;
411   unix_shared_memory_queue_t *q;
412
413   mp = vl_msg_api_alloc_as_if_client (sizeof (*mp));
414   memset (mp, 0, sizeof (*mp));
415
416   mp->_vl_msg_id = clib_host_to_net_u16 (VL_API_TRACE_PLUGIN_MSG_IDS);
417   strncpy ((char *) mp->plugin_name, (char *) name,
418            sizeof (mp->plugin_name) - 1);
419   mp->first_msg_id = clib_host_to_net_u16 (first_msg_id);
420   mp->last_msg_id = clib_host_to_net_u16 (last_msg_id);
421
422   q = shmem_hdr->vl_input_queue;
423
424   vl_msg_api_send_shmem (q, (u8 *) & mp);
425 }
426
427 static uword
428 memclnt_process (vlib_main_t * vm,
429                  vlib_node_runtime_t * node, vlib_frame_t * f)
430 {
431   uword mp;
432   vl_shmem_hdr_t *shm;
433   unix_shared_memory_queue_t *q;
434   clib_error_t *e;
435   int rv;
436   api_main_t *am = &api_main;
437   f64 dead_client_scan_time;
438   f64 sleep_time, start_time;
439   f64 vector_rate;
440   int i;
441
442   vlib_set_queue_signal_callback (vm, memclnt_queue_callback);
443
444   if ((rv = memory_api_init (am->region_name)) < 0)
445     {
446       clib_warning ("memory_api_init returned %d, wait for godot...", rv);
447       vlib_process_suspend (vm, 1e70);
448     }
449
450   shm = am->shmem_hdr;
451   ASSERT (shm);
452   q = shm->vl_input_queue;
453   ASSERT (q);
454
455   e = vlib_call_init_exit_functions
456     (vm, vm->api_init_function_registrations, 1 /* call_once */ );
457   if (e)
458     clib_error_report (e);
459
460   sleep_time = 20.0;
461   dead_client_scan_time = vlib_time_now (vm) + 20.0;
462
463   /*
464    * Send plugin message range messages for each plugin we loaded
465    */
466   for (i = 0; i < vec_len (am->msg_ranges); i++)
467     {
468       vl_api_msg_range_t *rp = am->msg_ranges + i;
469       send_one_plugin_msg_ids_msg (rp->name, rp->first_msg_id,
470                                    rp->last_msg_id);
471     }
472
473   /* $$$ pay attention to frame size, control CPU usage */
474   while (1)
475     {
476       uword event_type __attribute__ ((unused));
477       i8 *headp;
478       int need_broadcast;
479
480       /*
481        * There's a reason for checking the queue before
482        * sleeping. If the vlib application crashes, it's entirely
483        * possible for a client to enqueue a connect request
484        * during the process restart interval.
485        *
486        * Unless some force of physics causes the new incarnation
487        * of the application to process the request, the client will
488        * sit and wait for Godot...
489        */
490       vector_rate = vlib_last_vector_length_per_node (vm);
491       start_time = vlib_time_now (vm);
492       while (1)
493         {
494           pthread_mutex_lock (&q->mutex);
495           if (q->cursize == 0)
496             {
497               vm->api_queue_nonempty = 0;
498               pthread_mutex_unlock (&q->mutex);
499
500               if (TRACE_VLIB_MEMORY_QUEUE)
501                 {
502                   /* *INDENT-OFF* */
503                   ELOG_TYPE_DECLARE (e) =
504                     {
505                       .format = "q-underflow: len %d",
506                       .format_args = "i4",
507                     };
508                   /* *INDENT-ON* */
509                   struct
510                   {
511                     u32 len;
512                   } *ed;
513                   ed = ELOG_DATA (&vm->elog_main, e);
514                   ed->len = 0;
515                 }
516               sleep_time = 20.0;
517               break;
518             }
519
520           headp = (i8 *) (q->data + sizeof (uword) * q->head);
521           clib_memcpy (&mp, headp, sizeof (uword));
522
523           q->head++;
524           need_broadcast = (q->cursize == q->maxsize / 2);
525           q->cursize--;
526
527           if (PREDICT_FALSE (q->head == q->maxsize))
528             q->head = 0;
529           pthread_mutex_unlock (&q->mutex);
530           if (need_broadcast)
531             (void) pthread_cond_broadcast (&q->condvar);
532
533           vl_msg_api_handler_with_vm_node (am, (void *) mp, vm, node);
534
535           /* Allow no more than 10us without a pause */
536           if (vlib_time_now (vm) > start_time + 10e-6)
537             {
538               int index = SLEEP_400_US;
539               if (vector_rate > 40.0)
540                 sleep_time = 400e-6;
541               else if (vector_rate > 20.0)
542                 {
543                   index = SLEEP_200_US;
544                   sleep_time = 200e-6;
545                 }
546               else if (vector_rate >= 1.0)
547                 {
548                   index = SLEEP_100_US;
549                   sleep_time = 100e-6;
550                 }
551               else
552                 {
553                   index = SLEEP_10_US;
554                   sleep_time = 10e-6;
555                 }
556               vector_rate_histogram[index] += 1;
557               break;
558             }
559         }
560
561       event_type = vlib_process_wait_for_event_or_clock (vm, sleep_time);
562       vm->queue_signal_pending = 0;
563       vlib_process_get_events (vm, 0 /* event_data */ );
564
565       if (vlib_time_now (vm) > dead_client_scan_time)
566         {
567           vl_api_registration_t **regpp;
568           vl_api_registration_t *regp;
569           unix_shared_memory_queue_t *q;
570           static u32 *dead_indices;
571           static u32 *confused_indices;
572
573           vec_reset_length (dead_indices);
574           vec_reset_length (confused_indices);
575
576           /* *INDENT-OFF* */
577           pool_foreach (regpp, am->vl_clients,
578           ({
579             regp = *regpp;
580             if (regp)
581               {
582                 q = regp->vl_input_queue;
583                 if (kill (q->consumer_pid, 0) < 0)
584                   {
585                     vec_add1(dead_indices, regpp - am->vl_clients);
586                   }
587               }
588             else
589               {
590                 clib_warning ("NULL client registration index %d",
591                               regpp - am->vl_clients);
592                 vec_add1 (confused_indices, regpp - am->vl_clients);
593               }
594           }));
595           /* *INDENT-ON* */
596           /* This should "never happen," but if it does, fix it... */
597           if (PREDICT_FALSE (vec_len (confused_indices) > 0))
598             {
599               int i;
600               for (i = 0; i < vec_len (confused_indices); i++)
601                 {
602                   pool_put_index (am->vl_clients, confused_indices[i]);
603                 }
604             }
605
606           if (PREDICT_FALSE (vec_len (dead_indices) > 0))
607             {
608               int i;
609               svm_region_t *svm;
610               void *oldheap;
611
612               /* Allow the application to clean up its registrations */
613               for (i = 0; i < vec_len (dead_indices); i++)
614                 {
615                   regpp = pool_elt_at_index (am->vl_clients, dead_indices[i]);
616                   if (regpp)
617                     {
618                       u32 handle;
619
620                       handle = vl_msg_api_handle_from_index_and_epoch
621                         (dead_indices[i], shm->application_restarts);
622                       (void) vl_api_memclnt_delete_callback (handle);
623                     }
624                 }
625
626               svm = am->vlib_rp;
627               pthread_mutex_lock (&svm->mutex);
628               oldheap = svm_push_data_heap (svm);
629
630               for (i = 0; i < vec_len (dead_indices); i++)
631                 {
632                   regpp = pool_elt_at_index (am->vl_clients, dead_indices[i]);
633                   if (regpp)
634                     {
635                       /* Poison the old registration */
636                       memset (*regpp, 0xF3, sizeof (**regpp));
637                       clib_mem_free (*regpp);
638                       /* no dangling references, please */
639                       *regpp = 0;
640                     }
641                   else
642                     {
643                       svm_pop_heap (oldheap);
644                       clib_warning ("Duplicate free, client index %d",
645                                     regpp - am->vl_clients);
646                       oldheap = svm_push_data_heap (svm);
647                     }
648                 }
649
650               svm_client_scan_this_region_nolock (am->vlib_rp);
651
652               pthread_mutex_unlock (&svm->mutex);
653               svm_pop_heap (oldheap);
654               for (i = 0; i < vec_len (dead_indices); i++)
655                 pool_put_index (am->vl_clients, dead_indices[i]);
656             }
657
658           dead_client_scan_time = vlib_time_now (vm) + 20.0;
659         }
660
661       if (TRACE_VLIB_MEMORY_QUEUE)
662         {
663           /* *INDENT-OFF* */
664           ELOG_TYPE_DECLARE (e) = {
665             .format = "q-awake: len %d",
666             .format_args = "i4",
667           };
668           /* *INDENT-ON* */
669           struct
670           {
671             u32 len;
672           } *ed;
673           ed = ELOG_DATA (&vm->elog_main, e);
674           ed->len = q->cursize;
675         }
676     }
677
678   return 0;
679 }
680
681 static clib_error_t *
682 vl_api_show_histogram_command (vlib_main_t * vm,
683                                unformat_input_t * input,
684                                vlib_cli_command_t * cli_cmd)
685 {
686   u64 total_counts = 0;
687   int i;
688
689   for (i = 0; i < SLEEP_N_BUCKETS; i++)
690     {
691       total_counts += vector_rate_histogram[i];
692     }
693
694   if (total_counts == 0)
695     {
696       vlib_cli_output (vm, "No control-plane activity.");
697       return 0;
698     }
699
700 #define _(n)                                                    \
701     do {                                                        \
702         f64 percent;                                            \
703         percent = ((f64) vector_rate_histogram[SLEEP_##n##_US]) \
704             / (f64) total_counts;                               \
705         percent *= 100.0;                                       \
706         vlib_cli_output (vm, "Sleep %3d us: %llu, %.2f%%",n,    \
707                          vector_rate_histogram[SLEEP_##n##_US], \
708                          percent);                              \
709     } while (0);
710   foreach_histogram_bucket;
711 #undef _
712
713   return 0;
714 }
715
716 /* *INDENT-OFF* */
717 VLIB_CLI_COMMAND (cli_show_api_histogram_command, static) = {
718     .path = "show api histogram",
719     .short_help = "show api histogram",
720     .function = vl_api_show_histogram_command,
721 };
722 /* *INDENT-ON* */
723
724 static clib_error_t *
725 vl_api_clear_histogram_command (vlib_main_t * vm,
726                                 unformat_input_t * input,
727                                 vlib_cli_command_t * cli_cmd)
728 {
729   int i;
730
731   for (i = 0; i < SLEEP_N_BUCKETS; i++)
732     vector_rate_histogram[i] = 0;
733   return 0;
734 }
735
736 /* *INDENT-OFF* */
737 VLIB_CLI_COMMAND (cli_clear_api_histogram_command, static) = {
738     .path = "clear api histogram",
739     .short_help = "clear api histogram",
740     .function = vl_api_clear_histogram_command,
741 };
742 /* *INDENT-ON* */
743
744
745 /* *INDENT-OFF* */
746 VLIB_REGISTER_NODE (memclnt_node,static) = {
747     .function = memclnt_process,
748     .type = VLIB_NODE_TYPE_PROCESS,
749     .name = "api-rx-from-ring",
750     .state = VLIB_NODE_STATE_DISABLED,
751 };
752 /* *INDENT-ON* */
753
754 static void
755 memclnt_queue_callback (vlib_main_t * vm)
756 {
757   static volatile int *cursizep;
758
759   if (PREDICT_FALSE (cursizep == 0))
760     {
761       api_main_t *am = &api_main;
762       vl_shmem_hdr_t *shmem_hdr = am->shmem_hdr;
763       unix_shared_memory_queue_t *q;
764
765       if (shmem_hdr == 0)
766         return;
767
768       q = shmem_hdr->vl_input_queue;
769       if (q == 0)
770         return;
771       cursizep = &q->cursize;
772     }
773
774   if (*cursizep >= 1)
775     {
776       vm->queue_signal_pending = 1;
777       vm->api_queue_nonempty = 1;
778       vlib_process_signal_event (vm, memclnt_node.index,
779                                  /* event_type */ 0, /* event_data */ 0);
780     }
781 }
782
783 void
784 vl_enable_disable_memory_api (vlib_main_t * vm, int enable)
785 {
786   vlib_node_set_state (vm, memclnt_node.index,
787                        (enable
788                         ? VLIB_NODE_STATE_POLLING
789                         : VLIB_NODE_STATE_DISABLED));
790 }
791
792 static uword
793 api_rx_from_node (vlib_main_t * vm,
794                   vlib_node_runtime_t * node, vlib_frame_t * frame)
795 {
796   uword n_packets = frame->n_vectors;
797   uword n_left_from;
798   u32 *from;
799   static u8 *long_msg;
800
801   vec_validate (long_msg, 4095);
802   n_left_from = frame->n_vectors;
803   from = vlib_frame_args (frame);
804
805   while (n_left_from > 0)
806     {
807       u32 bi0;
808       vlib_buffer_t *b0;
809       void *msg;
810       uword msg_len;
811
812       bi0 = from[0];
813       b0 = vlib_get_buffer (vm, bi0);
814       from += 1;
815       n_left_from -= 1;
816
817       msg = b0->data + b0->current_data;
818       msg_len = b0->current_length;
819       if (b0->flags & VLIB_BUFFER_NEXT_PRESENT)
820         {
821           ASSERT (long_msg != 0);
822           _vec_len (long_msg) = 0;
823           vec_add (long_msg, msg, msg_len);
824           while (b0->flags & VLIB_BUFFER_NEXT_PRESENT)
825             {
826               b0 = vlib_get_buffer (vm, b0->next_buffer);
827               msg = b0->data + b0->current_data;
828               msg_len = b0->current_length;
829               vec_add (long_msg, msg, msg_len);
830             }
831           msg = long_msg;
832         }
833       vl_msg_api_handler_no_trace_no_free (msg);
834     }
835
836   /* Free what we've been given. */
837   vlib_buffer_free (vm, vlib_frame_args (frame), n_packets);
838
839   return n_packets;
840 }
841
842 /* *INDENT-OFF* */
843 VLIB_REGISTER_NODE (api_rx_from_node_node,static) = {
844     .function = api_rx_from_node,
845     .type = VLIB_NODE_TYPE_INTERNAL,
846     .vector_size = 4,
847     .name = "api-rx-from-node",
848 };
849 /* *INDENT-ON* */
850
851 static clib_error_t *
852 setup_memclnt_exit (vlib_main_t * vm)
853 {
854   atexit (vl_unmap_shmem);
855   return 0;
856 }
857
858 VLIB_INIT_FUNCTION (setup_memclnt_exit);
859
860
861 static clib_error_t *
862 vl_api_ring_command (vlib_main_t * vm,
863                      unformat_input_t * input, vlib_cli_command_t * cli_cmd)
864 {
865   int i;
866   ring_alloc_t *ap;
867   vl_shmem_hdr_t *shmem_hdr;
868   api_main_t *am = &api_main;
869
870   shmem_hdr = am->shmem_hdr;
871
872   if (shmem_hdr == 0)
873     {
874       vlib_cli_output (vm, "Shared memory segment not initialized...\n");
875       return 0;
876     }
877
878   vlib_cli_output (vm, "%8s %8s %8s %8s %8s\n",
879                    "Owner", "Size", "Nitems", "Hits", "Misses");
880
881   ap = shmem_hdr->vl_rings;
882
883   for (i = 0; i < vec_len (shmem_hdr->vl_rings); i++)
884     {
885       vlib_cli_output (vm, "%8s %8d %8d %8d %8d\n",
886                        "vlib", ap->size, ap->nitems, ap->hits, ap->misses);
887       ap++;
888     }
889
890   ap = shmem_hdr->client_rings;
891
892   for (i = 0; i < vec_len (shmem_hdr->client_rings); i++)
893     {
894       vlib_cli_output (vm, "%8s %8d %8d %8d %8d\n",
895                        "clnt", ap->size, ap->nitems, ap->hits, ap->misses);
896       ap++;
897     }
898
899   vlib_cli_output (vm, "%d ring miss fallback allocations\n",
900                    am->ring_misses);
901
902   vlib_cli_output
903     (vm, "%d application restarts, %d reclaimed msgs, %d garbage collects\n",
904      shmem_hdr->application_restarts,
905      shmem_hdr->restart_reclaims, shmem_hdr->garbage_collects);
906   return 0;
907 }
908
909 void dump_socket_clients (vlib_main_t * vm, api_main_t * am)
910   __attribute__ ((weak));
911
912 void
913 dump_socket_clients (vlib_main_t * vm, api_main_t * am)
914 {
915 }
916
917 static clib_error_t *
918 vl_api_client_command (vlib_main_t * vm,
919                        unformat_input_t * input, vlib_cli_command_t * cli_cmd)
920 {
921   vl_api_registration_t **regpp, *regp;
922   unix_shared_memory_queue_t *q;
923   char *health;
924   api_main_t *am = &api_main;
925   u32 *confused_indices = 0;
926
927   if (!pool_elts (am->vl_clients))
928     goto socket_clients;
929   vlib_cli_output (vm, "Shared memory clients");
930   vlib_cli_output (vm, "%16s %8s %14s %18s %s",
931                    "Name", "PID", "Queue Length", "Queue VA", "Health");
932
933   /* *INDENT-OFF* */
934   pool_foreach (regpp, am->vl_clients,
935   ({
936     regp = *regpp;
937
938     if (regp)
939       {
940         q = regp->vl_input_queue;
941         if (kill (q->consumer_pid, 0) < 0)
942           {
943             health = "DEAD";
944           }
945         else
946           {
947             health = "alive";
948           }
949         vlib_cli_output (vm, "%16s %8d %14d 0x%016llx %s\n",
950                          regp->name, q->consumer_pid, q->cursize,
951                          q, health);
952       }
953     else
954       {
955         clib_warning ("NULL client registration index %d",
956                       regpp - am->vl_clients);
957         vec_add1 (confused_indices, regpp - am->vl_clients);
958       }
959   }));
960   /* *INDENT-ON* */
961
962   /* This should "never happen," but if it does, fix it... */
963   if (PREDICT_FALSE (vec_len (confused_indices) > 0))
964     {
965       int i;
966       for (i = 0; i < vec_len (confused_indices); i++)
967         {
968           pool_put_index (am->vl_clients, confused_indices[i]);
969         }
970     }
971   vec_free (confused_indices);
972
973   if (am->missing_clients)
974     vlib_cli_output (vm, "%u messages with missing clients",
975                      am->missing_clients);
976 socket_clients:
977   dump_socket_clients (vm, am);
978
979   return 0;
980 }
981
982 static clib_error_t *
983 vl_api_status_command (vlib_main_t * vm,
984                        unformat_input_t * input, vlib_cli_command_t * cli_cmd)
985 {
986   api_main_t *am = &api_main;
987
988   // check if rx_trace and tx_trace are not null pointers
989
990   if (am->rx_trace == 0)
991     {
992       vlib_cli_output (vm, "RX Trace disabled\n");
993     }
994   else
995     {
996       if (am->rx_trace->enabled == 0)
997         vlib_cli_output (vm, "RX Trace disabled\n");
998       else
999         vlib_cli_output (vm, "RX Trace enabled\n");
1000     }
1001
1002   if (am->tx_trace == 0)
1003     {
1004       vlib_cli_output (vm, "TX Trace disabled\n");
1005     }
1006   else
1007     {
1008       if (am->tx_trace->enabled == 0)
1009         vlib_cli_output (vm, "TX Trace disabled\n");
1010       else
1011         vlib_cli_output (vm, "TX Trace enabled\n");
1012     }
1013
1014   return 0;
1015 }
1016
1017 /* *INDENT-OFF* */
1018 VLIB_CLI_COMMAND (cli_show_api_command, static) = {
1019     .path = "show api",
1020     .short_help = "Show API information",
1021 };
1022 /* *INDENT-ON* */
1023
1024 /* *INDENT-OFF* */
1025 VLIB_CLI_COMMAND (cli_show_api_ring_command, static) = {
1026     .path = "show api ring-stats",
1027     .short_help = "Message ring statistics",
1028     .function = vl_api_ring_command,
1029 };
1030 /* *INDENT-ON* */
1031
1032 /* *INDENT-OFF* */
1033 VLIB_CLI_COMMAND (cli_show_api_clients_command, static) = {
1034     .path = "show api clients",
1035     .short_help = "Client information",
1036     .function = vl_api_client_command,
1037 };
1038 /* *INDENT-ON* */
1039
1040 /* *INDENT-OFF* */
1041 VLIB_CLI_COMMAND (cli_show_api_status_command, static) = {
1042     .path = "show api status",
1043     .short_help = "Show API trace status",
1044     .function = vl_api_status_command,
1045 };
1046 /* *INDENT-ON* */
1047
1048 static clib_error_t *
1049 vl_api_message_table_command (vlib_main_t * vm,
1050                               unformat_input_t * input,
1051                               vlib_cli_command_t * cli_cmd)
1052 {
1053   api_main_t *am = &api_main;
1054   int i;
1055   int verbose = 0;
1056
1057   if (unformat (input, "verbose"))
1058     verbose = 1;
1059
1060
1061   if (verbose == 0)
1062     vlib_cli_output (vm, "%-4s %s", "ID", "Name");
1063   else
1064     vlib_cli_output (vm, "%-4s %-40s %6s %7s", "ID", "Name", "Bounce",
1065                      "MP-safe");
1066
1067   for (i = 1; i < vec_len (am->msg_names); i++)
1068     {
1069       if (verbose == 0)
1070         {
1071           vlib_cli_output (vm, "%-4d %s", i,
1072                            am->msg_names[i] ? am->msg_names[i] :
1073                            "  [no handler]");
1074         }
1075       else
1076         {
1077           vlib_cli_output (vm, "%-4d %-40s %6d %7d", i,
1078                            am->msg_names[i] ? am->msg_names[i] :
1079                            "  [no handler]", am->message_bounce[i],
1080                            am->is_mp_safe[i]);
1081         }
1082     }
1083
1084   return 0;
1085 }
1086
1087 /* *INDENT-OFF* */
1088 VLIB_CLI_COMMAND (cli_show_api_message_table_command, static) = {
1089     .path = "show api message-table",
1090     .short_help = "Message Table",
1091     .function = vl_api_message_table_command,
1092 };
1093 /* *INDENT-ON* */
1094
1095 static clib_error_t *
1096 vl_api_trace_command (vlib_main_t * vm,
1097                       unformat_input_t * input, vlib_cli_command_t * cli_cmd)
1098 {
1099   u32 nitems = 1024;
1100   vl_api_trace_which_t which = VL_API_TRACE_RX;
1101   api_main_t *am = &api_main;
1102
1103   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1104     {
1105       if (unformat (input, "rx nitems %u", &nitems) || unformat (input, "rx"))
1106         goto configure;
1107       else if (unformat (input, "tx nitems %u", &nitems)
1108                || unformat (input, "tx"))
1109         {
1110           which = VL_API_TRACE_RX;
1111           goto configure;
1112         }
1113       else if (unformat (input, "on rx"))
1114         {
1115           vl_msg_api_trace_onoff (am, VL_API_TRACE_RX, 1);
1116         }
1117       else if (unformat (input, "on tx"))
1118         {
1119           vl_msg_api_trace_onoff (am, VL_API_TRACE_TX, 1);
1120         }
1121       else if (unformat (input, "on"))
1122         {
1123           vl_msg_api_trace_onoff (am, VL_API_TRACE_RX, 1);
1124         }
1125       else if (unformat (input, "off"))
1126         {
1127           vl_msg_api_trace_onoff (am, VL_API_TRACE_RX, 0);
1128           vl_msg_api_trace_onoff (am, VL_API_TRACE_TX, 0);
1129         }
1130       else if (unformat (input, "free"))
1131         {
1132           vl_msg_api_trace_onoff (am, VL_API_TRACE_RX, 0);
1133           vl_msg_api_trace_onoff (am, VL_API_TRACE_TX, 0);
1134           vl_msg_api_trace_free (am, VL_API_TRACE_RX);
1135           vl_msg_api_trace_free (am, VL_API_TRACE_TX);
1136         }
1137       else if (unformat (input, "debug on"))
1138         {
1139           am->msg_print_flag = 1;
1140         }
1141       else if (unformat (input, "debug off"))
1142         {
1143           am->msg_print_flag = 0;
1144         }
1145       else
1146         return clib_error_return (0, "unknown input `%U'",
1147                                   format_unformat_error, input);
1148     }
1149   return 0;
1150
1151 configure:
1152   if (vl_msg_api_trace_configure (am, which, nitems))
1153     {
1154       vlib_cli_output (vm, "warning: trace configure error (%d, %d)",
1155                        which, nitems);
1156     }
1157
1158   return 0;
1159 }
1160
1161 /* *INDENT-OFF* */
1162 VLIB_CLI_COMMAND (trace, static) = {
1163     .path = "set api-trace",
1164     .short_help = "API trace",
1165     .function = vl_api_trace_command,
1166 };
1167 /* *INDENT-ON* */
1168
1169 clib_error_t *
1170 vlibmemory_init (vlib_main_t * vm)
1171 {
1172   api_main_t *am = &api_main;
1173   svm_map_region_args_t _a, *a = &_a;
1174
1175   memset (a, 0, sizeof (*a));
1176   a->root_path = am->root_path;
1177   a->name = SVM_GLOBAL_REGION_NAME;
1178   a->baseva = (am->global_baseva != 0) ?
1179     am->global_baseva : SVM_GLOBAL_REGION_BASEVA;
1180   a->size = (am->global_size != 0) ? am->global_size : SVM_GLOBAL_REGION_SIZE;
1181   a->flags = SVM_FLAGS_NODATA;
1182   a->uid = am->api_uid;
1183   a->gid = am->api_gid;
1184   a->pvt_heap_size =
1185     (am->global_pvt_heap_size !=
1186      0) ? am->global_pvt_heap_size : SVM_PVT_MHEAP_SIZE;
1187
1188   svm_region_init_args (a);
1189   return 0;
1190 }
1191
1192 VLIB_INIT_FUNCTION (vlibmemory_init);
1193
1194 void
1195 vl_set_memory_region_name (char *name)
1196 {
1197   api_main_t *am = &api_main;
1198
1199   am->region_name = name;
1200 }
1201
1202 static int
1203 range_compare (vl_api_msg_range_t * a0, vl_api_msg_range_t * a1)
1204 {
1205   int len0, len1, clen;
1206
1207   len0 = vec_len (a0->name);
1208   len1 = vec_len (a1->name);
1209   clen = len0 < len1 ? len0 : len1;
1210   return (strncmp ((char *) a0->name, (char *) a1->name, clen));
1211 }
1212
1213 static u8 *
1214 format_api_msg_range (u8 * s, va_list * args)
1215 {
1216   vl_api_msg_range_t *rp = va_arg (*args, vl_api_msg_range_t *);
1217
1218   if (rp == 0)
1219     s = format (s, "%-20s%9s%9s", "Name", "First-ID", "Last-ID");
1220   else
1221     s = format (s, "%-20s%9d%9d", rp->name, rp->first_msg_id,
1222                 rp->last_msg_id);
1223
1224   return s;
1225 }
1226
1227 static clib_error_t *
1228 vl_api_show_plugin_command (vlib_main_t * vm,
1229                             unformat_input_t * input,
1230                             vlib_cli_command_t * cli_cmd)
1231 {
1232   api_main_t *am = &api_main;
1233   vl_api_msg_range_t *rp = 0;
1234   int i;
1235
1236   if (vec_len (am->msg_ranges) == 0)
1237     {
1238       vlib_cli_output (vm, "No plugin API message ranges configured...");
1239       return 0;
1240     }
1241
1242   rp = vec_dup (am->msg_ranges);
1243
1244   vec_sort_with_function (rp, range_compare);
1245
1246   vlib_cli_output (vm, "Plugin API message ID ranges...\n");
1247   vlib_cli_output (vm, "%U", format_api_msg_range, 0 /* header */ );
1248
1249   for (i = 0; i < vec_len (rp); i++)
1250     vlib_cli_output (vm, "%U", format_api_msg_range, rp + i);
1251
1252   vec_free (rp);
1253
1254   return 0;
1255 }
1256
1257 /* *INDENT-OFF* */
1258 VLIB_CLI_COMMAND (cli_show_api_plugin_command, static) = {
1259     .path = "show api plugin",
1260     .short_help = "show api plugin",
1261     .function = vl_api_show_plugin_command,
1262 };
1263 /* *INDENT-ON* */
1264
1265 static void
1266 vl_api_rpc_call_t_handler (vl_api_rpc_call_t * mp)
1267 {
1268   vl_api_rpc_reply_t *rmp;
1269   int (*fp) (void *);
1270   i32 rv = 0;
1271   vlib_main_t *vm = vlib_get_main ();
1272
1273   if (mp->function == 0)
1274     {
1275       rv = -1;
1276       clib_warning ("rpc NULL function pointer");
1277     }
1278
1279   else
1280     {
1281       if (mp->need_barrier_sync)
1282         vlib_worker_thread_barrier_sync (vm);
1283
1284       fp = uword_to_pointer (mp->function, int (*)(void *));
1285       rv = fp (mp->data);
1286
1287       if (mp->need_barrier_sync)
1288         vlib_worker_thread_barrier_release (vm);
1289     }
1290
1291   if (mp->send_reply)
1292     {
1293       unix_shared_memory_queue_t *q =
1294         vl_api_client_index_to_input_queue (mp->client_index);
1295       if (q)
1296         {
1297           rmp = vl_msg_api_alloc_as_if_client (sizeof (*rmp));
1298           rmp->_vl_msg_id = ntohs (VL_API_RPC_REPLY);
1299           rmp->context = mp->context;
1300           rmp->retval = rv;
1301           vl_msg_api_send_shmem (q, (u8 *) & rmp);
1302         }
1303     }
1304   if (mp->multicast)
1305     {
1306       clib_warning ("multicast not yet implemented...");
1307     }
1308 }
1309
1310 static void
1311 vl_api_rpc_reply_t_handler (vl_api_rpc_reply_t * mp)
1312 {
1313   clib_warning ("unimplemented");
1314 }
1315
1316 void
1317 vl_api_rpc_call_main_thread (void *fp, u8 * data, u32 data_length)
1318 {
1319   vl_api_rpc_call_t *mp;
1320   api_main_t *am = &api_main;
1321   vl_shmem_hdr_t *shmem_hdr = am->shmem_hdr;
1322   unix_shared_memory_queue_t *q;
1323
1324   /* Main thread: call the function directly */
1325   if (os_get_cpu_number () == 0)
1326     {
1327       vlib_main_t *vm = vlib_get_main ();
1328       void (*call_fp) (void *);
1329
1330       vlib_worker_thread_barrier_sync (vm);
1331
1332       call_fp = fp;
1333       call_fp (data);
1334
1335       vlib_worker_thread_barrier_release (vm);
1336       return;
1337     }
1338
1339   /* Any other thread, actually do an RPC call... */
1340   mp = vl_msg_api_alloc_as_if_client (sizeof (*mp) + data_length);
1341
1342   memset (mp, 0, sizeof (*mp));
1343   clib_memcpy (mp->data, data, data_length);
1344   mp->_vl_msg_id = ntohs (VL_API_RPC_CALL);
1345   mp->function = pointer_to_uword (fp);
1346   mp->need_barrier_sync = 1;
1347
1348   /*
1349    * Use the "normal" control-plane mechanism for the main thread.
1350    * Well, almost. if the main input queue is full, we cannot
1351    * block. Otherwise, we can expect a barrier sync timeout.
1352    */
1353   q = shmem_hdr->vl_input_queue;
1354
1355   while (pthread_mutex_trylock (&q->mutex))
1356     vlib_worker_thread_barrier_check ();
1357
1358   while (PREDICT_FALSE (unix_shared_memory_queue_is_full (q)))
1359     {
1360       pthread_mutex_unlock (&q->mutex);
1361       vlib_worker_thread_barrier_check ();
1362       while (pthread_mutex_trylock (&q->mutex))
1363         vlib_worker_thread_barrier_check ();
1364     }
1365
1366   vl_msg_api_send_shmem_nolock (q, (u8 *) & mp);
1367
1368   pthread_mutex_unlock (&q->mutex);
1369 }
1370
1371 static void
1372 vl_api_trace_plugin_msg_ids_t_handler (vl_api_trace_plugin_msg_ids_t * mp)
1373 {
1374   api_main_t *am = &api_main;
1375   vl_api_msg_range_t *rp;
1376   uword *p;
1377
1378   /* Noop (except for tracing) during normal operation */
1379   if (am->replay_in_progress == 0)
1380     return;
1381
1382   p = hash_get_mem (am->msg_range_by_name, mp->plugin_name);
1383   if (p == 0)
1384     {
1385       clib_warning ("WARNING: traced plugin '%s' not in current image",
1386                     mp->plugin_name);
1387       return;
1388     }
1389
1390   rp = vec_elt_at_index (am->msg_ranges, p[0]);
1391   if (rp->first_msg_id != clib_net_to_host_u16 (mp->first_msg_id))
1392     {
1393       clib_warning ("WARNING: traced plugin '%s' first message id %d not %d",
1394                     mp->plugin_name, clib_net_to_host_u16 (mp->first_msg_id),
1395                     rp->first_msg_id);
1396     }
1397
1398   if (rp->last_msg_id != clib_net_to_host_u16 (mp->last_msg_id))
1399     {
1400       clib_warning ("WARNING: traced plugin '%s' last message id %d not %d",
1401                     mp->plugin_name, clib_net_to_host_u16 (mp->last_msg_id),
1402                     rp->last_msg_id);
1403     }
1404 }
1405
1406 #define foreach_rpc_api_msg                     \
1407 _(RPC_CALL,rpc_call)                            \
1408 _(RPC_REPLY,rpc_reply)
1409
1410 #define foreach_plugin_trace_msg                \
1411 _(TRACE_PLUGIN_MSG_IDS,trace_plugin_msg_ids)
1412
1413 static clib_error_t *
1414 rpc_api_hookup (vlib_main_t * vm)
1415 {
1416 #define _(N,n)                                                  \
1417     vl_msg_api_set_handlers(VL_API_##N, #n,                     \
1418                            vl_api_##n##_t_handler,              \
1419                            vl_noop_handler,                     \
1420                            vl_noop_handler,                     \
1421                            vl_api_##n##_t_print,                \
1422                            sizeof(vl_api_##n##_t), 0 /* do not trace */);
1423   foreach_rpc_api_msg;
1424 #undef _
1425
1426 #define _(N,n)                                                  \
1427     vl_msg_api_set_handlers(VL_API_##N, #n,                     \
1428                            vl_api_##n##_t_handler,              \
1429                            vl_noop_handler,                     \
1430                            vl_noop_handler,                     \
1431                            vl_api_##n##_t_print,                \
1432                            sizeof(vl_api_##n##_t), 1 /* do trace */);
1433   foreach_plugin_trace_msg;
1434 #undef _
1435   return 0;
1436 }
1437
1438 VLIB_API_INIT_FUNCTION (rpc_api_hookup);
1439
1440 /*
1441  * fd.io coding-style-patch-verification: ON
1442  *
1443  * Local Variables:
1444  * eval: (c-set-style "gnu")
1445  * End:
1446  */