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