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