sock api: add infra for bootstrapping shm clients
[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 <sys/stat.h>
26 #include <fcntl.h>
27 #include <signal.h>
28 #include <pthread.h>
29 #include <vppinfra/vec.h>
30 #include <vppinfra/hash.h>
31 #include <vppinfra/pool.h>
32 #include <vppinfra/format.h>
33 #include <vppinfra/byte_order.h>
34 #include <vppinfra/elog.h>
35 #include <stdarg.h>
36 #include <vlib/vlib.h>
37 #include <vlib/unix/unix.h>
38 #include <vlibapi/api.h>
39 #include <vlibmemory/api.h>
40
41 /**
42  * @file
43  * @brief Binary API messaging via shared memory
44  * Low-level, primary provisioning interface
45  */
46 /*? %%clicmd:group_label Binary API CLI %% ?*/
47 /*? %%syscfg:group_label Binary API configuration %% ?*/
48
49 #define TRACE_VLIB_MEMORY_QUEUE 0
50
51 #include <vlibmemory/vl_memory_msg_enum.h>      /* enumerate all vlib messages */
52
53 #define vl_typedefs             /* define message structures */
54 #include <vlibmemory/vl_memory_api_h.h>
55 #undef vl_typedefs
56
57 /* instantiate all the print functions we know about */
58 #define vl_print(handle, ...) vlib_cli_output (handle, __VA_ARGS__)
59 #define vl_printfun
60 #include <vlibmemory/vl_memory_api_h.h>
61 #undef vl_printfun
62
63 static inline void *
64 vl_api_memclnt_create_t_print (vl_api_memclnt_create_t * a, void *handle)
65 {
66   vl_print (handle, "vl_api_memclnt_create_t:\n");
67   vl_print (handle, "name: %s\n", a->name);
68   vl_print (handle, "input_queue: 0x%wx\n", a->input_queue);
69   vl_print (handle, "context: %u\n", (unsigned) a->context);
70   vl_print (handle, "ctx_quota: %ld\n", (long) a->ctx_quota);
71   return handle;
72 }
73
74 static inline void *
75 vl_api_memclnt_delete_t_print (vl_api_memclnt_delete_t * a, void *handle)
76 {
77   vl_print (handle, "vl_api_memclnt_delete_t:\n");
78   vl_print (handle, "index: %u\n", (unsigned) a->index);
79   vl_print (handle, "handle: 0x%wx\n", a->handle);
80   return handle;
81 }
82
83 static inline void *
84 vl_api_trace_plugin_msg_ids_t_print (vl_api_trace_plugin_msg_ids_t * a,
85                                      void *handle)
86 {
87   vl_print (handle, "vl_api_trace_plugin_msg_ids: %s first %u last %u\n",
88             a->plugin_name,
89             clib_host_to_net_u16 (a->first_msg_id),
90             clib_host_to_net_u16 (a->last_msg_id));
91   return handle;
92 }
93
94 /* instantiate all the endian swap functions we know about */
95 #define vl_endianfun
96 #include <vlibmemory/vl_memory_api_h.h>
97 #undef vl_endianfun
98
99 extern void vl_socket_api_send (vl_api_registration_t * rp, u8 * elem);
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, (u8 *) & 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_internal
139  */
140
141 u32
142 vl_api_memclnt_create_internal (char *name, unix_shared_memory_queue_t * q)
143 {
144   vl_api_registration_t **regpp;
145   vl_api_registration_t *regp;
146   svm_region_t *svm;
147   void *oldheap;
148   api_main_t *am = &api_main;
149
150   ASSERT (vlib_get_thread_index () == 0);
151   pool_get (am->vl_clients, regpp);
152
153   svm = am->vlib_rp;
154
155   pthread_mutex_lock (&svm->mutex);
156   oldheap = svm_push_data_heap (svm);
157   *regpp = clib_mem_alloc (sizeof (vl_api_registration_t));
158
159   regp = *regpp;
160   memset (regp, 0, sizeof (*regp));
161   regp->registration_type = REGISTRATION_TYPE_SHMEM;
162   regp->vl_api_registration_pool_index = regpp - am->vl_clients;
163   regp->vlib_rp = svm;
164   regp->shmem_hdr = am->shmem_hdr;
165
166   regp->vl_input_queue = q;
167   regp->name = format (0, "%s%c", name, 0);
168
169   pthread_mutex_unlock (&svm->mutex);
170   svm_pop_heap (oldheap);
171   return vl_msg_api_handle_from_index_and_epoch
172     (regp->vl_api_registration_pool_index,
173      am->shmem_hdr->application_restarts);
174 }
175
176
177 /*
178  * vl_api_memclnt_create_t_handler
179  */
180 void
181 vl_api_memclnt_create_t_handler (vl_api_memclnt_create_t * mp)
182 {
183   vl_api_registration_t **regpp;
184   vl_api_registration_t *regp;
185   vl_api_memclnt_create_reply_t *rp;
186   svm_region_t *svm;
187   unix_shared_memory_queue_t *q;
188   int rv = 0;
189   void *oldheap;
190   api_main_t *am = &api_main;
191
192   /*
193    * This is tortured. Maintain a vlib-address-space private
194    * pool of client registrations. We use the shared-memory virtual
195    * address of client structure as a handle, to allow direct
196    * manipulation of context quota vbls from the client library.
197    *
198    * This scheme causes trouble w/ API message trace replay, since
199    * some random VA from clib_mem_alloc() certainly won't
200    * occur in the Linux sim. The (very) few places
201    * that care need to use the pool index.
202    *
203    * Putting the registration object(s) into a pool in shared memory and
204    * using the pool index as a handle seems like a great idea.
205    * Unfortunately, each and every reference to that pool would need
206    * to be protected by a mutex:
207    *
208    *     Client                      VLIB
209    *     ------                      ----
210    *     convert pool index to
211    *     pointer.
212    *     <deschedule>
213    *                                 expand pool
214    *                                 <deschedule>
215    *     kaboom!
216    */
217
218   pool_get (am->vl_clients, regpp);
219
220   svm = am->vlib_rp;
221
222   pthread_mutex_lock (&svm->mutex);
223   oldheap = svm_push_data_heap (svm);
224   *regpp = clib_mem_alloc (sizeof (vl_api_registration_t));
225
226   regp = *regpp;
227   memset (regp, 0, sizeof (*regp));
228   regp->registration_type = REGISTRATION_TYPE_SHMEM;
229   regp->vl_api_registration_pool_index = regpp - am->vl_clients;
230   regp->vlib_rp = svm;
231   regp->shmem_hdr = am->shmem_hdr;
232
233   q = regp->vl_input_queue = (unix_shared_memory_queue_t *) (uword)
234     mp->input_queue;
235
236   regp->name = format (0, "%s", mp->name);
237   vec_add1 (regp->name, 0);
238
239   if (am->serialized_message_table_in_shmem == 0)
240     am->serialized_message_table_in_shmem =
241       vl_api_serialize_message_table (am, 0);
242
243   pthread_mutex_unlock (&svm->mutex);
244   svm_pop_heap (oldheap);
245
246   rp = vl_msg_api_alloc (sizeof (*rp));
247   rp->_vl_msg_id = ntohs (VL_API_MEMCLNT_CREATE_REPLY);
248   rp->handle = (uword) regp;
249   rp->index = vl_msg_api_handle_from_index_and_epoch
250     (regp->vl_api_registration_pool_index,
251      am->shmem_hdr->application_restarts);
252   rp->context = mp->context;
253   rp->response = ntohl (rv);
254   rp->message_table =
255     pointer_to_uword (am->serialized_message_table_in_shmem);
256
257   vl_msg_api_send_shmem (q, (u8 *) & rp);
258 }
259
260 static int
261 call_reaper_functions (u32 client_index)
262 {
263   clib_error_t *error = 0;
264   _vl_msg_api_function_list_elt_t *i;
265
266   i = api_main.reaper_function_registrations;
267   while (i)
268     {
269       error = i->f (client_index);
270       if (error)
271         clib_error_report (error);
272       i = i->next_init_function;
273     }
274   return 0;
275 }
276
277 /*
278  * vl_api_memclnt_delete_t_handler
279  */
280 void
281 vl_api_memclnt_delete_t_handler (vl_api_memclnt_delete_t * mp)
282 {
283   vl_api_registration_t **regpp;
284   vl_api_registration_t *regp;
285   vl_api_memclnt_delete_reply_t *rp;
286   svm_region_t *svm;
287   void *oldheap;
288   api_main_t *am = &api_main;
289   u32 handle, client_index, epoch;
290
291   handle = mp->index;
292
293   if (call_reaper_functions (handle))
294     return;
295
296   epoch = vl_msg_api_handle_get_epoch (handle);
297   client_index = vl_msg_api_handle_get_index (handle);
298
299   if (epoch != (am->shmem_hdr->application_restarts & VL_API_EPOCH_MASK))
300     {
301       clib_warning
302         ("Stale clnt delete index %d old epoch %d cur epoch %d",
303          client_index, epoch,
304          (am->shmem_hdr->application_restarts & VL_API_EPOCH_MASK));
305       return;
306     }
307
308   regpp = am->vl_clients + client_index;
309
310   if (!pool_is_free (am->vl_clients, regpp))
311     {
312       int i;
313       regp = *regpp;
314       svm = am->vlib_rp;
315       int private_registration = 0;
316
317       /*
318        * Note: the API message handling path will set am->vlib_rp
319        * as appropriate for pairwise / private memory segments
320        */
321       rp = vl_msg_api_alloc (sizeof (*rp));
322       rp->_vl_msg_id = ntohs (VL_API_MEMCLNT_DELETE_REPLY);
323       rp->handle = mp->handle;
324       rp->response = 1;
325
326       vl_msg_api_send_shmem (regp->vl_input_queue, (u8 *) & rp);
327
328       if (client_index != regp->vl_api_registration_pool_index)
329         {
330           clib_warning ("mismatch client_index %d pool_index %d",
331                         client_index, regp->vl_api_registration_pool_index);
332           vl_msg_api_free (rp);
333           return;
334         }
335
336       /* For horizontal scaling, add a hash table... */
337       for (i = 0; i < vec_len (am->vlib_private_rps); i++)
338         {
339           /* Is this a pairwise / private API segment? */
340           if (am->vlib_private_rps[i] == svm)
341             {
342               /* Note: account for the memfd header page */
343               u64 virtual_base = svm->virtual_base - MMAP_PAGESIZE;
344               u64 virtual_size = svm->virtual_size + MMAP_PAGESIZE;
345
346               /*
347                * Kill the registration pool element before we make
348                * the index vanish forever
349                */
350               pool_put_index (am->vl_clients,
351                               regp->vl_api_registration_pool_index);
352
353               vec_delete (am->vlib_private_rps, 1, i);
354               /* Kill it, accounting for the memfd header page */
355               if (munmap ((void *) virtual_base, virtual_size) < 0)
356                 clib_unix_warning ("munmap");
357               /* Reset the queue-length-address cache */
358               vec_reset_length (vl_api_queue_cursizes);
359               private_registration = 1;
360               break;
361             }
362         }
363
364       /* No dangling references, please */
365       *regpp = 0;
366
367       if (private_registration == 0)
368         {
369           pool_put_index (am->vl_clients,
370                           regp->vl_api_registration_pool_index);
371           pthread_mutex_lock (&svm->mutex);
372           oldheap = svm_push_data_heap (svm);
373           /* Poison the old registration */
374           memset (regp, 0xF1, sizeof (*regp));
375           clib_mem_free (regp);
376           pthread_mutex_unlock (&svm->mutex);
377           svm_pop_heap (oldheap);
378           /*
379            * These messages must be freed manually, since they're set up
380            * as "bounce" messages. In the private_registration == 1 case,
381            * we kill the shared-memory segment which contains the message
382            * with munmap.
383            */
384           vl_msg_api_free (mp);
385         }
386     }
387   else
388     {
389       clib_warning ("unknown client ID %d", mp->index);
390     }
391 }
392
393 void
394 vl_api_get_first_msg_id_t_handler (vl_api_get_first_msg_id_t * mp)
395 {
396   vl_api_get_first_msg_id_reply_t *rmp;
397   unix_shared_memory_queue_t *q;
398   uword *p;
399   api_main_t *am = &api_main;
400   vl_api_msg_range_t *rp;
401   u8 name[64];
402   u16 first_msg_id = ~0;
403   int rv = -7;                  /* VNET_API_ERROR_INVALID_VALUE */
404
405   q = vl_api_client_index_to_input_queue (mp->client_index);
406   if (!q)
407     return;
408
409   if (am->msg_range_by_name == 0)
410     goto out;
411
412   strncpy ((char *) name, (char *) mp->name, ARRAY_LEN (name) - 1);
413
414   p = hash_get_mem (am->msg_range_by_name, name);
415   if (p == 0)
416     goto out;
417
418   rp = vec_elt_at_index (am->msg_ranges, p[0]);
419
420   first_msg_id = rp->first_msg_id;
421   rv = 0;
422
423 out:
424
425   rmp = vl_msg_api_alloc (sizeof (*rmp));
426   rmp->_vl_msg_id = ntohs (VL_API_GET_FIRST_MSG_ID_REPLY);
427   rmp->context = mp->context;
428   rmp->retval = ntohl (rv);
429   rmp->first_msg_id = ntohs (first_msg_id);
430   vl_msg_api_send_shmem (q, (u8 *) & rmp);
431 }
432
433 /**
434  * client answered a ping, stave off the grim reaper...
435  */
436
437 void
438   vl_api_memclnt_keepalive_reply_t_handler
439   (vl_api_memclnt_keepalive_reply_t * mp)
440 {
441   vl_api_registration_t *regp;
442   vlib_main_t *vm = vlib_get_main ();
443
444   regp = vl_api_client_index_to_registration (mp->context);
445   if (regp)
446     {
447       regp->last_heard = vlib_time_now (vm);
448       regp->unanswered_pings = 0;
449     }
450   else
451     clib_warning ("BUG: anonymous memclnt_keepalive_reply");
452 }
453
454 /**
455  * We can send ourselves these messages if someone uses the
456  * builtin binary api test tool...
457  */
458 static void
459 vl_api_memclnt_keepalive_t_handler (vl_api_memclnt_keepalive_t * mp)
460 {
461   vl_api_memclnt_keepalive_reply_t *rmp;
462   api_main_t *am;
463   vl_shmem_hdr_t *shmem_hdr;
464
465   am = &api_main;
466   shmem_hdr = am->shmem_hdr;
467
468   rmp = vl_msg_api_alloc_as_if_client (sizeof (*rmp));
469   memset (rmp, 0, sizeof (*rmp));
470   rmp->_vl_msg_id = ntohs (VL_API_MEMCLNT_KEEPALIVE_REPLY);
471   rmp->context = mp->context;
472   vl_msg_api_send_shmem (shmem_hdr->vl_input_queue, (u8 *) & rmp);
473 }
474
475 void
476 vl_api_api_versions_t_handler (vl_api_api_versions_t * mp)
477 {
478   api_main_t *am = &api_main;
479   vl_api_api_versions_reply_t *rmp;
480   unix_shared_memory_queue_t *q;
481   u32 nmsg = vec_len (am->api_version_list);
482   int msg_size = sizeof (*rmp) + sizeof (rmp->api_versions[0]) * nmsg;
483   int i;
484
485   q = vl_api_client_index_to_input_queue (mp->client_index);
486   if (q == 0)
487     return;
488
489   rmp = vl_msg_api_alloc (msg_size);
490   memset (rmp, 0, msg_size);
491   rmp->_vl_msg_id = ntohs (VL_API_API_VERSIONS_REPLY);
492
493   /* fill in the message */
494   rmp->context = mp->context;
495   rmp->count = htonl (nmsg);
496
497   for (i = 0; i < nmsg; ++i)
498     {
499       api_version_t *vl = &am->api_version_list[i];
500       rmp->api_versions[i].major = htonl (vl->major);
501       rmp->api_versions[i].minor = htonl (vl->minor);
502       rmp->api_versions[i].patch = htonl (vl->patch);
503       strncpy ((char *) rmp->api_versions[i].name, vl->name, 64 - 1);
504     }
505
506   vl_msg_api_send_shmem (q, (u8 *) & rmp);
507
508 }
509
510 #define foreach_vlib_api_msg                            \
511 _(MEMCLNT_CREATE, memclnt_create)                       \
512 _(MEMCLNT_DELETE, memclnt_delete)                       \
513 _(GET_FIRST_MSG_ID, get_first_msg_id)                   \
514 _(MEMCLNT_KEEPALIVE, memclnt_keepalive)                 \
515 _(MEMCLNT_KEEPALIVE_REPLY, memclnt_keepalive_reply)     \
516 _(API_VERSIONS, api_versions)
517
518 /*
519  * vl_api_init
520  */
521 static int
522 memory_api_init (const char *region_name)
523 {
524   int rv;
525   api_main_t *am = &api_main;
526   vl_msg_api_msg_config_t cfg;
527   vl_msg_api_msg_config_t *c = &cfg;
528
529   memset (c, 0, sizeof (*c));
530
531   if ((rv = vl_map_shmem (region_name, 1 /* is_vlib */ )) < 0)
532     return rv;
533
534 #define _(N,n) do {                                             \
535     c->id = VL_API_##N;                                         \
536     c->name = #n;                                               \
537     c->handler = vl_api_##n##_t_handler;                        \
538     c->cleanup = vl_noop_handler;                               \
539     c->endian = vl_api_##n##_t_endian;                          \
540     c->print = vl_api_##n##_t_print;                            \
541     c->size = sizeof(vl_api_##n##_t);                           \
542     c->traced = 1; /* trace, so these msgs print */             \
543     c->replay = 0; /* don't replay client create/delete msgs */ \
544     c->message_bounce = 0; /* don't bounce this message */      \
545     vl_msg_api_config(c);} while (0);
546
547   foreach_vlib_api_msg;
548 #undef _
549
550   /*
551    * special-case freeing of memclnt_delete messages, so we can
552    * simply munmap pairwise / private API segments...
553    */
554   am->message_bounce[VL_API_MEMCLNT_DELETE] = 1;
555   am->is_mp_safe[VL_API_MEMCLNT_KEEPALIVE_REPLY] = 1;
556
557   return 0;
558 }
559
560 #define foreach_histogram_bucket                \
561 _(400)                                          \
562 _(200)                                          \
563 _(100)                                          \
564 _(10)
565
566 typedef enum
567 {
568 #define _(n) SLEEP_##n##_US,
569   foreach_histogram_bucket
570 #undef _
571     SLEEP_N_BUCKETS,
572 } histogram_index_t;
573
574 static u64 vector_rate_histogram[SLEEP_N_BUCKETS];
575
576 static void memclnt_queue_callback (vlib_main_t * vm);
577
578 /*
579  * Callback to send ourselves a plugin numbering-space trace msg
580  */
581 static void
582 send_one_plugin_msg_ids_msg (u8 * name, u16 first_msg_id, u16 last_msg_id)
583 {
584   vl_api_trace_plugin_msg_ids_t *mp;
585   api_main_t *am = &api_main;
586   vl_shmem_hdr_t *shmem_hdr = am->shmem_hdr;
587   unix_shared_memory_queue_t *q;
588
589   mp = vl_msg_api_alloc_as_if_client (sizeof (*mp));
590   memset (mp, 0, sizeof (*mp));
591
592   mp->_vl_msg_id = clib_host_to_net_u16 (VL_API_TRACE_PLUGIN_MSG_IDS);
593   strncpy ((char *) mp->plugin_name, (char *) name,
594            sizeof (mp->plugin_name) - 1);
595   mp->first_msg_id = clib_host_to_net_u16 (first_msg_id);
596   mp->last_msg_id = clib_host_to_net_u16 (last_msg_id);
597
598   q = shmem_hdr->vl_input_queue;
599
600   vl_msg_api_send_shmem (q, (u8 *) & mp);
601 }
602
603 static void
604 send_memclnt_keepalive (vl_api_registration_t * regp, f64 now)
605 {
606   vl_api_memclnt_keepalive_t *mp;
607   unix_shared_memory_queue_t *q;
608   api_main_t *am = &api_main;
609   svm_region_t *save_vlib_rp = am->vlib_rp;
610   vl_shmem_hdr_t *save_shmem_hdr = am->shmem_hdr;
611
612   q = regp->vl_input_queue;
613
614   /*
615    * If the queue head is moving, assume that the client is processing
616    * messages and skip the ping. This heuristic may fail if the queue
617    * is in the same position as last time, net of wrapping; in which
618    * case, the client will receive a keepalive.
619    */
620   if (regp->last_queue_head != q->head)
621     {
622       regp->last_heard = now;
623       regp->unanswered_pings = 0;
624       regp->last_queue_head = q->head;
625       return;
626     }
627
628   /*
629    * push/pop shared memory segment, so this routine
630    * will work with "normal" as well as "private segment"
631    * memory clients..
632    */
633
634   am->vlib_rp = regp->vlib_rp;
635   am->shmem_hdr = regp->shmem_hdr;
636
637   mp = vl_msg_api_alloc (sizeof (*mp));
638   memset (mp, 0, sizeof (*mp));
639   mp->_vl_msg_id = clib_host_to_net_u16 (VL_API_MEMCLNT_KEEPALIVE);
640   mp->context = mp->client_index =
641     vl_msg_api_handle_from_index_and_epoch
642     (regp->vl_api_registration_pool_index,
643      am->shmem_hdr->application_restarts);
644
645   regp->unanswered_pings++;
646
647   /* Failure-to-send due to a stuffed queue is absolutely expected */
648   if (unix_shared_memory_queue_add (q, (u8 *) & mp, 1 /* nowait */ ))
649     vl_msg_api_free (mp);
650
651   am->vlib_rp = save_vlib_rp;
652   am->shmem_hdr = save_shmem_hdr;
653 }
654
655 static void
656 dead_client_scan (api_main_t * am, vl_shmem_hdr_t * shm, f64 now)
657 {
658
659   vl_api_registration_t **regpp;
660   vl_api_registration_t *regp;
661   static u32 *dead_indices;
662   static u32 *confused_indices;
663
664   vec_reset_length (dead_indices);
665   vec_reset_length (confused_indices);
666
667   /* *INDENT-OFF* */
668   pool_foreach (regpp, am->vl_clients,
669   ({
670     regp = *regpp;
671     if (regp)
672       {
673         /* If we haven't heard from this client recently... */
674         if (regp->last_heard < (now - 10.0))
675           {
676             if (regp->unanswered_pings == 2)
677               {
678                 unix_shared_memory_queue_t *q;
679                 q = regp->vl_input_queue;
680                 if (kill (q->consumer_pid, 0) >=0)
681                   {
682                     clib_warning ("REAPER: lazy binary API client '%s'",
683                                   regp->name);
684                     regp->unanswered_pings = 0;
685                     regp->last_heard = now;
686                   }
687                 else
688                   {
689                     clib_warning ("REAPER: binary API client '%s' died",
690                                   regp->name);
691                     vec_add1(dead_indices, regpp - am->vl_clients);
692                   }
693               }
694             else
695               send_memclnt_keepalive (regp, now);
696           }
697         else
698           regp->unanswered_pings = 0;
699       }
700     else
701       {
702         clib_warning ("NULL client registration index %d",
703                       regpp - am->vl_clients);
704         vec_add1 (confused_indices, regpp - am->vl_clients);
705       }
706   }));
707   /* *INDENT-ON* */
708   /* This should "never happen," but if it does, fix it... */
709   if (PREDICT_FALSE (vec_len (confused_indices) > 0))
710     {
711       int i;
712       for (i = 0; i < vec_len (confused_indices); i++)
713         {
714           pool_put_index (am->vl_clients, confused_indices[i]);
715         }
716     }
717
718   if (PREDICT_FALSE (vec_len (dead_indices) > 0))
719     {
720       int i;
721       svm_region_t *svm;
722       void *oldheap;
723
724       /* Allow the application to clean up its registrations */
725       for (i = 0; i < vec_len (dead_indices); i++)
726         {
727           regpp = pool_elt_at_index (am->vl_clients, dead_indices[i]);
728           if (regpp)
729             {
730               u32 handle;
731
732               handle = vl_msg_api_handle_from_index_and_epoch
733                 (dead_indices[i], shm->application_restarts);
734               (void) call_reaper_functions (handle);
735             }
736         }
737
738       svm = am->vlib_rp;
739       pthread_mutex_lock (&svm->mutex);
740       oldheap = svm_push_data_heap (svm);
741
742       for (i = 0; i < vec_len (dead_indices); i++)
743         {
744           regpp = pool_elt_at_index (am->vl_clients, dead_indices[i]);
745           if (regpp)
746             {
747               /* Is this a pairwise SVM segment? */
748               if ((*regpp)->vlib_rp != svm)
749                 {
750                   int i;
751                   svm_region_t *dead_rp = (*regpp)->vlib_rp;
752                   /* Note: account for the memfd header page */
753                   u64 virtual_base = dead_rp->virtual_base - MMAP_PAGESIZE;
754                   u64 virtual_size = dead_rp->virtual_size + MMAP_PAGESIZE;
755
756                   /* For horizontal scaling, add a hash table... */
757                   for (i = 0; i < vec_len (am->vlib_private_rps); i++)
758                     if (am->vlib_private_rps[i] == dead_rp)
759                       {
760                         vec_delete (am->vlib_private_rps, 1, i);
761                         goto found;
762                       }
763                   clib_warning ("private rp %llx AWOL", dead_rp);
764
765                 found:
766                   /* Kill it, accounting for the memfd header page */
767                   if (munmap ((void *) virtual_base, virtual_size) < 0)
768                     clib_unix_warning ("munmap");
769                   /* Reset the queue-length-address cache */
770                   vec_reset_length (vl_api_queue_cursizes);
771                 }
772               else
773                 {
774                   /* Poison the old registration */
775                   memset (*regpp, 0xF3, sizeof (**regpp));
776                   clib_mem_free (*regpp);
777                 }
778               /* no dangling references, please */
779               *regpp = 0;
780             }
781           else
782             {
783               svm_pop_heap (oldheap);
784               clib_warning ("Duplicate free, client index %d",
785                             regpp - am->vl_clients);
786               oldheap = svm_push_data_heap (svm);
787             }
788         }
789
790       svm_client_scan_this_region_nolock (am->vlib_rp);
791
792       pthread_mutex_unlock (&svm->mutex);
793       svm_pop_heap (oldheap);
794       for (i = 0; i < vec_len (dead_indices); i++)
795         pool_put_index (am->vl_clients, dead_indices[i]);
796     }
797 }
798
799
800 static uword
801 memclnt_process (vlib_main_t * vm,
802                  vlib_node_runtime_t * node, vlib_frame_t * f)
803 {
804   uword mp;
805   vl_shmem_hdr_t *shm;
806   unix_shared_memory_queue_t *q;
807   clib_error_t *e;
808   int rv;
809   api_main_t *am = &api_main;
810   f64 dead_client_scan_time;
811   f64 sleep_time, start_time;
812   f64 vector_rate;
813   clib_error_t *socksvr_api_init (vlib_main_t * vm);
814   clib_error_t *error;
815   int i;
816   vl_socket_args_for_process_t *a;
817   uword event_type;
818   uword *event_data = 0;
819   int private_segment_rotor = 0;
820   svm_region_t *vlib_rp;
821   f64 now;
822
823   vlib_set_queue_signal_callback (vm, memclnt_queue_callback);
824
825   if ((rv = memory_api_init (am->region_name)) < 0)
826     {
827       clib_warning ("memory_api_init returned %d, quitting...", rv);
828       return 0;
829     }
830
831   if ((error = socksvr_api_init (vm)))
832     {
833       clib_error_report (error);
834       clib_warning ("socksvr_api_init failed, quitting...");
835       return 0;
836     }
837
838   shm = am->shmem_hdr;
839   ASSERT (shm);
840   q = shm->vl_input_queue;
841   ASSERT (q);
842   /* Make a note so we can always find the primary region easily */
843   am->vlib_primary_rp = am->vlib_rp;
844
845   e = vlib_call_init_exit_functions
846     (vm, vm->api_init_function_registrations, 1 /* call_once */ );
847   if (e)
848     clib_error_report (e);
849
850   sleep_time = 10.0;
851   dead_client_scan_time = vlib_time_now (vm) + 10.0;
852
853   /*
854    * Send plugin message range messages for each plugin we loaded
855    */
856   for (i = 0; i < vec_len (am->msg_ranges); i++)
857     {
858       vl_api_msg_range_t *rp = am->msg_ranges + i;
859       send_one_plugin_msg_ids_msg (rp->name, rp->first_msg_id,
860                                    rp->last_msg_id);
861     }
862
863   /*
864    * Save the api message table snapshot, if configured
865    */
866   if (am->save_msg_table_filename)
867     {
868       int fd, rv;
869       u8 *chroot_file;
870       u8 *serialized_message_table;
871
872       /*
873        * Snapshoot the api message table.
874        */
875       if (strstr ((char *) am->save_msg_table_filename, "..")
876           || index ((char *) am->save_msg_table_filename, '/'))
877         {
878           clib_warning ("illegal save-message-table filename '%s'",
879                         am->save_msg_table_filename);
880           goto skip_save;
881         }
882
883       chroot_file = format (0, "/tmp/%s%c", am->save_msg_table_filename, 0);
884
885       fd = creat ((char *) chroot_file, 0644);
886
887       if (fd < 0)
888         {
889           clib_unix_warning ("creat");
890           goto skip_save;
891         }
892
893       serialized_message_table = vl_api_serialize_message_table (am, 0);
894
895       rv = write (fd, serialized_message_table,
896                   vec_len (serialized_message_table));
897
898       if (rv != vec_len (serialized_message_table))
899         clib_unix_warning ("write");
900
901       rv = close (fd);
902       if (rv < 0)
903         clib_unix_warning ("close");
904
905       vec_free (chroot_file);
906       vec_free (serialized_message_table);
907     }
908
909 skip_save:
910
911   /* $$$ pay attention to frame size, control CPU usage */
912   while (1)
913     {
914       i8 *headp;
915       int need_broadcast;
916
917       /*
918        * There's a reason for checking the queue before
919        * sleeping. If the vlib application crashes, it's entirely
920        * possible for a client to enqueue a connect request
921        * during the process restart interval.
922        *
923        * Unless some force of physics causes the new incarnation
924        * of the application to process the request, the client will
925        * sit and wait for Godot...
926        */
927       vector_rate = vlib_last_vector_length_per_node (vm);
928       start_time = vlib_time_now (vm);
929       while (1)
930         {
931           pthread_mutex_lock (&q->mutex);
932           if (q->cursize == 0)
933             {
934               vm->api_queue_nonempty = 0;
935               pthread_mutex_unlock (&q->mutex);
936
937               if (TRACE_VLIB_MEMORY_QUEUE)
938                 {
939                   /* *INDENT-OFF* */
940                   ELOG_TYPE_DECLARE (e) =
941                     {
942                       .format = "q-underflow: len %d",
943                       .format_args = "i4",
944                     };
945                   /* *INDENT-ON* */
946                   struct
947                   {
948                     u32 len;
949                   } *ed;
950                   ed = ELOG_DATA (&vm->elog_main, e);
951                   ed->len = 0;
952                 }
953               sleep_time = 20.0;
954               break;
955             }
956
957           headp = (i8 *) (q->data + sizeof (uword) * q->head);
958           clib_memcpy (&mp, headp, sizeof (uword));
959
960           q->head++;
961           need_broadcast = (q->cursize == q->maxsize / 2);
962           q->cursize--;
963
964           if (PREDICT_FALSE (q->head == q->maxsize))
965             q->head = 0;
966           pthread_mutex_unlock (&q->mutex);
967           if (need_broadcast)
968             (void) pthread_cond_broadcast (&q->condvar);
969
970           vl_msg_api_handler_with_vm_node (am, (void *) mp, vm, node);
971
972           /* Allow no more than 10us without a pause */
973           if (vlib_time_now (vm) > start_time + 10e-6)
974             {
975               int index = SLEEP_400_US;
976               if (vector_rate > 40.0)
977                 sleep_time = 400e-6;
978               else if (vector_rate > 20.0)
979                 {
980                   index = SLEEP_200_US;
981                   sleep_time = 200e-6;
982                 }
983               else if (vector_rate >= 1.0)
984                 {
985                   index = SLEEP_100_US;
986                   sleep_time = 100e-6;
987                 }
988               else
989                 {
990                   index = SLEEP_10_US;
991                   sleep_time = 10e-6;
992                 }
993               vector_rate_histogram[index] += 1;
994               break;
995             }
996         }
997
998       /*
999        * see if we have any private api shared-memory segments
1000        * If so, push required context variables, and process
1001        * a message.
1002        */
1003       if (PREDICT_FALSE (vec_len (am->vlib_private_rps)))
1004         {
1005           unix_shared_memory_queue_t *save_vlib_input_queue = q;
1006           vl_shmem_hdr_t *save_shmem_hdr = am->shmem_hdr;
1007           svm_region_t *save_vlib_rp = am->vlib_rp;
1008
1009           vlib_rp = am->vlib_rp = am->vlib_private_rps[private_segment_rotor];
1010
1011           am->shmem_hdr = (void *) vlib_rp->user_ctx;
1012           q = am->shmem_hdr->vl_input_queue;
1013
1014           pthread_mutex_lock (&q->mutex);
1015           if (q->cursize > 0)
1016             {
1017               headp = (i8 *) (q->data + sizeof (uword) * q->head);
1018               clib_memcpy (&mp, headp, sizeof (uword));
1019
1020               q->head++;
1021               need_broadcast = (q->cursize == q->maxsize / 2);
1022               q->cursize--;
1023
1024               if (PREDICT_FALSE (q->head == q->maxsize))
1025                 q->head = 0;
1026               pthread_mutex_unlock (&q->mutex);
1027
1028               if (need_broadcast)
1029                 (void) pthread_cond_broadcast (&q->condvar);
1030
1031               vl_msg_api_handler_with_vm_node (am, (void *) mp, vm, node);
1032             }
1033           else
1034             pthread_mutex_unlock (&q->mutex);
1035
1036           q = save_vlib_input_queue;
1037           am->shmem_hdr = save_shmem_hdr;
1038           am->vlib_rp = save_vlib_rp;
1039
1040           private_segment_rotor++;
1041           if (private_segment_rotor >= vec_len (am->vlib_private_rps))
1042             private_segment_rotor = 0;
1043         }
1044
1045       vlib_process_wait_for_event_or_clock (vm, sleep_time);
1046       vec_reset_length (event_data);
1047       event_type = vlib_process_get_events (vm, &event_data);
1048       now = vlib_time_now (vm);
1049
1050       switch (event_type)
1051         {
1052         case QUEUE_SIGNAL_EVENT:
1053           vm->queue_signal_pending = 0;
1054           break;
1055
1056         case SOCKET_READ_EVENT:
1057           for (i = 0; i < vec_len (event_data); i++)
1058             {
1059               a = pool_elt_at_index (socket_main.process_args, event_data[i]);
1060               vl_api_socket_process_msg (a->clib_file, a->regp,
1061                                          (i8 *) a->data);
1062               vec_free (a->data);
1063               pool_put (socket_main.process_args, a);
1064             }
1065           break;
1066
1067           /* Timeout... */
1068         case -1:
1069           break;
1070
1071         default:
1072           clib_warning ("unknown event type %d", event_type);
1073           break;
1074         }
1075
1076       if (now > dead_client_scan_time)
1077         {
1078           dead_client_scan (am, shm, now);
1079           dead_client_scan_time = vlib_time_now (vm) + 10.0;
1080         }
1081
1082       if (TRACE_VLIB_MEMORY_QUEUE)
1083         {
1084           /* *INDENT-OFF* */
1085           ELOG_TYPE_DECLARE (e) = {
1086             .format = "q-awake: len %d",
1087             .format_args = "i4",
1088           };
1089           /* *INDENT-ON* */
1090           struct
1091           {
1092             u32 len;
1093           } *ed;
1094           ed = ELOG_DATA (&vm->elog_main, e);
1095           ed->len = q->cursize;
1096         }
1097     }
1098
1099   return 0;
1100 }
1101 /* *INDENT-OFF* */
1102 VLIB_REGISTER_NODE (memclnt_node) =
1103 {
1104   .function = memclnt_process,
1105   .type = VLIB_NODE_TYPE_PROCESS,
1106   .name = "api-rx-from-ring",
1107   .state = VLIB_NODE_STATE_DISABLED,
1108 };
1109 /* *INDENT-ON* */
1110
1111
1112 static clib_error_t *
1113 vl_api_show_histogram_command (vlib_main_t * vm,
1114                                unformat_input_t * input,
1115                                vlib_cli_command_t * cli_cmd)
1116 {
1117   u64 total_counts = 0;
1118   int i;
1119
1120   for (i = 0; i < SLEEP_N_BUCKETS; i++)
1121     {
1122       total_counts += vector_rate_histogram[i];
1123     }
1124
1125   if (total_counts == 0)
1126     {
1127       vlib_cli_output (vm, "No control-plane activity.");
1128       return 0;
1129     }
1130
1131 #define _(n)                                                    \
1132     do {                                                        \
1133         f64 percent;                                            \
1134         percent = ((f64) vector_rate_histogram[SLEEP_##n##_US]) \
1135             / (f64) total_counts;                               \
1136         percent *= 100.0;                                       \
1137         vlib_cli_output (vm, "Sleep %3d us: %llu, %.2f%%",n,    \
1138                          vector_rate_histogram[SLEEP_##n##_US], \
1139                          percent);                              \
1140     } while (0);
1141   foreach_histogram_bucket;
1142 #undef _
1143
1144   return 0;
1145 }
1146
1147 /*?
1148  * Display the binary api sleep-time histogram
1149 ?*/
1150 /* *INDENT-OFF* */
1151 VLIB_CLI_COMMAND (cli_show_api_histogram_command, static) =
1152 {
1153   .path = "show api histogram",
1154   .short_help = "show api histogram",
1155   .function = vl_api_show_histogram_command,
1156 };
1157 /* *INDENT-ON* */
1158
1159 static clib_error_t *
1160 vl_api_clear_histogram_command (vlib_main_t * vm,
1161                                 unformat_input_t * input,
1162                                 vlib_cli_command_t * cli_cmd)
1163 {
1164   int i;
1165
1166   for (i = 0; i < SLEEP_N_BUCKETS; i++)
1167     vector_rate_histogram[i] = 0;
1168   return 0;
1169 }
1170
1171 /*?
1172  * Clear the binary api sleep-time histogram
1173 ?*/
1174 /* *INDENT-OFF* */
1175 VLIB_CLI_COMMAND (cli_clear_api_histogram_command, static) =
1176 {
1177   .path = "clear api histogram",
1178   .short_help = "clear api histogram",
1179   .function = vl_api_clear_histogram_command,
1180 };
1181 /* *INDENT-ON* */
1182
1183 volatile int **vl_api_queue_cursizes;
1184
1185 static void
1186 memclnt_queue_callback (vlib_main_t * vm)
1187 {
1188   int i;
1189   api_main_t *am = &api_main;
1190
1191   if (PREDICT_FALSE (vec_len (vl_api_queue_cursizes) !=
1192                      1 + vec_len (am->vlib_private_rps)))
1193     {
1194       vl_shmem_hdr_t *shmem_hdr = am->shmem_hdr;
1195       unix_shared_memory_queue_t *q;
1196
1197       if (shmem_hdr == 0)
1198         return;
1199
1200       q = shmem_hdr->vl_input_queue;
1201       if (q == 0)
1202         return;
1203
1204       vec_add1 (vl_api_queue_cursizes, &q->cursize);
1205
1206       for (i = 0; i < vec_len (am->vlib_private_rps); i++)
1207         {
1208           svm_region_t *vlib_rp = am->vlib_private_rps[i];
1209
1210           shmem_hdr = (void *) vlib_rp->user_ctx;
1211           q = shmem_hdr->vl_input_queue;
1212           vec_add1 (vl_api_queue_cursizes, &q->cursize);
1213         }
1214     }
1215
1216   for (i = 0; i < vec_len (vl_api_queue_cursizes); i++)
1217     {
1218       if (*vl_api_queue_cursizes[i])
1219         {
1220           vm->queue_signal_pending = 1;
1221           vm->api_queue_nonempty = 1;
1222           vlib_process_signal_event (vm, memclnt_node.index,
1223                                      /* event_type */ QUEUE_SIGNAL_EVENT,
1224                                      /* event_data */ 0);
1225           break;
1226         }
1227     }
1228 }
1229
1230 void
1231 vl_enable_disable_memory_api (vlib_main_t * vm, int enable)
1232 {
1233   vlib_node_set_state (vm, memclnt_node.index,
1234                        (enable
1235                         ? VLIB_NODE_STATE_POLLING
1236                         : VLIB_NODE_STATE_DISABLED));
1237 }
1238
1239 static uword
1240 api_rx_from_node (vlib_main_t * vm,
1241                   vlib_node_runtime_t * node, vlib_frame_t * frame)
1242 {
1243   uword n_packets = frame->n_vectors;
1244   uword n_left_from;
1245   u32 *from;
1246   static u8 *long_msg;
1247
1248   vec_validate (long_msg, 4095);
1249   n_left_from = frame->n_vectors;
1250   from = vlib_frame_args (frame);
1251
1252   while (n_left_from > 0)
1253     {
1254       u32 bi0;
1255       vlib_buffer_t *b0;
1256       void *msg;
1257       uword msg_len;
1258
1259       bi0 = from[0];
1260       b0 = vlib_get_buffer (vm, bi0);
1261       from += 1;
1262       n_left_from -= 1;
1263
1264       msg = b0->data + b0->current_data;
1265       msg_len = b0->current_length;
1266       if (b0->flags & VLIB_BUFFER_NEXT_PRESENT)
1267         {
1268           ASSERT (long_msg != 0);
1269           _vec_len (long_msg) = 0;
1270           vec_add (long_msg, msg, msg_len);
1271           while (b0->flags & VLIB_BUFFER_NEXT_PRESENT)
1272             {
1273               b0 = vlib_get_buffer (vm, b0->next_buffer);
1274               msg = b0->data + b0->current_data;
1275               msg_len = b0->current_length;
1276               vec_add (long_msg, msg, msg_len);
1277             }
1278           msg = long_msg;
1279         }
1280       vl_msg_api_handler_no_trace_no_free (msg);
1281     }
1282
1283   /* Free what we've been given. */
1284   vlib_buffer_free (vm, vlib_frame_args (frame), n_packets);
1285
1286   return n_packets;
1287 }
1288
1289 /* *INDENT-OFF* */
1290 VLIB_REGISTER_NODE (api_rx_from_node_node,static) = {
1291     .function = api_rx_from_node,
1292     .type = VLIB_NODE_TYPE_INTERNAL,
1293     .vector_size = 4,
1294     .name = "api-rx-from-node",
1295 };
1296 /* *INDENT-ON* */
1297
1298 static clib_error_t *
1299 setup_memclnt_exit (vlib_main_t * vm)
1300 {
1301   atexit (vl_unmap_shmem);
1302   return 0;
1303 }
1304
1305 VLIB_INIT_FUNCTION (setup_memclnt_exit);
1306
1307 u8 *
1308 format_api_message_rings (u8 * s, va_list * args)
1309 {
1310   api_main_t *am = va_arg (*args, api_main_t *);
1311   vl_shmem_hdr_t *shmem_hdr = va_arg (*args, vl_shmem_hdr_t *);
1312   int main_segment = va_arg (*args, int);
1313   ring_alloc_t *ap;
1314   int i;
1315
1316   if (shmem_hdr == 0)
1317     return format (s, "%8s %8s %8s %8s %8s\n",
1318                    "Owner", "Size", "Nitems", "Hits", "Misses");
1319
1320   ap = shmem_hdr->vl_rings;
1321
1322   for (i = 0; i < vec_len (shmem_hdr->vl_rings); i++)
1323     {
1324       s = format (s, "%8s %8d %8d %8d %8d\n",
1325                   "vlib", ap->size, ap->nitems, ap->hits, ap->misses);
1326       ap++;
1327     }
1328
1329   ap = shmem_hdr->client_rings;
1330
1331   for (i = 0; i < vec_len (shmem_hdr->client_rings); i++)
1332     {
1333       s = format (s, "%8s %8d %8d %8d %8d\n",
1334                   "clnt", ap->size, ap->nitems, ap->hits, ap->misses);
1335       ap++;
1336     }
1337
1338   if (main_segment)
1339     {
1340       s = format (s, "%d ring miss fallback allocations\n", am->ring_misses);
1341       s = format
1342         (s,
1343          "%d application restarts, %d reclaimed msgs, %d garbage collects\n",
1344          shmem_hdr->application_restarts, shmem_hdr->restart_reclaims,
1345          shmem_hdr->garbage_collects);
1346     }
1347   return s;
1348 }
1349
1350
1351 static clib_error_t *
1352 vl_api_ring_command (vlib_main_t * vm,
1353                      unformat_input_t * input, vlib_cli_command_t * cli_cmd)
1354 {
1355   int i;
1356   vl_shmem_hdr_t *shmem_hdr;
1357   api_main_t *am = &api_main;
1358
1359   /* First, dump the primary region rings.. */
1360
1361   if (am->vlib_primary_rp == 0 || am->vlib_primary_rp->user_ctx == 0)
1362     {
1363       vlib_cli_output (vm, "Shared memory segment not initialized...\n");
1364       return 0;
1365     }
1366
1367   shmem_hdr = (void *) am->vlib_primary_rp->user_ctx;
1368
1369   vlib_cli_output (vm, "Main API segment rings:");
1370
1371   vlib_cli_output (vm, "%U", format_api_message_rings, am,
1372                    0 /* print header */ , 0 /* notused */ );
1373
1374   vlib_cli_output (vm, "%U", format_api_message_rings, am,
1375                    shmem_hdr, 1 /* main segment */ );
1376
1377   for (i = 0; i < vec_len (am->vlib_private_rps); i++)
1378     {
1379       svm_region_t *vlib_rp = am->vlib_private_rps[i];
1380       shmem_hdr = (void *) vlib_rp->user_ctx;
1381       vl_api_registration_t **regpp;
1382       vl_api_registration_t *regp = 0;
1383
1384       /* For horizontal scaling, add a hash table... */
1385       /* *INDENT-OFF* */
1386       pool_foreach (regpp, am->vl_clients,
1387       ({
1388         regp = *regpp;
1389         if (regp && regp->vlib_rp == vlib_rp)
1390           {
1391             vlib_cli_output (vm, "%s segment rings:", regp->name);
1392             goto found;
1393           }
1394       }));
1395       vlib_cli_output (vm, "regp %llx not found?", regp);
1396       continue;
1397       /* *INDENT-ON* */
1398     found:
1399       vlib_cli_output (vm, "%U", format_api_message_rings, am,
1400                        0 /* print header */ , 0 /* notused */ );
1401       vlib_cli_output (vm, "%U", format_api_message_rings, am,
1402                        shmem_hdr, 0 /* main segment */ );
1403     }
1404
1405   return 0;
1406 }
1407
1408 void dump_socket_clients (vlib_main_t * vm, api_main_t * am)
1409   __attribute__ ((weak));
1410
1411 void
1412 dump_socket_clients (vlib_main_t * vm, api_main_t * am)
1413 {
1414 }
1415
1416 static clib_error_t *
1417 vl_api_client_command (vlib_main_t * vm,
1418                        unformat_input_t * input, vlib_cli_command_t * cli_cmd)
1419 {
1420   vl_api_registration_t **regpp, *regp;
1421   unix_shared_memory_queue_t *q;
1422   char *health;
1423   api_main_t *am = &api_main;
1424   u32 *confused_indices = 0;
1425
1426   if (!pool_elts (am->vl_clients))
1427     goto socket_clients;
1428   vlib_cli_output (vm, "Shared memory clients");
1429   vlib_cli_output (vm, "%20s %8s %14s %18s %s",
1430                    "Name", "PID", "Queue Length", "Queue VA", "Health");
1431
1432   /* *INDENT-OFF* */
1433   pool_foreach (regpp, am->vl_clients,
1434   ({
1435     regp = *regpp;
1436
1437     if (regp)
1438       {
1439         if (regp->unanswered_pings > 0)
1440           health = "questionable";
1441         else
1442           health = "OK";
1443
1444         q = regp->vl_input_queue;
1445
1446         vlib_cli_output (vm, "%20s %8d %14d 0x%016llx %s\n",
1447                          regp->name, q->consumer_pid, q->cursize,
1448                          q, health);
1449       }
1450     else
1451       {
1452         clib_warning ("NULL client registration index %d",
1453                       regpp - am->vl_clients);
1454         vec_add1 (confused_indices, regpp - am->vl_clients);
1455       }
1456   }));
1457   /* *INDENT-ON* */
1458
1459   /* This should "never happen," but if it does, fix it... */
1460   if (PREDICT_FALSE (vec_len (confused_indices) > 0))
1461     {
1462       int i;
1463       for (i = 0; i < vec_len (confused_indices); i++)
1464         {
1465           pool_put_index (am->vl_clients, confused_indices[i]);
1466         }
1467     }
1468   vec_free (confused_indices);
1469
1470   if (am->missing_clients)
1471     vlib_cli_output (vm, "%u messages with missing clients",
1472                      am->missing_clients);
1473 socket_clients:
1474   dump_socket_clients (vm, am);
1475
1476   return 0;
1477 }
1478
1479 static clib_error_t *
1480 vl_api_status_command (vlib_main_t * vm,
1481                        unformat_input_t * input, vlib_cli_command_t * cli_cmd)
1482 {
1483   api_main_t *am = &api_main;
1484
1485   // check if rx_trace and tx_trace are not null pointers
1486
1487   if (am->rx_trace == 0)
1488     {
1489       vlib_cli_output (vm, "RX Trace disabled\n");
1490     }
1491   else
1492     {
1493       if (am->rx_trace->enabled == 0)
1494         vlib_cli_output (vm, "RX Trace disabled\n");
1495       else
1496         vlib_cli_output (vm, "RX Trace enabled\n");
1497     }
1498
1499   if (am->tx_trace == 0)
1500     {
1501       vlib_cli_output (vm, "TX Trace disabled\n");
1502     }
1503   else
1504     {
1505       if (am->tx_trace->enabled == 0)
1506         vlib_cli_output (vm, "TX Trace disabled\n");
1507       else
1508         vlib_cli_output (vm, "TX Trace enabled\n");
1509     }
1510
1511   return 0;
1512 }
1513
1514 /* *INDENT-OFF* */
1515 VLIB_CLI_COMMAND (cli_show_api_command, static) =
1516 {
1517   .path = "show api",
1518   .short_help = "Show API information",
1519 };
1520 /* *INDENT-ON* */
1521
1522 /*?
1523  * Display binary api message allocation ring statistics
1524 ?*/
1525 /* *INDENT-OFF* */
1526 VLIB_CLI_COMMAND (cli_show_api_ring_command, static) =
1527 {
1528   .path = "show api ring-stats",
1529   .short_help = "Message ring statistics",
1530   .function = vl_api_ring_command,
1531 };
1532 /* *INDENT-ON* */
1533
1534 /*?
1535  * Display current api client connections
1536 ?*/
1537 /* *INDENT-OFF* */
1538 VLIB_CLI_COMMAND (cli_show_api_clients_command, static) =
1539 {
1540   .path = "show api clients",
1541   .short_help = "Client information",
1542   .function = vl_api_client_command,
1543 };
1544 /* *INDENT-ON* */
1545
1546 /*?
1547  * Display the current api message tracing status
1548 ?*/
1549 /* *INDENT-OFF* */
1550 VLIB_CLI_COMMAND (cli_show_api_status_command, static) =
1551 {
1552   .path = "show api trace-status",
1553   .short_help = "Display API trace status",
1554   .function = vl_api_status_command,
1555 };
1556 /* *INDENT-ON* */
1557
1558 static clib_error_t *
1559 vl_api_message_table_command (vlib_main_t * vm,
1560                               unformat_input_t * input,
1561                               vlib_cli_command_t * cli_cmd)
1562 {
1563   api_main_t *am = &api_main;
1564   int i;
1565   int verbose = 0;
1566
1567   if (unformat (input, "verbose"))
1568     verbose = 1;
1569
1570
1571   if (verbose == 0)
1572     vlib_cli_output (vm, "%-4s %s", "ID", "Name");
1573   else
1574     vlib_cli_output (vm, "%-4s %-40s %6s %7s", "ID", "Name", "Bounce",
1575                      "MP-safe");
1576
1577   for (i = 1; i < vec_len (am->msg_names); i++)
1578     {
1579       if (verbose == 0)
1580         {
1581           vlib_cli_output (vm, "%-4d %s", i,
1582                            am->msg_names[i] ? am->msg_names[i] :
1583                            "  [no handler]");
1584         }
1585       else
1586         {
1587           vlib_cli_output (vm, "%-4d %-40s %6d %7d", i,
1588                            am->msg_names[i] ? am->msg_names[i] :
1589                            "  [no handler]", am->message_bounce[i],
1590                            am->is_mp_safe[i]);
1591         }
1592     }
1593
1594   return 0;
1595 }
1596
1597 /*?
1598  * Display the current api message decode tables
1599 ?*/
1600 /* *INDENT-OFF* */
1601 VLIB_CLI_COMMAND (cli_show_api_message_table_command, static) =
1602 {
1603   .path = "show api message-table",
1604   .short_help = "Message Table",
1605   .function = vl_api_message_table_command,
1606 };
1607 /* *INDENT-ON* */
1608
1609 static clib_error_t *
1610 vl_api_trace_command (vlib_main_t * vm,
1611                       unformat_input_t * input, vlib_cli_command_t * cli_cmd)
1612 {
1613   u32 nitems = 1024;
1614   vl_api_trace_which_t which = VL_API_TRACE_RX;
1615   api_main_t *am = &api_main;
1616
1617   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1618     {
1619       if (unformat (input, "rx nitems %u", &nitems) || unformat (input, "rx"))
1620         goto configure;
1621       else if (unformat (input, "tx nitems %u", &nitems)
1622                || unformat (input, "tx"))
1623         {
1624           which = VL_API_TRACE_RX;
1625           goto configure;
1626         }
1627       else if (unformat (input, "on rx"))
1628         {
1629           vl_msg_api_trace_onoff (am, VL_API_TRACE_RX, 1);
1630         }
1631       else if (unformat (input, "on tx"))
1632         {
1633           vl_msg_api_trace_onoff (am, VL_API_TRACE_TX, 1);
1634         }
1635       else if (unformat (input, "on"))
1636         {
1637           vl_msg_api_trace_onoff (am, VL_API_TRACE_RX, 1);
1638         }
1639       else if (unformat (input, "off"))
1640         {
1641           vl_msg_api_trace_onoff (am, VL_API_TRACE_RX, 0);
1642           vl_msg_api_trace_onoff (am, VL_API_TRACE_TX, 0);
1643         }
1644       else if (unformat (input, "free"))
1645         {
1646           vl_msg_api_trace_onoff (am, VL_API_TRACE_RX, 0);
1647           vl_msg_api_trace_onoff (am, VL_API_TRACE_TX, 0);
1648           vl_msg_api_trace_free (am, VL_API_TRACE_RX);
1649           vl_msg_api_trace_free (am, VL_API_TRACE_TX);
1650         }
1651       else if (unformat (input, "debug on"))
1652         {
1653           am->msg_print_flag = 1;
1654         }
1655       else if (unformat (input, "debug off"))
1656         {
1657           am->msg_print_flag = 0;
1658         }
1659       else
1660         return clib_error_return (0, "unknown input `%U'",
1661                                   format_unformat_error, input);
1662     }
1663   return 0;
1664
1665 configure:
1666   if (vl_msg_api_trace_configure (am, which, nitems))
1667     {
1668       vlib_cli_output (vm, "warning: trace configure error (%d, %d)",
1669                        which, nitems);
1670     }
1671
1672   return 0;
1673 }
1674
1675 /*?
1676  * Control the binary API trace mechanism
1677 ?*/
1678 /* *INDENT-OFF* */
1679 VLIB_CLI_COMMAND (trace, static) =
1680 {
1681   .path = "set api-trace [on][on tx][on rx][off][free][debug on][debug off]",
1682   .short_help = "API trace",
1683   .function = vl_api_trace_command,
1684 };
1685 /* *INDENT-ON* */
1686
1687 clib_error_t *
1688 vlibmemory_init (vlib_main_t * vm)
1689 {
1690   api_main_t *am = &api_main;
1691   svm_map_region_args_t _a, *a = &_a;
1692   clib_error_t *error;
1693
1694   memset (a, 0, sizeof (*a));
1695   a->root_path = am->root_path;
1696   a->name = SVM_GLOBAL_REGION_NAME;
1697   a->baseva = (am->global_baseva != 0) ?
1698     am->global_baseva : SVM_GLOBAL_REGION_BASEVA;
1699   a->size = (am->global_size != 0) ? am->global_size : SVM_GLOBAL_REGION_SIZE;
1700   a->flags = SVM_FLAGS_NODATA;
1701   a->uid = am->api_uid;
1702   a->gid = am->api_gid;
1703   a->pvt_heap_size =
1704     (am->global_pvt_heap_size !=
1705      0) ? am->global_pvt_heap_size : SVM_PVT_MHEAP_SIZE;
1706
1707   svm_region_init_args (a);
1708
1709   error = vlib_call_init_function (vm, vlibsocket_init);
1710
1711   return error;
1712 }
1713
1714 VLIB_INIT_FUNCTION (vlibmemory_init);
1715
1716 void
1717 vl_set_memory_region_name (const char *name)
1718 {
1719   api_main_t *am = &api_main;
1720
1721   am->region_name = name;
1722 }
1723
1724 static int
1725 range_compare (vl_api_msg_range_t * a0, vl_api_msg_range_t * a1)
1726 {
1727   int len0, len1, clen;
1728
1729   len0 = vec_len (a0->name);
1730   len1 = vec_len (a1->name);
1731   clen = len0 < len1 ? len0 : len1;
1732   return (strncmp ((char *) a0->name, (char *) a1->name, clen));
1733 }
1734
1735 static u8 *
1736 format_api_msg_range (u8 * s, va_list * args)
1737 {
1738   vl_api_msg_range_t *rp = va_arg (*args, vl_api_msg_range_t *);
1739
1740   if (rp == 0)
1741     s = format (s, "%-50s%9s%9s", "Name", "First-ID", "Last-ID");
1742   else
1743     s = format (s, "%-50s%9d%9d", rp->name, rp->first_msg_id,
1744                 rp->last_msg_id);
1745
1746   return s;
1747 }
1748
1749 static clib_error_t *
1750 vl_api_show_plugin_command (vlib_main_t * vm,
1751                             unformat_input_t * input,
1752                             vlib_cli_command_t * cli_cmd)
1753 {
1754   api_main_t *am = &api_main;
1755   vl_api_msg_range_t *rp = 0;
1756   int i;
1757
1758   if (vec_len (am->msg_ranges) == 0)
1759     {
1760       vlib_cli_output (vm, "No plugin API message ranges configured...");
1761       return 0;
1762     }
1763
1764   rp = vec_dup (am->msg_ranges);
1765
1766   vec_sort_with_function (rp, range_compare);
1767
1768   vlib_cli_output (vm, "Plugin API message ID ranges...\n");
1769   vlib_cli_output (vm, "%U", format_api_msg_range, 0 /* header */ );
1770
1771   for (i = 0; i < vec_len (rp); i++)
1772     vlib_cli_output (vm, "%U", format_api_msg_range, rp + i);
1773
1774   vec_free (rp);
1775
1776   return 0;
1777 }
1778
1779 /*?
1780  * Display the plugin binary API message range table
1781 ?*/
1782 /* *INDENT-OFF* */
1783 VLIB_CLI_COMMAND (cli_show_api_plugin_command, static) =
1784 {
1785   .path = "show api plugin",
1786   .short_help = "show api plugin",
1787   .function = vl_api_show_plugin_command,
1788 };
1789 /* *INDENT-ON* */
1790
1791 static void
1792 vl_api_rpc_call_t_handler (vl_api_rpc_call_t * mp)
1793 {
1794   vl_api_rpc_call_reply_t *rmp;
1795   int (*fp) (void *);
1796   i32 rv = 0;
1797   vlib_main_t *vm = vlib_get_main ();
1798
1799   if (mp->function == 0)
1800     {
1801       rv = -1;
1802       clib_warning ("rpc NULL function pointer");
1803     }
1804
1805   else
1806     {
1807       if (mp->need_barrier_sync)
1808         vlib_worker_thread_barrier_sync (vm);
1809
1810       fp = uword_to_pointer (mp->function, int (*)(void *));
1811       rv = fp (mp->data);
1812
1813       if (mp->need_barrier_sync)
1814         vlib_worker_thread_barrier_release (vm);
1815     }
1816
1817   if (mp->send_reply)
1818     {
1819       unix_shared_memory_queue_t *q =
1820         vl_api_client_index_to_input_queue (mp->client_index);
1821       if (q)
1822         {
1823           rmp = vl_msg_api_alloc_as_if_client (sizeof (*rmp));
1824           rmp->_vl_msg_id = ntohs (VL_API_RPC_CALL_REPLY);
1825           rmp->context = mp->context;
1826           rmp->retval = rv;
1827           vl_msg_api_send_shmem (q, (u8 *) & rmp);
1828         }
1829     }
1830   if (mp->multicast)
1831     {
1832       clib_warning ("multicast not yet implemented...");
1833     }
1834 }
1835
1836 static void
1837 vl_api_rpc_call_reply_t_handler (vl_api_rpc_call_reply_t * mp)
1838 {
1839   clib_warning ("unimplemented");
1840 }
1841
1842 void
1843 vl_api_send_pending_rpc_requests (vlib_main_t * vm)
1844 {
1845   api_main_t *am = &api_main;
1846   vl_shmem_hdr_t *shmem_hdr = am->shmem_hdr;
1847   unix_shared_memory_queue_t *q;
1848   int i;
1849
1850   /*
1851    * Use the "normal" control-plane mechanism for the main thread.
1852    * Well, almost. if the main input queue is full, we cannot
1853    * block. Otherwise, we can expect a barrier sync timeout.
1854    */
1855   q = shmem_hdr->vl_input_queue;
1856
1857   for (i = 0; i < vec_len (vm->pending_rpc_requests); i++)
1858     {
1859       while (pthread_mutex_trylock (&q->mutex))
1860         vlib_worker_thread_barrier_check ();
1861
1862       while (PREDICT_FALSE (unix_shared_memory_queue_is_full (q)))
1863         {
1864           pthread_mutex_unlock (&q->mutex);
1865           vlib_worker_thread_barrier_check ();
1866           while (pthread_mutex_trylock (&q->mutex))
1867             vlib_worker_thread_barrier_check ();
1868         }
1869
1870       vl_msg_api_send_shmem_nolock (q, (u8 *) (vm->pending_rpc_requests + i));
1871
1872       pthread_mutex_unlock (&q->mutex);
1873     }
1874   _vec_len (vm->pending_rpc_requests) = 0;
1875 }
1876
1877 always_inline void
1878 vl_api_rpc_call_main_thread_inline (void *fp, u8 * data, u32 data_length,
1879                                     u8 force_rpc)
1880 {
1881   vl_api_rpc_call_t *mp;
1882   vlib_main_t *vm = vlib_get_main ();
1883
1884   /* Main thread and not a forced RPC: call the function directly */
1885   if ((force_rpc == 0) && (vlib_get_thread_index () == 0))
1886     {
1887       void (*call_fp) (void *);
1888
1889       vlib_worker_thread_barrier_sync (vm);
1890
1891       call_fp = fp;
1892       call_fp (data);
1893
1894       vlib_worker_thread_barrier_release (vm);
1895       return;
1896     }
1897
1898   /* Otherwise, actually do an RPC */
1899   mp = vl_msg_api_alloc_as_if_client (sizeof (*mp) + data_length);
1900
1901   memset (mp, 0, sizeof (*mp));
1902   clib_memcpy (mp->data, data, data_length);
1903   mp->_vl_msg_id = ntohs (VL_API_RPC_CALL);
1904   mp->function = pointer_to_uword (fp);
1905   mp->need_barrier_sync = 1;
1906
1907   vec_add1 (vm->pending_rpc_requests, (uword) mp);
1908 }
1909
1910 /*
1911  * Check if called from worker threads.
1912  * If so, make rpc call of fp through shmem.
1913  * Otherwise, call fp directly
1914  */
1915 void
1916 vl_api_rpc_call_main_thread (void *fp, u8 * data, u32 data_length)
1917 {
1918   vl_api_rpc_call_main_thread_inline (fp, data, data_length,    /*force_rpc */
1919                                       0);
1920 }
1921
1922 /*
1923  * Always make rpc call of fp through shmem, useful for calling from threads
1924  * not setup as worker threads, such as DPDK callback thread
1925  */
1926 void
1927 vl_api_force_rpc_call_main_thread (void *fp, u8 * data, u32 data_length)
1928 {
1929   vl_api_rpc_call_main_thread_inline (fp, data, data_length,    /*force_rpc */
1930                                       1);
1931 }
1932
1933 static void
1934 vl_api_trace_plugin_msg_ids_t_handler (vl_api_trace_plugin_msg_ids_t * mp)
1935 {
1936   api_main_t *am = &api_main;
1937   vl_api_msg_range_t *rp;
1938   uword *p;
1939
1940   /* Noop (except for tracing) during normal operation */
1941   if (am->replay_in_progress == 0)
1942     return;
1943
1944   p = hash_get_mem (am->msg_range_by_name, mp->plugin_name);
1945   if (p == 0)
1946     {
1947       clib_warning ("WARNING: traced plugin '%s' not in current image",
1948                     mp->plugin_name);
1949       return;
1950     }
1951
1952   rp = vec_elt_at_index (am->msg_ranges, p[0]);
1953   if (rp->first_msg_id != clib_net_to_host_u16 (mp->first_msg_id))
1954     {
1955       clib_warning ("WARNING: traced plugin '%s' first message id %d not %d",
1956                     mp->plugin_name, clib_net_to_host_u16 (mp->first_msg_id),
1957                     rp->first_msg_id);
1958     }
1959
1960   if (rp->last_msg_id != clib_net_to_host_u16 (mp->last_msg_id))
1961     {
1962       clib_warning ("WARNING: traced plugin '%s' last message id %d not %d",
1963                     mp->plugin_name, clib_net_to_host_u16 (mp->last_msg_id),
1964                     rp->last_msg_id);
1965     }
1966 }
1967
1968 #define foreach_rpc_api_msg                     \
1969 _(RPC_CALL,rpc_call)                            \
1970 _(RPC_CALL_REPLY,rpc_call_reply)
1971
1972 #define foreach_plugin_trace_msg                \
1973 _(TRACE_PLUGIN_MSG_IDS,trace_plugin_msg_ids)
1974
1975 /*
1976  * Set the rpc callback at our earliest possible convenience.
1977  * This avoids ordering issues between thread_init() -> start_workers and
1978  * an init function which we could define here. If we ever intend to use
1979  * vlib all by itself, we can't create a link-time dependency on
1980  * an init function here and a typical "call foo_init first"
1981  * guitar lick.
1982  */
1983
1984 extern void *rpc_call_main_thread_cb_fn;
1985
1986 static clib_error_t *
1987 rpc_api_hookup (vlib_main_t * vm)
1988 {
1989   api_main_t *am = &api_main;
1990 #define _(N,n)                                                  \
1991     vl_msg_api_set_handlers(VL_API_##N, #n,                     \
1992                            vl_api_##n##_t_handler,              \
1993                            vl_noop_handler,                     \
1994                            vl_noop_handler,                     \
1995                            vl_api_##n##_t_print,                \
1996                            sizeof(vl_api_##n##_t), 0 /* do not trace */);
1997   foreach_rpc_api_msg;
1998 #undef _
1999
2000 #define _(N,n)                                                  \
2001     vl_msg_api_set_handlers(VL_API_##N, #n,                     \
2002                            vl_api_##n##_t_handler,              \
2003                            vl_noop_handler,                     \
2004                            vl_noop_handler,                     \
2005                            vl_api_##n##_t_print,                \
2006                            sizeof(vl_api_##n##_t), 1 /* do trace */);
2007   foreach_plugin_trace_msg;
2008 #undef _
2009
2010   /* No reason to halt the parade to create a trace record... */
2011   am->is_mp_safe[VL_API_TRACE_PLUGIN_MSG_IDS] = 1;
2012   rpc_call_main_thread_cb_fn = vl_api_rpc_call_main_thread;
2013   return 0;
2014 }
2015
2016 VLIB_API_INIT_FUNCTION (rpc_api_hookup);
2017
2018 typedef enum
2019 {
2020   DUMP,
2021   CUSTOM_DUMP,
2022   REPLAY,
2023   INITIALIZERS,
2024 } vl_api_replay_t;
2025
2026 u8 *
2027 format_vl_msg_api_trace_status (u8 * s, va_list * args)
2028 {
2029   api_main_t *am = va_arg (*args, api_main_t *);
2030   vl_api_trace_which_t which = va_arg (*args, vl_api_trace_which_t);
2031   vl_api_trace_t *tp;
2032   char *trace_name;
2033
2034   switch (which)
2035     {
2036     case VL_API_TRACE_TX:
2037       tp = am->tx_trace;
2038       trace_name = "TX trace";
2039       break;
2040
2041     case VL_API_TRACE_RX:
2042       tp = am->rx_trace;
2043       trace_name = "RX trace";
2044       break;
2045
2046     default:
2047       abort ();
2048     }
2049
2050   if (tp == 0)
2051     {
2052       s = format (s, "%s: not yet configured.\n", trace_name);
2053       return s;
2054     }
2055
2056   s = format (s, "%s: used %d of %d items, %s enabled, %s wrapped\n",
2057               trace_name, vec_len (tp->traces), tp->nitems,
2058               tp->enabled ? "is" : "is not", tp->wrapped ? "has" : "has not");
2059   return s;
2060 }
2061
2062 void vl_msg_api_custom_dump_configure (api_main_t * am)
2063   __attribute__ ((weak));
2064 void
2065 vl_msg_api_custom_dump_configure (api_main_t * am)
2066 {
2067 }
2068
2069 static void
2070 vl_msg_api_process_file (vlib_main_t * vm, u8 * filename,
2071                          u32 first_index, u32 last_index,
2072                          vl_api_replay_t which)
2073 {
2074   vl_api_trace_file_header_t *hp;
2075   int i, fd;
2076   struct stat statb;
2077   size_t file_size;
2078   u8 *msg;
2079   u8 endian_swap_needed = 0;
2080   api_main_t *am = &api_main;
2081   u8 *tmpbuf = 0;
2082   u32 nitems;
2083   void **saved_print_handlers = 0;
2084
2085   fd = open ((char *) filename, O_RDONLY);
2086
2087   if (fd < 0)
2088     {
2089       vlib_cli_output (vm, "Couldn't open %s\n", filename);
2090       return;
2091     }
2092
2093   if (fstat (fd, &statb) < 0)
2094     {
2095       vlib_cli_output (vm, "Couldn't stat %s\n", filename);
2096       close (fd);
2097       return;
2098     }
2099
2100   if (!(statb.st_mode & S_IFREG) || (statb.st_size < sizeof (*hp)))
2101     {
2102       vlib_cli_output (vm, "File not plausible: %s\n", filename);
2103       close (fd);
2104       return;
2105     }
2106
2107   file_size = statb.st_size;
2108   file_size = (file_size + 4095) & ~(4096);
2109
2110   hp = mmap (0, file_size, PROT_READ, MAP_PRIVATE, fd, 0);
2111
2112   if (hp == (vl_api_trace_file_header_t *) MAP_FAILED)
2113     {
2114       vlib_cli_output (vm, "mmap failed: %s\n", filename);
2115       close (fd);
2116       return;
2117     }
2118   close (fd);
2119
2120   if ((clib_arch_is_little_endian && hp->endian == VL_API_BIG_ENDIAN)
2121       || (clib_arch_is_big_endian && hp->endian == VL_API_LITTLE_ENDIAN))
2122     endian_swap_needed = 1;
2123
2124   if (endian_swap_needed)
2125     nitems = ntohl (hp->nitems);
2126   else
2127     nitems = hp->nitems;
2128
2129   if (last_index == (u32) ~ 0)
2130     {
2131       last_index = nitems - 1;
2132     }
2133
2134   if (first_index >= nitems || last_index >= nitems)
2135     {
2136       vlib_cli_output (vm, "Range (%d, %d) outside file range (0, %d)\n",
2137                        first_index, last_index, nitems - 1);
2138       munmap (hp, file_size);
2139       return;
2140     }
2141   if (hp->wrapped)
2142     vlib_cli_output (vm,
2143                      "Note: wrapped/incomplete trace, results may vary\n");
2144
2145   if (which == CUSTOM_DUMP)
2146     {
2147       saved_print_handlers = (void **) vec_dup (am->msg_print_handlers);
2148       vl_msg_api_custom_dump_configure (am);
2149     }
2150
2151
2152   msg = (u8 *) (hp + 1);
2153
2154   for (i = 0; i < first_index; i++)
2155     {
2156       trace_cfg_t *cfgp;
2157       int size;
2158       u16 msg_id;
2159
2160       size = clib_host_to_net_u32 (*(u32 *) msg);
2161       msg += sizeof (u32);
2162
2163       if (clib_arch_is_little_endian)
2164         msg_id = ntohs (*((u16 *) msg));
2165       else
2166         msg_id = *((u16 *) msg);
2167
2168       cfgp = am->api_trace_cfg + msg_id;
2169       if (!cfgp)
2170         {
2171           vlib_cli_output (vm, "Ugh: msg id %d no trace config\n", msg_id);
2172           munmap (hp, file_size);
2173           return;
2174         }
2175       msg += size;
2176     }
2177
2178   if (which == REPLAY)
2179     am->replay_in_progress = 1;
2180
2181   for (; i <= last_index; i++)
2182     {
2183       trace_cfg_t *cfgp;
2184       u16 *msg_idp;
2185       u16 msg_id;
2186       int size;
2187
2188       if (which == DUMP)
2189         vlib_cli_output (vm, "---------- trace %d -----------\n", i);
2190
2191       size = clib_host_to_net_u32 (*(u32 *) msg);
2192       msg += sizeof (u32);
2193
2194       if (clib_arch_is_little_endian)
2195         msg_id = ntohs (*((u16 *) msg));
2196       else
2197         msg_id = *((u16 *) msg);
2198
2199       cfgp = am->api_trace_cfg + msg_id;
2200       if (!cfgp)
2201         {
2202           vlib_cli_output (vm, "Ugh: msg id %d no trace config\n", msg_id);
2203           munmap (hp, file_size);
2204           vec_free (tmpbuf);
2205           am->replay_in_progress = 0;
2206           return;
2207         }
2208
2209       /* Copy the buffer (from the read-only mmap'ed file) */
2210       vec_validate (tmpbuf, size - 1 + sizeof (uword));
2211       clib_memcpy (tmpbuf + sizeof (uword), msg, size);
2212       memset (tmpbuf, 0xf, sizeof (uword));
2213
2214       /*
2215        * Endian swap if needed. All msg data is supposed to be
2216        * in network byte order. All msg handlers are supposed to
2217        * know that. The generic message dumpers don't know that.
2218        * One could fix apigen, I suppose.
2219        */
2220       if ((which == DUMP && clib_arch_is_little_endian) || endian_swap_needed)
2221         {
2222           void (*endian_fp) (void *);
2223           if (msg_id >= vec_len (am->msg_endian_handlers)
2224               || (am->msg_endian_handlers[msg_id] == 0))
2225             {
2226               vlib_cli_output (vm, "Ugh: msg id %d no endian swap\n", msg_id);
2227               munmap (hp, file_size);
2228               vec_free (tmpbuf);
2229               am->replay_in_progress = 0;
2230               return;
2231             }
2232           endian_fp = am->msg_endian_handlers[msg_id];
2233           (*endian_fp) (tmpbuf + sizeof (uword));
2234         }
2235
2236       /* msg_id always in network byte order */
2237       if (clib_arch_is_little_endian)
2238         {
2239           msg_idp = (u16 *) (tmpbuf + sizeof (uword));
2240           *msg_idp = msg_id;
2241         }
2242
2243       switch (which)
2244         {
2245         case CUSTOM_DUMP:
2246         case DUMP:
2247           if (msg_id < vec_len (am->msg_print_handlers) &&
2248               am->msg_print_handlers[msg_id])
2249             {
2250               u8 *(*print_fp) (void *, void *);
2251
2252               print_fp = (void *) am->msg_print_handlers[msg_id];
2253               (*print_fp) (tmpbuf + sizeof (uword), vm);
2254             }
2255           else
2256             {
2257               vlib_cli_output (vm, "Skipping msg id %d: no print fcn\n",
2258                                msg_id);
2259               break;
2260             }
2261           break;
2262
2263         case INITIALIZERS:
2264           if (msg_id < vec_len (am->msg_print_handlers) &&
2265               am->msg_print_handlers[msg_id])
2266             {
2267               u8 *s;
2268               int j;
2269               u8 *(*print_fp) (void *, void *);
2270
2271               print_fp = (void *) am->msg_print_handlers[msg_id];
2272
2273               vlib_cli_output (vm, "/*");
2274
2275               (*print_fp) (tmpbuf + sizeof (uword), vm);
2276               vlib_cli_output (vm, "*/\n");
2277
2278               s = format (0, "static u8 * vl_api_%s_%d[%d] = {",
2279                           am->msg_names[msg_id], i,
2280                           am->api_trace_cfg[msg_id].size);
2281
2282               for (j = 0; j < am->api_trace_cfg[msg_id].size; j++)
2283                 {
2284                   if ((j & 7) == 0)
2285                     s = format (s, "\n    ");
2286                   s = format (s, "0x%02x,", tmpbuf[sizeof (uword) + j]);
2287                 }
2288               s = format (s, "\n};\n%c", 0);
2289               vlib_cli_output (vm, (char *) s);
2290               vec_free (s);
2291             }
2292           break;
2293
2294         case REPLAY:
2295           if (msg_id < vec_len (am->msg_print_handlers) &&
2296               am->msg_print_handlers[msg_id] && cfgp->replay_enable)
2297             {
2298               void (*handler) (void *);
2299
2300               handler = (void *) am->msg_handlers[msg_id];
2301
2302               if (!am->is_mp_safe[msg_id])
2303                 vl_msg_api_barrier_sync ();
2304               (*handler) (tmpbuf + sizeof (uword));
2305               if (!am->is_mp_safe[msg_id])
2306                 vl_msg_api_barrier_release ();
2307             }
2308           else
2309             {
2310               if (cfgp->replay_enable)
2311                 vlib_cli_output (vm, "Skipping msg id %d: no handler\n",
2312                                  msg_id);
2313               break;
2314             }
2315           break;
2316         }
2317
2318       _vec_len (tmpbuf) = 0;
2319       msg += size;
2320     }
2321
2322   if (saved_print_handlers)
2323     {
2324       clib_memcpy (am->msg_print_handlers, saved_print_handlers,
2325                    vec_len (am->msg_print_handlers) * sizeof (void *));
2326       vec_free (saved_print_handlers);
2327     }
2328
2329   munmap (hp, file_size);
2330   vec_free (tmpbuf);
2331   am->replay_in_progress = 0;
2332 }
2333
2334 static clib_error_t *
2335 api_trace_command_fn (vlib_main_t * vm,
2336                       unformat_input_t * input, vlib_cli_command_t * cmd)
2337 {
2338   u32 nitems = 256 << 10;
2339   api_main_t *am = &api_main;
2340   vl_api_trace_which_t which = VL_API_TRACE_RX;
2341   u8 *filename;
2342   u32 first = 0;
2343   u32 last = (u32) ~ 0;
2344   FILE *fp;
2345   int rv;
2346
2347   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2348     {
2349       if (unformat (input, "on") || unformat (input, "enable"))
2350         {
2351           if (unformat (input, "nitems %d", &nitems))
2352             ;
2353           vl_msg_api_trace_configure (am, which, nitems);
2354           vl_msg_api_trace_onoff (am, which, 1 /* on */ );
2355         }
2356       else if (unformat (input, "off"))
2357         {
2358           vl_msg_api_trace_onoff (am, which, 0);
2359         }
2360       else if (unformat (input, "save %s", &filename))
2361         {
2362           u8 *chroot_filename;
2363           if (strstr ((char *) filename, "..")
2364               || index ((char *) filename, '/'))
2365             {
2366               vlib_cli_output (vm, "illegal characters in filename '%s'",
2367                                filename);
2368               return 0;
2369             }
2370
2371           chroot_filename = format (0, "/tmp/%s%c", filename, 0);
2372
2373           vec_free (filename);
2374
2375           fp = fopen ((char *) chroot_filename, "w");
2376           if (fp == NULL)
2377             {
2378               vlib_cli_output (vm, "Couldn't create %s\n", chroot_filename);
2379               return 0;
2380             }
2381           rv = vl_msg_api_trace_save (am, which, fp);
2382           fclose (fp);
2383           if (rv == -1)
2384             vlib_cli_output (vm, "API Trace data not present\n");
2385           else if (rv == -2)
2386             vlib_cli_output (vm, "File for writing is closed\n");
2387           else if (rv == -10)
2388             vlib_cli_output (vm, "Error while writing header to file\n");
2389           else if (rv == -11)
2390             vlib_cli_output (vm, "Error while writing trace to file\n");
2391           else if (rv == -12)
2392             vlib_cli_output (vm,
2393                              "Error while writing end of buffer trace to file\n");
2394           else if (rv == -13)
2395             vlib_cli_output (vm,
2396                              "Error while writing start of buffer trace to file\n");
2397           else if (rv < 0)
2398             vlib_cli_output (vm, "Unkown error while saving: %d", rv);
2399           else
2400             vlib_cli_output (vm, "API trace saved to %s\n", chroot_filename);
2401           vec_free (chroot_filename);
2402         }
2403       else if (unformat (input, "dump %s", &filename))
2404         {
2405           vl_msg_api_process_file (vm, filename, first, last, DUMP);
2406         }
2407       else if (unformat (input, "custom-dump %s", &filename))
2408         {
2409           vl_msg_api_process_file (vm, filename, first, last, CUSTOM_DUMP);
2410         }
2411       else if (unformat (input, "replay %s", &filename))
2412         {
2413           vl_msg_api_process_file (vm, filename, first, last, REPLAY);
2414         }
2415       else if (unformat (input, "initializers %s", &filename))
2416         {
2417           vl_msg_api_process_file (vm, filename, first, last, INITIALIZERS);
2418         }
2419       else if (unformat (input, "tx"))
2420         {
2421           which = VL_API_TRACE_TX;
2422         }
2423       else if (unformat (input, "first %d", &first))
2424         {
2425           ;
2426         }
2427       else if (unformat (input, "last %d", &last))
2428         {
2429           ;
2430         }
2431       else if (unformat (input, "status"))
2432         {
2433           vlib_cli_output (vm, "%U", format_vl_msg_api_trace_status,
2434                            am, which);
2435         }
2436       else if (unformat (input, "free"))
2437         {
2438           vl_msg_api_trace_onoff (am, which, 0);
2439           vl_msg_api_trace_free (am, which);
2440         }
2441       else if (unformat (input, "post-mortem-on"))
2442         vl_msg_api_post_mortem_dump_enable_disable (1 /* enable */ );
2443       else if (unformat (input, "post-mortem-off"))
2444         vl_msg_api_post_mortem_dump_enable_disable (0 /* enable */ );
2445       else
2446         return clib_error_return (0, "unknown input `%U'",
2447                                   format_unformat_error, input);
2448     }
2449   return 0;
2450 }
2451
2452 /*?
2453  * Display, replay, or save a binary API trace
2454 ?*/
2455
2456 /* *INDENT-OFF* */
2457 VLIB_CLI_COMMAND (api_trace_command, static) =
2458 {
2459   .path = "api trace",
2460   .short_help =
2461   "api trace [on|off][dump|save|replay <file>][status][free][post-mortem-on]",
2462   .function = api_trace_command_fn,
2463 };
2464 /* *INDENT-ON* */
2465
2466 static clib_error_t *
2467 api_config_fn (vlib_main_t * vm, unformat_input_t * input)
2468 {
2469   u32 nitems = 256 << 10;
2470   vl_api_trace_which_t which = VL_API_TRACE_RX;
2471   api_main_t *am = &api_main;
2472
2473   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2474     {
2475       if (unformat (input, "on") || unformat (input, "enable"))
2476         {
2477           if (unformat (input, "nitems %d", &nitems))
2478             ;
2479           vl_msg_api_trace_configure (am, which, nitems);
2480           vl_msg_api_trace_onoff (am, which, 1 /* on */ );
2481           vl_msg_api_post_mortem_dump_enable_disable (1 /* enable */ );
2482         }
2483       else if (unformat (input, "save-api-table %s",
2484                          &am->save_msg_table_filename))
2485         ;
2486       else
2487         return clib_error_return (0, "unknown input `%U'",
2488                                   format_unformat_error, input);
2489     }
2490   return 0;
2491 }
2492
2493 /*?
2494  * This module has three configuration parameters:
2495  * "on" or "enable" - enables binary api tracing
2496  * "nitems <nnn>" - sets the size of the circular buffer to <nnn>
2497  * "save-api-table <filename>" - dumps the API message table to /tmp/<filename>
2498 ?*/
2499 VLIB_CONFIG_FUNCTION (api_config_fn, "api-trace");
2500
2501 static clib_error_t *
2502 api_queue_config_fn (vlib_main_t * vm, unformat_input_t * input)
2503 {
2504   api_main_t *am = &api_main;
2505   u32 nitems;
2506
2507   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2508     {
2509       if (unformat (input, "length %d", &nitems) ||
2510           (unformat (input, "len %d", &nitems)))
2511         {
2512           if (nitems >= 1024)
2513             am->vlib_input_queue_length = nitems;
2514           else
2515             clib_warning ("vlib input queue length %d too small, ignored",
2516                           nitems);
2517         }
2518       else
2519         return clib_error_return (0, "unknown input `%U'",
2520                                   format_unformat_error, input);
2521     }
2522   return 0;
2523 }
2524
2525 VLIB_CONFIG_FUNCTION (api_queue_config_fn, "api-queue");
2526
2527 static u8 *
2528 extract_name (u8 * s)
2529 {
2530   u8 *rv;
2531
2532   rv = vec_dup (s);
2533
2534   while (vec_len (rv) && rv[vec_len (rv)] != '_')
2535     _vec_len (rv)--;
2536
2537   rv[vec_len (rv)] = 0;
2538
2539   return rv;
2540 }
2541
2542 static u8 *
2543 extract_crc (u8 * s)
2544 {
2545   int i;
2546   u8 *rv;
2547
2548   rv = vec_dup (s);
2549
2550   for (i = vec_len (rv) - 1; i >= 0; i--)
2551     {
2552       if (rv[i] == '_')
2553         {
2554           vec_delete (rv, i + 1, 0);
2555           break;
2556         }
2557     }
2558   return rv;
2559 }
2560
2561 typedef struct
2562 {
2563   u8 *name_and_crc;
2564   u8 *name;
2565   u8 *crc;
2566   u32 msg_index;
2567   int which;
2568 } msg_table_unserialize_t;
2569
2570 static int
2571 table_id_cmp (void *a1, void *a2)
2572 {
2573   msg_table_unserialize_t *n1 = a1;
2574   msg_table_unserialize_t *n2 = a2;
2575
2576   return (n1->msg_index - n2->msg_index);
2577 }
2578
2579 static int
2580 table_name_and_crc_cmp (void *a1, void *a2)
2581 {
2582   msg_table_unserialize_t *n1 = a1;
2583   msg_table_unserialize_t *n2 = a2;
2584
2585   return strcmp ((char *) n1->name_and_crc, (char *) n2->name_and_crc);
2586 }
2587
2588 static clib_error_t *
2589 dump_api_table_file_command_fn (vlib_main_t * vm,
2590                                 unformat_input_t * input,
2591                                 vlib_cli_command_t * cmd)
2592 {
2593   u8 *filename = 0;
2594   api_main_t *am = &api_main;
2595   serialize_main_t _sm, *sm = &_sm;
2596   clib_error_t *error;
2597   u32 nmsgs;
2598   u32 msg_index;
2599   u8 *name_and_crc;
2600   int compare_current = 0;
2601   int numeric_sort = 0;
2602   msg_table_unserialize_t *table = 0, *item;
2603   u32 i;
2604   u32 ndifferences = 0;
2605
2606   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2607     {
2608       if (unformat (input, "file %s", &filename))
2609         ;
2610       else if (unformat (input, "compare-current")
2611                || unformat (input, "compare"))
2612         compare_current = 1;
2613       else if (unformat (input, "numeric"))
2614         numeric_sort = 1;
2615       else
2616         return clib_error_return (0, "unknown input `%U'",
2617                                   format_unformat_error, input);
2618     }
2619
2620   if (numeric_sort && compare_current)
2621     return clib_error_return
2622       (0, "Comparison and numeric sorting are incompatible");
2623
2624   if (filename == 0)
2625     return clib_error_return (0, "File not specified");
2626
2627   /* Load the serialized message table from the table dump */
2628
2629   error = unserialize_open_clib_file (sm, (char *) filename);
2630
2631   if (error)
2632     return error;
2633
2634   unserialize_integer (sm, &nmsgs, sizeof (u32));
2635
2636   for (i = 0; i < nmsgs; i++)
2637     {
2638       msg_index = unserialize_likely_small_unsigned_integer (sm);
2639       unserialize_cstring (sm, (char **) &name_and_crc);
2640       vec_add2 (table, item, 1);
2641       item->msg_index = msg_index;
2642       item->name_and_crc = name_and_crc;
2643       item->name = extract_name (name_and_crc);
2644       item->crc = extract_crc (name_and_crc);
2645       item->which = 0;          /* file */
2646     }
2647   serialize_close (sm);
2648
2649   /* Compare with the current image? */
2650   if (compare_current)
2651     {
2652       /* Append the current message table */
2653       u8 *tblv = vl_api_serialize_message_table (am, 0);
2654
2655       serialize_open_vector (sm, tblv);
2656       unserialize_integer (sm, &nmsgs, sizeof (u32));
2657
2658       for (i = 0; i < nmsgs; i++)
2659         {
2660           msg_index = unserialize_likely_small_unsigned_integer (sm);
2661           unserialize_cstring (sm, (char **) &name_and_crc);
2662
2663           vec_add2 (table, item, 1);
2664           item->msg_index = msg_index;
2665           item->name_and_crc = name_and_crc;
2666           item->name = extract_name (name_and_crc);
2667           item->crc = extract_crc (name_and_crc);
2668           item->which = 1;      /* current_image */
2669         }
2670       vec_free (tblv);
2671     }
2672
2673   /* Sort the table. */
2674   if (numeric_sort)
2675     vec_sort_with_function (table, table_id_cmp);
2676   else
2677     vec_sort_with_function (table, table_name_and_crc_cmp);
2678
2679   if (compare_current)
2680     {
2681       ndifferences = 0;
2682
2683       /*
2684        * In this case, the recovered table will have two entries per
2685        * API message. So, if entries i and i+1 match, the message definitions
2686        * are identical. Otherwise, the crc is different, or a message is
2687        * present in only one of the tables.
2688        */
2689       vlib_cli_output (vm, "%=60s %s", "Message Name", "Result");
2690
2691       for (i = 0; i < vec_len (table);)
2692         {
2693           /* Last message lonely? */
2694           if (i == vec_len (table) - 1)
2695             {
2696               ndifferences++;
2697               goto last_unique;
2698             }
2699
2700           /* Identical pair? */
2701           if (!strncmp
2702               ((char *) table[i].name_and_crc,
2703                (char *) table[i + 1].name_and_crc,
2704                vec_len (table[i].name_and_crc)))
2705             {
2706               i += 2;
2707               continue;
2708             }
2709
2710           ndifferences++;
2711
2712           /* Only in one of two tables? */
2713           if (strncmp ((char *) table[i].name, (char *) table[i + 1].name,
2714                        vec_len (table[i].name)))
2715             {
2716             last_unique:
2717               vlib_cli_output (vm, "%-60s only in %s",
2718                                table[i].name, table[i].which ?
2719                                "image" : "file");
2720               i++;
2721               continue;
2722             }
2723           /* In both tables, but with different signatures */
2724           vlib_cli_output (vm, "%-60s definition changed", table[i].name);
2725           i += 2;
2726         }
2727       if (ndifferences == 0)
2728         vlib_cli_output (vm, "No api message signature differences found.");
2729       else
2730         vlib_cli_output (vm, "Found %u api message signature differences",
2731                          ndifferences);
2732       goto cleanup;
2733     }
2734
2735   /* Dump the table, sorted as shown above */
2736   vlib_cli_output (vm, "%=60s %=8s %=10s", "Message name", "MsgID", "CRC");
2737
2738   for (i = 0; i < vec_len (table); i++)
2739     {
2740       item = table + i;
2741       vlib_cli_output (vm, "%-60s %8u %10s", item->name,
2742                        item->msg_index, item->crc);
2743     }
2744
2745 cleanup:
2746   for (i = 0; i < vec_len (table); i++)
2747     {
2748       vec_free (table[i].name_and_crc);
2749       vec_free (table[i].name);
2750       vec_free (table[i].crc);
2751     }
2752
2753   vec_free (table);
2754
2755   return 0;
2756 }
2757
2758 /*?
2759  * Displays a serialized API message decode table, sorted by message name
2760  *
2761  * @cliexpar
2762  * @cliexstart{show api dump file <filename>}
2763  *                                                Message name    MsgID        CRC
2764  * accept_session                                                    407   8e2a127e
2765  * accept_session_reply                                              408   67d8c22a
2766  * add_node_next                                                     549   e4202993
2767  * add_node_next_reply                                               550   e89d6eed
2768  * etc.
2769  * @cliexend
2770 ?*/
2771
2772 /*?
2773  * Compares a serialized API message decode table with the current image
2774  *
2775  * @cliexpar
2776  * @cliexstart{show api dump file <filename> compare}
2777  * ip_add_del_route                                             definition changed
2778  * ip_table_add_del                                             definition changed
2779  * l2_macs_event                                                only in image
2780  * vnet_ip4_fib_counters                                        only in file
2781  * vnet_ip4_nbr_counters                                        only in file
2782  * @cliexend
2783 ?*/
2784
2785 /*?
2786  * Display a serialized API message decode table, compare a saved
2787  * decode table with the current image, to establish API differences.
2788  *
2789 ?*/
2790 /* *INDENT-OFF* */
2791 VLIB_CLI_COMMAND (dump_api_table_file, static) =
2792 {
2793   .path = "show api dump",
2794   .short_help = "show api dump file <filename> [numeric | compare-current]",
2795   .function = dump_api_table_file_command_fn,
2796 };
2797 /* *INDENT-ON* */
2798
2799 /*
2800  * fd.io coding-style-patch-verification: ON
2801  *
2802  * Local Variables:
2803  * eval: (c-set-style "gnu")
2804  * End:
2805  */