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