TCP proxy prototype
[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 #define TRACE_VLIB_MEMORY_QUEUE 0
42
43 #include <vlibmemory/vl_memory_msg_enum.h>      /* enumerate all vlib messages */
44
45 #define vl_typedefs             /* define message structures */
46 #include <vlibmemory/vl_memory_api_h.h>
47 #undef vl_typedefs
48
49 /* instantiate all the print functions we know about */
50 #define vl_print(handle, ...) vlib_cli_output (handle, __VA_ARGS__)
51 #define vl_printfun
52 #include <vlibmemory/vl_memory_api_h.h>
53 #undef vl_printfun
54
55 static inline void *
56 vl_api_memclnt_create_t_print (vl_api_memclnt_create_t * a, void *handle)
57 {
58   vl_print (handle, "vl_api_memclnt_create_t:\n");
59   vl_print (handle, "name: %s\n", a->name);
60   vl_print (handle, "input_queue: 0x%wx\n", a->input_queue);
61   vl_print (handle, "context: %u\n", (unsigned) a->context);
62   vl_print (handle, "ctx_quota: %ld\n", (long) a->ctx_quota);
63   return handle;
64 }
65
66 static inline void *
67 vl_api_memclnt_delete_t_print (vl_api_memclnt_delete_t * a, void *handle)
68 {
69   vl_print (handle, "vl_api_memclnt_delete_t:\n");
70   vl_print (handle, "index: %u\n", (unsigned) a->index);
71   vl_print (handle, "handle: 0x%wx\n", a->handle);
72   return handle;
73 }
74
75 static inline void *
76 vl_api_trace_plugin_msg_ids_t_print (vl_api_trace_plugin_msg_ids_t * a,
77                                      void *handle)
78 {
79   vl_print (handle, "vl_api_trace_plugin_msg_ids: %s first %u last %u\n",
80             a->plugin_name,
81             clib_host_to_net_u16 (a->first_msg_id),
82             clib_host_to_net_u16 (a->last_msg_id));
83   return handle;
84 }
85
86 /* instantiate all the endian swap functions we know about */
87 #define vl_endianfun
88 #include <vlibmemory/vl_memory_api_h.h>
89 #undef vl_endianfun
90
91 void vl_socket_api_send (vl_api_registration_t * rp, u8 * elem)
92   __attribute__ ((weak));
93
94 void
95 vl_socket_api_send (vl_api_registration_t * rp, u8 * elem)
96 {
97   static int count;
98
99   if (count++ < 5)
100     clib_warning ("need to link against -lvlibsocket, msg not sent!");
101 }
102
103 void
104 vl_msg_api_send (vl_api_registration_t * rp, u8 * elem)
105 {
106   if (PREDICT_FALSE (rp->registration_type > REGISTRATION_TYPE_SHMEM))
107     {
108       vl_socket_api_send (rp, elem);
109     }
110   else
111     {
112       vl_msg_api_send_shmem (rp->vl_input_queue, elem);
113     }
114 }
115
116 u8 *
117 vl_api_serialize_message_table (api_main_t * am, u8 * vector)
118 {
119   serialize_main_t _sm, *sm = &_sm;
120   hash_pair_t *hp;
121   u32 nmsg = hash_elts (am->msg_index_by_name_and_crc);
122
123   serialize_open_vector (sm, vector);
124
125   /* serialize the count */
126   serialize_integer (sm, nmsg, sizeof (u32));
127
128   /* *INDENT-OFF* */
129   hash_foreach_pair (hp, am->msg_index_by_name_and_crc,
130   ({
131     serialize_likely_small_unsigned_integer (sm, hp->value[0]);
132     serialize_cstring (sm, (char *) hp->key);
133   }));
134   /* *INDENT-ON* */
135
136   return serialize_close_vector (sm);
137 }
138
139 /*
140  * vl_api_memclnt_create_internal
141  */
142
143 u32
144 vl_api_memclnt_create_internal (char *name, unix_shared_memory_queue_t * q)
145 {
146   vl_api_registration_t **regpp;
147   vl_api_registration_t *regp;
148   svm_region_t *svm;
149   void *oldheap;
150   api_main_t *am = &api_main;
151
152   ASSERT (vlib_get_thread_index () == 0);
153   pool_get (am->vl_clients, regpp);
154
155   svm = am->vlib_rp;
156
157   pthread_mutex_lock (&svm->mutex);
158   oldheap = svm_push_data_heap (svm);
159   *regpp = clib_mem_alloc (sizeof (vl_api_registration_t));
160
161   regp = *regpp;
162   memset (regp, 0, sizeof (*regp));
163   regp->registration_type = REGISTRATION_TYPE_SHMEM;
164   regp->vl_api_registration_pool_index = regpp - am->vl_clients;
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 = 0;
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   if (am->serialized_message_table_in_shmem == 0)
224     serialized_message_table = vl_api_serialize_message_table (am, 0);
225
226   pthread_mutex_lock (&svm->mutex);
227   oldheap = svm_push_data_heap (svm);
228   *regpp = clib_mem_alloc (sizeof (vl_api_registration_t));
229
230   regp = *regpp;
231   memset (regp, 0, sizeof (*regp));
232   regp->registration_type = REGISTRATION_TYPE_SHMEM;
233   regp->vl_api_registration_pool_index = regpp - am->vl_clients;
234
235   q = regp->vl_input_queue = (unix_shared_memory_queue_t *) (uword)
236     mp->input_queue;
237
238   regp->name = format (0, "%s", mp->name);
239   vec_add1 (regp->name, 0);
240   if (serialized_message_table)
241     am->serialized_message_table_in_shmem =
242       vec_dup (serialized_message_table);
243
244   pthread_mutex_unlock (&svm->mutex);
245   svm_pop_heap (oldheap);
246
247   vec_free (serialized_message_table);
248
249   rp = vl_msg_api_alloc (sizeof (*rp));
250   rp->_vl_msg_id = ntohs (VL_API_MEMCLNT_CREATE_REPLY);
251   rp->handle = (uword) regp;
252   rp->index = vl_msg_api_handle_from_index_and_epoch
253     (regp->vl_api_registration_pool_index,
254      am->shmem_hdr->application_restarts);
255   rp->context = mp->context;
256   rp->response = ntohl (rv);
257   rp->message_table =
258     pointer_to_uword (am->serialized_message_table_in_shmem);
259
260   vl_msg_api_send_shmem (q, (u8 *) & rp);
261 }
262
263 static int
264 call_reaper_functions (u32 client_index)
265 {
266   clib_error_t *error = 0;
267   _vl_msg_api_function_list_elt_t *i;
268
269   i = api_main.reaper_function_registrations;
270   while (i)
271     {
272       error = i->f (client_index);
273       if (error)
274         clib_error_report (error);
275       i = i->next_init_function;
276     }
277   return 0;
278 }
279
280 /*
281  * vl_api_memclnt_delete_t_handler
282  */
283 void
284 vl_api_memclnt_delete_t_handler (vl_api_memclnt_delete_t * mp)
285 {
286   vl_api_registration_t **regpp;
287   vl_api_registration_t *regp;
288   vl_api_memclnt_delete_reply_t *rp;
289   svm_region_t *svm;
290   void *oldheap;
291   api_main_t *am = &api_main;
292   u32 handle, client_index, epoch;
293
294   handle = mp->index;
295
296   if (call_reaper_functions (handle))
297     return;
298
299   epoch = vl_msg_api_handle_get_epoch (handle);
300   client_index = vl_msg_api_handle_get_index (handle);
301
302   if (epoch != (am->shmem_hdr->application_restarts & VL_API_EPOCH_MASK))
303     {
304       clib_warning
305         ("Stale clnt delete index %d old epoch %d cur epoch %d",
306          client_index, epoch,
307          (am->shmem_hdr->application_restarts & VL_API_EPOCH_MASK));
308       return;
309     }
310
311   regpp = am->vl_clients + client_index;
312
313   if (!pool_is_free (am->vl_clients, regpp))
314     {
315       regp = *regpp;
316       svm = am->vlib_rp;
317
318       /* $$$ check the input queue for e.g. punted sf's */
319
320       rp = vl_msg_api_alloc (sizeof (*rp));
321       rp->_vl_msg_id = ntohs (VL_API_MEMCLNT_DELETE_REPLY);
322       rp->handle = mp->handle;
323       rp->response = 1;
324
325       vl_msg_api_send_shmem (regp->vl_input_queue, (u8 *) & rp);
326
327       if (client_index != regp->vl_api_registration_pool_index)
328         {
329           clib_warning ("mismatch client_index %d pool_index %d",
330                         client_index, regp->vl_api_registration_pool_index);
331           vl_msg_api_free (rp);
332           return;
333         }
334
335       /* No dangling references, please */
336       *regpp = 0;
337
338       pool_put_index (am->vl_clients, regp->vl_api_registration_pool_index);
339
340       pthread_mutex_lock (&svm->mutex);
341       oldheap = svm_push_data_heap (svm);
342       /* Poison the old registration */
343       memset (regp, 0xF1, sizeof (*regp));
344       clib_mem_free (regp);
345       pthread_mutex_unlock (&svm->mutex);
346       svm_pop_heap (oldheap);
347     }
348   else
349     {
350       clib_warning ("unknown client ID %d", mp->index);
351     }
352 }
353
354 void
355 vl_api_get_first_msg_id_t_handler (vl_api_get_first_msg_id_t * mp)
356 {
357   vl_api_get_first_msg_id_reply_t *rmp;
358   unix_shared_memory_queue_t *q;
359   uword *p;
360   api_main_t *am = &api_main;
361   vl_api_msg_range_t *rp;
362   u8 name[64];
363   u16 first_msg_id = ~0;
364   int rv = -7;                  /* VNET_API_ERROR_INVALID_VALUE */
365
366   q = vl_api_client_index_to_input_queue (mp->client_index);
367   if (!q)
368     return;
369
370   if (am->msg_range_by_name == 0)
371     goto out;
372
373   strncpy ((char *) name, (char *) mp->name, ARRAY_LEN (name) - 1);
374
375   p = hash_get_mem (am->msg_range_by_name, name);
376   if (p == 0)
377     goto out;
378
379   rp = vec_elt_at_index (am->msg_ranges, p[0]);
380
381   first_msg_id = rp->first_msg_id;
382   rv = 0;
383
384 out:
385
386   rmp = vl_msg_api_alloc (sizeof (*rmp));
387   rmp->_vl_msg_id = ntohs (VL_API_GET_FIRST_MSG_ID_REPLY);
388   rmp->context = mp->context;
389   rmp->retval = ntohl (rv);
390   rmp->first_msg_id = ntohs (first_msg_id);
391   vl_msg_api_send_shmem (q, (u8 *) & rmp);
392 }
393
394 #define foreach_vlib_api_msg                    \
395 _(MEMCLNT_CREATE, memclnt_create)               \
396 _(MEMCLNT_DELETE, memclnt_delete)               \
397 _(GET_FIRST_MSG_ID, get_first_msg_id)
398
399 /*
400  * vl_api_init
401  */
402 static int
403 memory_api_init (const char *region_name)
404 {
405   int rv;
406   vl_msg_api_msg_config_t cfg;
407   vl_msg_api_msg_config_t *c = &cfg;
408
409   memset (c, 0, sizeof (*c));
410
411   if ((rv = vl_map_shmem (region_name, 1 /* is_vlib */ )) < 0)
412     return rv;
413
414 #define _(N,n) do {                                             \
415     c->id = VL_API_##N;                                         \
416     c->name = #n;                                               \
417     c->handler = vl_api_##n##_t_handler;                        \
418     c->cleanup = vl_noop_handler;                               \
419     c->endian = vl_api_##n##_t_endian;                          \
420     c->print = vl_api_##n##_t_print;                            \
421     c->size = sizeof(vl_api_##n##_t);                           \
422     c->traced = 1; /* trace, so these msgs print */             \
423     c->replay = 0; /* don't replay client create/delete msgs */ \
424     c->message_bounce = 0; /* don't bounce this message */      \
425     vl_msg_api_config(c);} while (0);
426
427   foreach_vlib_api_msg;
428 #undef _
429
430   return 0;
431 }
432
433 #define foreach_histogram_bucket                \
434 _(400)                                          \
435 _(200)                                          \
436 _(100)                                          \
437 _(10)
438
439 typedef enum
440 {
441 #define _(n) SLEEP_##n##_US,
442   foreach_histogram_bucket
443 #undef _
444     SLEEP_N_BUCKETS,
445 } histogram_index_t;
446
447 static u64 vector_rate_histogram[SLEEP_N_BUCKETS];
448
449 static void memclnt_queue_callback (vlib_main_t * vm);
450
451 /*
452  * Callback to send ourselves a plugin numbering-space trace msg
453  */
454 static void
455 send_one_plugin_msg_ids_msg (u8 * name, u16 first_msg_id, u16 last_msg_id)
456 {
457   vl_api_trace_plugin_msg_ids_t *mp;
458   api_main_t *am = &api_main;
459   vl_shmem_hdr_t *shmem_hdr = am->shmem_hdr;
460   unix_shared_memory_queue_t *q;
461
462   mp = vl_msg_api_alloc_as_if_client (sizeof (*mp));
463   memset (mp, 0, sizeof (*mp));
464
465   mp->_vl_msg_id = clib_host_to_net_u16 (VL_API_TRACE_PLUGIN_MSG_IDS);
466   strncpy ((char *) mp->plugin_name, (char *) name,
467            sizeof (mp->plugin_name) - 1);
468   mp->first_msg_id = clib_host_to_net_u16 (first_msg_id);
469   mp->last_msg_id = clib_host_to_net_u16 (last_msg_id);
470
471   q = shmem_hdr->vl_input_queue;
472
473   vl_msg_api_send_shmem (q, (u8 *) & mp);
474 }
475
476 static uword
477 memclnt_process (vlib_main_t * vm,
478                  vlib_node_runtime_t * node, vlib_frame_t * f)
479 {
480   uword mp;
481   vl_shmem_hdr_t *shm;
482   unix_shared_memory_queue_t *q;
483   clib_error_t *e;
484   int rv;
485   api_main_t *am = &api_main;
486   f64 dead_client_scan_time;
487   f64 sleep_time, start_time;
488   f64 vector_rate;
489   int i;
490
491   vlib_set_queue_signal_callback (vm, memclnt_queue_callback);
492
493   if ((rv = memory_api_init (am->region_name)) < 0)
494     {
495       clib_warning ("memory_api_init returned %d, wait for godot...", rv);
496       vlib_process_suspend (vm, 1e70);
497     }
498
499   shm = am->shmem_hdr;
500   ASSERT (shm);
501   q = shm->vl_input_queue;
502   ASSERT (q);
503
504   e = vlib_call_init_exit_functions
505     (vm, vm->api_init_function_registrations, 1 /* call_once */ );
506   if (e)
507     clib_error_report (e);
508
509   sleep_time = 20.0;
510   dead_client_scan_time = vlib_time_now (vm) + 20.0;
511
512   /*
513    * Send plugin message range messages for each plugin we loaded
514    */
515   for (i = 0; i < vec_len (am->msg_ranges); i++)
516     {
517       vl_api_msg_range_t *rp = am->msg_ranges + i;
518       send_one_plugin_msg_ids_msg (rp->name, rp->first_msg_id,
519                                    rp->last_msg_id);
520     }
521
522   /* $$$ pay attention to frame size, control CPU usage */
523   while (1)
524     {
525       uword event_type __attribute__ ((unused));
526       i8 *headp;
527       int need_broadcast;
528
529       /*
530        * There's a reason for checking the queue before
531        * sleeping. If the vlib application crashes, it's entirely
532        * possible for a client to enqueue a connect request
533        * during the process restart interval.
534        *
535        * Unless some force of physics causes the new incarnation
536        * of the application to process the request, the client will
537        * sit and wait for Godot...
538        */
539       vector_rate = vlib_last_vector_length_per_node (vm);
540       start_time = vlib_time_now (vm);
541       while (1)
542         {
543           pthread_mutex_lock (&q->mutex);
544           if (q->cursize == 0)
545             {
546               vm->api_queue_nonempty = 0;
547               pthread_mutex_unlock (&q->mutex);
548
549               if (TRACE_VLIB_MEMORY_QUEUE)
550                 {
551                   /* *INDENT-OFF* */
552                   ELOG_TYPE_DECLARE (e) =
553                     {
554                       .format = "q-underflow: len %d",
555                       .format_args = "i4",
556                     };
557                   /* *INDENT-ON* */
558                   struct
559                   {
560                     u32 len;
561                   } *ed;
562                   ed = ELOG_DATA (&vm->elog_main, e);
563                   ed->len = 0;
564                 }
565               sleep_time = 20.0;
566               break;
567             }
568
569           headp = (i8 *) (q->data + sizeof (uword) * q->head);
570           clib_memcpy (&mp, headp, sizeof (uword));
571
572           q->head++;
573           need_broadcast = (q->cursize == q->maxsize / 2);
574           q->cursize--;
575
576           if (PREDICT_FALSE (q->head == q->maxsize))
577             q->head = 0;
578           pthread_mutex_unlock (&q->mutex);
579           if (need_broadcast)
580             (void) pthread_cond_broadcast (&q->condvar);
581
582           vl_msg_api_handler_with_vm_node (am, (void *) mp, vm, node);
583
584           /* Allow no more than 10us without a pause */
585           if (vlib_time_now (vm) > start_time + 10e-6)
586             {
587               int index = SLEEP_400_US;
588               if (vector_rate > 40.0)
589                 sleep_time = 400e-6;
590               else if (vector_rate > 20.0)
591                 {
592                   index = SLEEP_200_US;
593                   sleep_time = 200e-6;
594                 }
595               else if (vector_rate >= 1.0)
596                 {
597                   index = SLEEP_100_US;
598                   sleep_time = 100e-6;
599                 }
600               else
601                 {
602                   index = SLEEP_10_US;
603                   sleep_time = 10e-6;
604                 }
605               vector_rate_histogram[index] += 1;
606               break;
607             }
608         }
609
610       event_type = vlib_process_wait_for_event_or_clock (vm, sleep_time);
611       vm->queue_signal_pending = 0;
612       vlib_process_get_events (vm, 0 /* event_data */ );
613
614       if (vlib_time_now (vm) > dead_client_scan_time)
615         {
616           vl_api_registration_t **regpp;
617           vl_api_registration_t *regp;
618           unix_shared_memory_queue_t *q;
619           static u32 *dead_indices;
620           static u32 *confused_indices;
621
622           vec_reset_length (dead_indices);
623           vec_reset_length (confused_indices);
624
625           /* *INDENT-OFF* */
626           pool_foreach (regpp, am->vl_clients,
627           ({
628             regp = *regpp;
629             if (regp)
630               {
631                 q = regp->vl_input_queue;
632                 if (kill (q->consumer_pid, 0) < 0)
633                   {
634                     vec_add1(dead_indices, regpp - am->vl_clients);
635                   }
636               }
637             else
638               {
639                 clib_warning ("NULL client registration index %d",
640                               regpp - am->vl_clients);
641                 vec_add1 (confused_indices, regpp - am->vl_clients);
642               }
643           }));
644           /* *INDENT-ON* */
645           /* This should "never happen," but if it does, fix it... */
646           if (PREDICT_FALSE (vec_len (confused_indices) > 0))
647             {
648               int i;
649               for (i = 0; i < vec_len (confused_indices); i++)
650                 {
651                   pool_put_index (am->vl_clients, confused_indices[i]);
652                 }
653             }
654
655           if (PREDICT_FALSE (vec_len (dead_indices) > 0))
656             {
657               int i;
658               svm_region_t *svm;
659               void *oldheap;
660
661               /* Allow the application to clean up its registrations */
662               for (i = 0; i < vec_len (dead_indices); i++)
663                 {
664                   regpp = pool_elt_at_index (am->vl_clients, dead_indices[i]);
665                   if (regpp)
666                     {
667                       u32 handle;
668
669                       handle = vl_msg_api_handle_from_index_and_epoch
670                         (dead_indices[i], shm->application_restarts);
671                       (void) call_reaper_functions (handle);
672                     }
673                 }
674
675               svm = am->vlib_rp;
676               pthread_mutex_lock (&svm->mutex);
677               oldheap = svm_push_data_heap (svm);
678
679               for (i = 0; i < vec_len (dead_indices); i++)
680                 {
681                   regpp = pool_elt_at_index (am->vl_clients, dead_indices[i]);
682                   if (regpp)
683                     {
684                       /* Poison the old registration */
685                       memset (*regpp, 0xF3, sizeof (**regpp));
686                       clib_mem_free (*regpp);
687                       /* no dangling references, please */
688                       *regpp = 0;
689                     }
690                   else
691                     {
692                       svm_pop_heap (oldheap);
693                       clib_warning ("Duplicate free, client index %d",
694                                     regpp - am->vl_clients);
695                       oldheap = svm_push_data_heap (svm);
696                     }
697                 }
698
699               svm_client_scan_this_region_nolock (am->vlib_rp);
700
701               pthread_mutex_unlock (&svm->mutex);
702               svm_pop_heap (oldheap);
703               for (i = 0; i < vec_len (dead_indices); i++)
704                 pool_put_index (am->vl_clients, dead_indices[i]);
705             }
706
707           dead_client_scan_time = vlib_time_now (vm) + 20.0;
708         }
709
710       if (TRACE_VLIB_MEMORY_QUEUE)
711         {
712           /* *INDENT-OFF* */
713           ELOG_TYPE_DECLARE (e) = {
714             .format = "q-awake: len %d",
715             .format_args = "i4",
716           };
717           /* *INDENT-ON* */
718           struct
719           {
720             u32 len;
721           } *ed;
722           ed = ELOG_DATA (&vm->elog_main, e);
723           ed->len = q->cursize;
724         }
725     }
726
727   return 0;
728 }
729
730 static clib_error_t *
731 vl_api_show_histogram_command (vlib_main_t * vm,
732                                unformat_input_t * input,
733                                vlib_cli_command_t * cli_cmd)
734 {
735   u64 total_counts = 0;
736   int i;
737
738   for (i = 0; i < SLEEP_N_BUCKETS; i++)
739     {
740       total_counts += vector_rate_histogram[i];
741     }
742
743   if (total_counts == 0)
744     {
745       vlib_cli_output (vm, "No control-plane activity.");
746       return 0;
747     }
748
749 #define _(n)                                                    \
750     do {                                                        \
751         f64 percent;                                            \
752         percent = ((f64) vector_rate_histogram[SLEEP_##n##_US]) \
753             / (f64) total_counts;                               \
754         percent *= 100.0;                                       \
755         vlib_cli_output (vm, "Sleep %3d us: %llu, %.2f%%",n,    \
756                          vector_rate_histogram[SLEEP_##n##_US], \
757                          percent);                              \
758     } while (0);
759   foreach_histogram_bucket;
760 #undef _
761
762   return 0;
763 }
764
765 /* *INDENT-OFF* */
766 VLIB_CLI_COMMAND (cli_show_api_histogram_command, static) = {
767     .path = "show api histogram",
768     .short_help = "show api histogram",
769     .function = vl_api_show_histogram_command,
770 };
771 /* *INDENT-ON* */
772
773 static clib_error_t *
774 vl_api_clear_histogram_command (vlib_main_t * vm,
775                                 unformat_input_t * input,
776                                 vlib_cli_command_t * cli_cmd)
777 {
778   int i;
779
780   for (i = 0; i < SLEEP_N_BUCKETS; i++)
781     vector_rate_histogram[i] = 0;
782   return 0;
783 }
784
785 /* *INDENT-OFF* */
786 VLIB_CLI_COMMAND (cli_clear_api_histogram_command, static) = {
787     .path = "clear api histogram",
788     .short_help = "clear api histogram",
789     .function = vl_api_clear_histogram_command,
790 };
791 /* *INDENT-ON* */
792
793
794 /* *INDENT-OFF* */
795 VLIB_REGISTER_NODE (memclnt_node,static) = {
796     .function = memclnt_process,
797     .type = VLIB_NODE_TYPE_PROCESS,
798     .name = "api-rx-from-ring",
799     .state = VLIB_NODE_STATE_DISABLED,
800 };
801 /* *INDENT-ON* */
802
803 static void
804 memclnt_queue_callback (vlib_main_t * vm)
805 {
806   static volatile int *cursizep;
807
808   if (PREDICT_FALSE (cursizep == 0))
809     {
810       api_main_t *am = &api_main;
811       vl_shmem_hdr_t *shmem_hdr = am->shmem_hdr;
812       unix_shared_memory_queue_t *q;
813
814       if (shmem_hdr == 0)
815         return;
816
817       q = shmem_hdr->vl_input_queue;
818       if (q == 0)
819         return;
820       cursizep = &q->cursize;
821     }
822
823   if (*cursizep >= 1)
824     {
825       vm->queue_signal_pending = 1;
826       vm->api_queue_nonempty = 1;
827       vlib_process_signal_event (vm, memclnt_node.index,
828                                  /* event_type */ 0, /* event_data */ 0);
829     }
830 }
831
832 void
833 vl_enable_disable_memory_api (vlib_main_t * vm, int enable)
834 {
835   vlib_node_set_state (vm, memclnt_node.index,
836                        (enable
837                         ? VLIB_NODE_STATE_POLLING
838                         : VLIB_NODE_STATE_DISABLED));
839 }
840
841 static uword
842 api_rx_from_node (vlib_main_t * vm,
843                   vlib_node_runtime_t * node, vlib_frame_t * frame)
844 {
845   uword n_packets = frame->n_vectors;
846   uword n_left_from;
847   u32 *from;
848   static u8 *long_msg;
849
850   vec_validate (long_msg, 4095);
851   n_left_from = frame->n_vectors;
852   from = vlib_frame_args (frame);
853
854   while (n_left_from > 0)
855     {
856       u32 bi0;
857       vlib_buffer_t *b0;
858       void *msg;
859       uword msg_len;
860
861       bi0 = from[0];
862       b0 = vlib_get_buffer (vm, bi0);
863       from += 1;
864       n_left_from -= 1;
865
866       msg = b0->data + b0->current_data;
867       msg_len = b0->current_length;
868       if (b0->flags & VLIB_BUFFER_NEXT_PRESENT)
869         {
870           ASSERT (long_msg != 0);
871           _vec_len (long_msg) = 0;
872           vec_add (long_msg, msg, msg_len);
873           while (b0->flags & VLIB_BUFFER_NEXT_PRESENT)
874             {
875               b0 = vlib_get_buffer (vm, b0->next_buffer);
876               msg = b0->data + b0->current_data;
877               msg_len = b0->current_length;
878               vec_add (long_msg, msg, msg_len);
879             }
880           msg = long_msg;
881         }
882       vl_msg_api_handler_no_trace_no_free (msg);
883     }
884
885   /* Free what we've been given. */
886   vlib_buffer_free (vm, vlib_frame_args (frame), n_packets);
887
888   return n_packets;
889 }
890
891 /* *INDENT-OFF* */
892 VLIB_REGISTER_NODE (api_rx_from_node_node,static) = {
893     .function = api_rx_from_node,
894     .type = VLIB_NODE_TYPE_INTERNAL,
895     .vector_size = 4,
896     .name = "api-rx-from-node",
897 };
898 /* *INDENT-ON* */
899
900 static clib_error_t *
901 setup_memclnt_exit (vlib_main_t * vm)
902 {
903   atexit (vl_unmap_shmem);
904   return 0;
905 }
906
907 VLIB_INIT_FUNCTION (setup_memclnt_exit);
908
909
910 static clib_error_t *
911 vl_api_ring_command (vlib_main_t * vm,
912                      unformat_input_t * input, vlib_cli_command_t * cli_cmd)
913 {
914   int i;
915   ring_alloc_t *ap;
916   vl_shmem_hdr_t *shmem_hdr;
917   api_main_t *am = &api_main;
918
919   shmem_hdr = am->shmem_hdr;
920
921   if (shmem_hdr == 0)
922     {
923       vlib_cli_output (vm, "Shared memory segment not initialized...\n");
924       return 0;
925     }
926
927   vlib_cli_output (vm, "%8s %8s %8s %8s %8s\n",
928                    "Owner", "Size", "Nitems", "Hits", "Misses");
929
930   ap = shmem_hdr->vl_rings;
931
932   for (i = 0; i < vec_len (shmem_hdr->vl_rings); i++)
933     {
934       vlib_cli_output (vm, "%8s %8d %8d %8d %8d\n",
935                        "vlib", ap->size, ap->nitems, ap->hits, ap->misses);
936       ap++;
937     }
938
939   ap = shmem_hdr->client_rings;
940
941   for (i = 0; i < vec_len (shmem_hdr->client_rings); i++)
942     {
943       vlib_cli_output (vm, "%8s %8d %8d %8d %8d\n",
944                        "clnt", ap->size, ap->nitems, ap->hits, ap->misses);
945       ap++;
946     }
947
948   vlib_cli_output (vm, "%d ring miss fallback allocations\n",
949                    am->ring_misses);
950
951   vlib_cli_output
952     (vm, "%d application restarts, %d reclaimed msgs, %d garbage collects\n",
953      shmem_hdr->application_restarts,
954      shmem_hdr->restart_reclaims, shmem_hdr->garbage_collects);
955   return 0;
956 }
957
958 void dump_socket_clients (vlib_main_t * vm, api_main_t * am)
959   __attribute__ ((weak));
960
961 void
962 dump_socket_clients (vlib_main_t * vm, api_main_t * am)
963 {
964 }
965
966 static clib_error_t *
967 vl_api_client_command (vlib_main_t * vm,
968                        unformat_input_t * input, vlib_cli_command_t * cli_cmd)
969 {
970   vl_api_registration_t **regpp, *regp;
971   unix_shared_memory_queue_t *q;
972   char *health;
973   api_main_t *am = &api_main;
974   u32 *confused_indices = 0;
975
976   if (!pool_elts (am->vl_clients))
977     goto socket_clients;
978   vlib_cli_output (vm, "Shared memory clients");
979   vlib_cli_output (vm, "%16s %8s %14s %18s %s",
980                    "Name", "PID", "Queue Length", "Queue VA", "Health");
981
982   /* *INDENT-OFF* */
983   pool_foreach (regpp, am->vl_clients,
984   ({
985     regp = *regpp;
986
987     if (regp)
988       {
989         q = regp->vl_input_queue;
990         if (kill (q->consumer_pid, 0) < 0)
991           {
992             health = "DEAD";
993           }
994         else
995           {
996             health = "alive";
997           }
998         vlib_cli_output (vm, "%16s %8d %14d 0x%016llx %s\n",
999                          regp->name, q->consumer_pid, q->cursize,
1000                          q, health);
1001       }
1002     else
1003       {
1004         clib_warning ("NULL client registration index %d",
1005                       regpp - am->vl_clients);
1006         vec_add1 (confused_indices, regpp - am->vl_clients);
1007       }
1008   }));
1009   /* *INDENT-ON* */
1010
1011   /* This should "never happen," but if it does, fix it... */
1012   if (PREDICT_FALSE (vec_len (confused_indices) > 0))
1013     {
1014       int i;
1015       for (i = 0; i < vec_len (confused_indices); i++)
1016         {
1017           pool_put_index (am->vl_clients, confused_indices[i]);
1018         }
1019     }
1020   vec_free (confused_indices);
1021
1022   if (am->missing_clients)
1023     vlib_cli_output (vm, "%u messages with missing clients",
1024                      am->missing_clients);
1025 socket_clients:
1026   dump_socket_clients (vm, am);
1027
1028   return 0;
1029 }
1030
1031 static clib_error_t *
1032 vl_api_status_command (vlib_main_t * vm,
1033                        unformat_input_t * input, vlib_cli_command_t * cli_cmd)
1034 {
1035   api_main_t *am = &api_main;
1036
1037   // check if rx_trace and tx_trace are not null pointers
1038
1039   if (am->rx_trace == 0)
1040     {
1041       vlib_cli_output (vm, "RX Trace disabled\n");
1042     }
1043   else
1044     {
1045       if (am->rx_trace->enabled == 0)
1046         vlib_cli_output (vm, "RX Trace disabled\n");
1047       else
1048         vlib_cli_output (vm, "RX Trace enabled\n");
1049     }
1050
1051   if (am->tx_trace == 0)
1052     {
1053       vlib_cli_output (vm, "TX Trace disabled\n");
1054     }
1055   else
1056     {
1057       if (am->tx_trace->enabled == 0)
1058         vlib_cli_output (vm, "TX Trace disabled\n");
1059       else
1060         vlib_cli_output (vm, "TX Trace enabled\n");
1061     }
1062
1063   return 0;
1064 }
1065
1066 /* *INDENT-OFF* */
1067 VLIB_CLI_COMMAND (cli_show_api_command, static) = {
1068     .path = "show api",
1069     .short_help = "Show API information",
1070 };
1071 /* *INDENT-ON* */
1072
1073 /* *INDENT-OFF* */
1074 VLIB_CLI_COMMAND (cli_show_api_ring_command, static) = {
1075     .path = "show api ring-stats",
1076     .short_help = "Message ring statistics",
1077     .function = vl_api_ring_command,
1078 };
1079 /* *INDENT-ON* */
1080
1081 /* *INDENT-OFF* */
1082 VLIB_CLI_COMMAND (cli_show_api_clients_command, static) = {
1083     .path = "show api clients",
1084     .short_help = "Client information",
1085     .function = vl_api_client_command,
1086 };
1087 /* *INDENT-ON* */
1088
1089 /* *INDENT-OFF* */
1090 VLIB_CLI_COMMAND (cli_show_api_status_command, static) = {
1091     .path = "show api status",
1092     .short_help = "Show API trace status",
1093     .function = vl_api_status_command,
1094 };
1095 /* *INDENT-ON* */
1096
1097 static clib_error_t *
1098 vl_api_message_table_command (vlib_main_t * vm,
1099                               unformat_input_t * input,
1100                               vlib_cli_command_t * cli_cmd)
1101 {
1102   api_main_t *am = &api_main;
1103   int i;
1104   int verbose = 0;
1105
1106   if (unformat (input, "verbose"))
1107     verbose = 1;
1108
1109
1110   if (verbose == 0)
1111     vlib_cli_output (vm, "%-4s %s", "ID", "Name");
1112   else
1113     vlib_cli_output (vm, "%-4s %-40s %6s %7s", "ID", "Name", "Bounce",
1114                      "MP-safe");
1115
1116   for (i = 1; i < vec_len (am->msg_names); i++)
1117     {
1118       if (verbose == 0)
1119         {
1120           vlib_cli_output (vm, "%-4d %s", i,
1121                            am->msg_names[i] ? am->msg_names[i] :
1122                            "  [no handler]");
1123         }
1124       else
1125         {
1126           vlib_cli_output (vm, "%-4d %-40s %6d %7d", i,
1127                            am->msg_names[i] ? am->msg_names[i] :
1128                            "  [no handler]", am->message_bounce[i],
1129                            am->is_mp_safe[i]);
1130         }
1131     }
1132
1133   return 0;
1134 }
1135
1136 /* *INDENT-OFF* */
1137 VLIB_CLI_COMMAND (cli_show_api_message_table_command, static) = {
1138     .path = "show api message-table",
1139     .short_help = "Message Table",
1140     .function = vl_api_message_table_command,
1141 };
1142 /* *INDENT-ON* */
1143
1144 static clib_error_t *
1145 vl_api_trace_command (vlib_main_t * vm,
1146                       unformat_input_t * input, vlib_cli_command_t * cli_cmd)
1147 {
1148   u32 nitems = 1024;
1149   vl_api_trace_which_t which = VL_API_TRACE_RX;
1150   api_main_t *am = &api_main;
1151
1152   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1153     {
1154       if (unformat (input, "rx nitems %u", &nitems) || unformat (input, "rx"))
1155         goto configure;
1156       else if (unformat (input, "tx nitems %u", &nitems)
1157                || unformat (input, "tx"))
1158         {
1159           which = VL_API_TRACE_RX;
1160           goto configure;
1161         }
1162       else if (unformat (input, "on rx"))
1163         {
1164           vl_msg_api_trace_onoff (am, VL_API_TRACE_RX, 1);
1165         }
1166       else if (unformat (input, "on tx"))
1167         {
1168           vl_msg_api_trace_onoff (am, VL_API_TRACE_TX, 1);
1169         }
1170       else if (unformat (input, "on"))
1171         {
1172           vl_msg_api_trace_onoff (am, VL_API_TRACE_RX, 1);
1173         }
1174       else if (unformat (input, "off"))
1175         {
1176           vl_msg_api_trace_onoff (am, VL_API_TRACE_RX, 0);
1177           vl_msg_api_trace_onoff (am, VL_API_TRACE_TX, 0);
1178         }
1179       else if (unformat (input, "free"))
1180         {
1181           vl_msg_api_trace_onoff (am, VL_API_TRACE_RX, 0);
1182           vl_msg_api_trace_onoff (am, VL_API_TRACE_TX, 0);
1183           vl_msg_api_trace_free (am, VL_API_TRACE_RX);
1184           vl_msg_api_trace_free (am, VL_API_TRACE_TX);
1185         }
1186       else if (unformat (input, "debug on"))
1187         {
1188           am->msg_print_flag = 1;
1189         }
1190       else if (unformat (input, "debug off"))
1191         {
1192           am->msg_print_flag = 0;
1193         }
1194       else
1195         return clib_error_return (0, "unknown input `%U'",
1196                                   format_unformat_error, input);
1197     }
1198   return 0;
1199
1200 configure:
1201   if (vl_msg_api_trace_configure (am, which, nitems))
1202     {
1203       vlib_cli_output (vm, "warning: trace configure error (%d, %d)",
1204                        which, nitems);
1205     }
1206
1207   return 0;
1208 }
1209
1210 /* *INDENT-OFF* */
1211 VLIB_CLI_COMMAND (trace, static) = {
1212     .path = "set api-trace",
1213     .short_help = "API trace",
1214     .function = vl_api_trace_command,
1215 };
1216 /* *INDENT-ON* */
1217
1218 clib_error_t *
1219 vlibmemory_init (vlib_main_t * vm)
1220 {
1221   api_main_t *am = &api_main;
1222   svm_map_region_args_t _a, *a = &_a;
1223
1224   memset (a, 0, sizeof (*a));
1225   a->root_path = am->root_path;
1226   a->name = SVM_GLOBAL_REGION_NAME;
1227   a->baseva = (am->global_baseva != 0) ?
1228     am->global_baseva : SVM_GLOBAL_REGION_BASEVA;
1229   a->size = (am->global_size != 0) ? am->global_size : SVM_GLOBAL_REGION_SIZE;
1230   a->flags = SVM_FLAGS_NODATA;
1231   a->uid = am->api_uid;
1232   a->gid = am->api_gid;
1233   a->pvt_heap_size =
1234     (am->global_pvt_heap_size !=
1235      0) ? am->global_pvt_heap_size : SVM_PVT_MHEAP_SIZE;
1236
1237   svm_region_init_args (a);
1238   return 0;
1239 }
1240
1241 VLIB_INIT_FUNCTION (vlibmemory_init);
1242
1243 void
1244 vl_set_memory_region_name (const char *name)
1245 {
1246   api_main_t *am = &api_main;
1247
1248   am->region_name = name;
1249 }
1250
1251 static int
1252 range_compare (vl_api_msg_range_t * a0, vl_api_msg_range_t * a1)
1253 {
1254   int len0, len1, clen;
1255
1256   len0 = vec_len (a0->name);
1257   len1 = vec_len (a1->name);
1258   clen = len0 < len1 ? len0 : len1;
1259   return (strncmp ((char *) a0->name, (char *) a1->name, clen));
1260 }
1261
1262 static u8 *
1263 format_api_msg_range (u8 * s, va_list * args)
1264 {
1265   vl_api_msg_range_t *rp = va_arg (*args, vl_api_msg_range_t *);
1266
1267   if (rp == 0)
1268     s = format (s, "%-20s%9s%9s", "Name", "First-ID", "Last-ID");
1269   else
1270     s = format (s, "%-20s%9d%9d", rp->name, rp->first_msg_id,
1271                 rp->last_msg_id);
1272
1273   return s;
1274 }
1275
1276 static clib_error_t *
1277 vl_api_show_plugin_command (vlib_main_t * vm,
1278                             unformat_input_t * input,
1279                             vlib_cli_command_t * cli_cmd)
1280 {
1281   api_main_t *am = &api_main;
1282   vl_api_msg_range_t *rp = 0;
1283   int i;
1284
1285   if (vec_len (am->msg_ranges) == 0)
1286     {
1287       vlib_cli_output (vm, "No plugin API message ranges configured...");
1288       return 0;
1289     }
1290
1291   rp = vec_dup (am->msg_ranges);
1292
1293   vec_sort_with_function (rp, range_compare);
1294
1295   vlib_cli_output (vm, "Plugin API message ID ranges...\n");
1296   vlib_cli_output (vm, "%U", format_api_msg_range, 0 /* header */ );
1297
1298   for (i = 0; i < vec_len (rp); i++)
1299     vlib_cli_output (vm, "%U", format_api_msg_range, rp + i);
1300
1301   vec_free (rp);
1302
1303   return 0;
1304 }
1305
1306 /* *INDENT-OFF* */
1307 VLIB_CLI_COMMAND (cli_show_api_plugin_command, static) = {
1308     .path = "show api plugin",
1309     .short_help = "show api plugin",
1310     .function = vl_api_show_plugin_command,
1311 };
1312 /* *INDENT-ON* */
1313
1314 static void
1315 vl_api_rpc_call_t_handler (vl_api_rpc_call_t * mp)
1316 {
1317   vl_api_rpc_call_reply_t *rmp;
1318   int (*fp) (void *);
1319   i32 rv = 0;
1320   vlib_main_t *vm = vlib_get_main ();
1321
1322   if (mp->function == 0)
1323     {
1324       rv = -1;
1325       clib_warning ("rpc NULL function pointer");
1326     }
1327
1328   else
1329     {
1330       if (mp->need_barrier_sync)
1331         vlib_worker_thread_barrier_sync (vm);
1332
1333       fp = uword_to_pointer (mp->function, int (*)(void *));
1334       rv = fp (mp->data);
1335
1336       if (mp->need_barrier_sync)
1337         vlib_worker_thread_barrier_release (vm);
1338     }
1339
1340   if (mp->send_reply)
1341     {
1342       unix_shared_memory_queue_t *q =
1343         vl_api_client_index_to_input_queue (mp->client_index);
1344       if (q)
1345         {
1346           rmp = vl_msg_api_alloc_as_if_client (sizeof (*rmp));
1347           rmp->_vl_msg_id = ntohs (VL_API_RPC_CALL_REPLY);
1348           rmp->context = mp->context;
1349           rmp->retval = rv;
1350           vl_msg_api_send_shmem (q, (u8 *) & rmp);
1351         }
1352     }
1353   if (mp->multicast)
1354     {
1355       clib_warning ("multicast not yet implemented...");
1356     }
1357 }
1358
1359 static void
1360 vl_api_rpc_call_reply_t_handler (vl_api_rpc_call_reply_t * mp)
1361 {
1362   clib_warning ("unimplemented");
1363 }
1364
1365 void
1366 vl_api_rpc_call_main_thread (void *fp, u8 * data, u32 data_length)
1367 {
1368   vl_api_rpc_call_t *mp;
1369   api_main_t *am = &api_main;
1370   vl_shmem_hdr_t *shmem_hdr = am->shmem_hdr;
1371   unix_shared_memory_queue_t *q;
1372
1373   /* Main thread: call the function directly */
1374   if (vlib_get_thread_index () == 0)
1375     {
1376       vlib_main_t *vm = vlib_get_main ();
1377       void (*call_fp) (void *);
1378
1379       vlib_worker_thread_barrier_sync (vm);
1380
1381       call_fp = fp;
1382       call_fp (data);
1383
1384       vlib_worker_thread_barrier_release (vm);
1385       return;
1386     }
1387
1388   /* Any other thread, actually do an RPC call... */
1389   mp = vl_msg_api_alloc_as_if_client (sizeof (*mp) + data_length);
1390
1391   memset (mp, 0, sizeof (*mp));
1392   clib_memcpy (mp->data, data, data_length);
1393   mp->_vl_msg_id = ntohs (VL_API_RPC_CALL);
1394   mp->function = pointer_to_uword (fp);
1395   mp->need_barrier_sync = 1;
1396
1397   /*
1398    * Use the "normal" control-plane mechanism for the main thread.
1399    * Well, almost. if the main input queue is full, we cannot
1400    * block. Otherwise, we can expect a barrier sync timeout.
1401    */
1402   q = shmem_hdr->vl_input_queue;
1403
1404   while (pthread_mutex_trylock (&q->mutex))
1405     vlib_worker_thread_barrier_check ();
1406
1407   while (PREDICT_FALSE (unix_shared_memory_queue_is_full (q)))
1408     {
1409       pthread_mutex_unlock (&q->mutex);
1410       vlib_worker_thread_barrier_check ();
1411       while (pthread_mutex_trylock (&q->mutex))
1412         vlib_worker_thread_barrier_check ();
1413     }
1414
1415   vl_msg_api_send_shmem_nolock (q, (u8 *) & mp);
1416
1417   pthread_mutex_unlock (&q->mutex);
1418 }
1419
1420 static void
1421 vl_api_trace_plugin_msg_ids_t_handler (vl_api_trace_plugin_msg_ids_t * mp)
1422 {
1423   api_main_t *am = &api_main;
1424   vl_api_msg_range_t *rp;
1425   uword *p;
1426
1427   /* Noop (except for tracing) during normal operation */
1428   if (am->replay_in_progress == 0)
1429     return;
1430
1431   p = hash_get_mem (am->msg_range_by_name, mp->plugin_name);
1432   if (p == 0)
1433     {
1434       clib_warning ("WARNING: traced plugin '%s' not in current image",
1435                     mp->plugin_name);
1436       return;
1437     }
1438
1439   rp = vec_elt_at_index (am->msg_ranges, p[0]);
1440   if (rp->first_msg_id != clib_net_to_host_u16 (mp->first_msg_id))
1441     {
1442       clib_warning ("WARNING: traced plugin '%s' first message id %d not %d",
1443                     mp->plugin_name, clib_net_to_host_u16 (mp->first_msg_id),
1444                     rp->first_msg_id);
1445     }
1446
1447   if (rp->last_msg_id != clib_net_to_host_u16 (mp->last_msg_id))
1448     {
1449       clib_warning ("WARNING: traced plugin '%s' last message id %d not %d",
1450                     mp->plugin_name, clib_net_to_host_u16 (mp->last_msg_id),
1451                     rp->last_msg_id);
1452     }
1453 }
1454
1455 #define foreach_rpc_api_msg                     \
1456 _(RPC_CALL,rpc_call)                            \
1457 _(RPC_CALL_REPLY,rpc_call_reply)
1458
1459 #define foreach_plugin_trace_msg                \
1460 _(TRACE_PLUGIN_MSG_IDS,trace_plugin_msg_ids)
1461
1462 static clib_error_t *
1463 rpc_api_hookup (vlib_main_t * vm)
1464 {
1465 #define _(N,n)                                                  \
1466     vl_msg_api_set_handlers(VL_API_##N, #n,                     \
1467                            vl_api_##n##_t_handler,              \
1468                            vl_noop_handler,                     \
1469                            vl_noop_handler,                     \
1470                            vl_api_##n##_t_print,                \
1471                            sizeof(vl_api_##n##_t), 0 /* do not trace */);
1472   foreach_rpc_api_msg;
1473 #undef _
1474
1475 #define _(N,n)                                                  \
1476     vl_msg_api_set_handlers(VL_API_##N, #n,                     \
1477                            vl_api_##n##_t_handler,              \
1478                            vl_noop_handler,                     \
1479                            vl_noop_handler,                     \
1480                            vl_api_##n##_t_print,                \
1481                            sizeof(vl_api_##n##_t), 1 /* do trace */);
1482   foreach_plugin_trace_msg;
1483 #undef _
1484   return 0;
1485 }
1486
1487 VLIB_API_INIT_FUNCTION (rpc_api_hookup);
1488
1489 typedef enum
1490 {
1491   DUMP,
1492   CUSTOM_DUMP,
1493   REPLAY,
1494   INITIALIZERS,
1495 } vl_api_replay_t;
1496
1497 u8 *
1498 format_vl_msg_api_trace_status (u8 * s, va_list * args)
1499 {
1500   api_main_t *am = va_arg (*args, api_main_t *);
1501   vl_api_trace_which_t which = va_arg (*args, vl_api_trace_which_t);
1502   vl_api_trace_t *tp;
1503   char *trace_name;
1504
1505   switch (which)
1506     {
1507     case VL_API_TRACE_TX:
1508       tp = am->tx_trace;
1509       trace_name = "TX trace";
1510       break;
1511
1512     case VL_API_TRACE_RX:
1513       tp = am->rx_trace;
1514       trace_name = "RX trace";
1515       break;
1516
1517     default:
1518       abort ();
1519     }
1520
1521   if (tp == 0)
1522     {
1523       s = format (s, "%s: not yet configured.\n", trace_name);
1524       return s;
1525     }
1526
1527   s = format (s, "%s: used %d of %d items, %s enabled, %s wrapped\n",
1528               trace_name, vec_len (tp->traces), tp->nitems,
1529               tp->enabled ? "is" : "is not", tp->wrapped ? "has" : "has not");
1530   return s;
1531 }
1532
1533 void vl_msg_api_custom_dump_configure (api_main_t * am)
1534   __attribute__ ((weak));
1535 void
1536 vl_msg_api_custom_dump_configure (api_main_t * am)
1537 {
1538 }
1539
1540 static void
1541 vl_msg_api_process_file (vlib_main_t * vm, u8 * filename,
1542                          u32 first_index, u32 last_index,
1543                          vl_api_replay_t which)
1544 {
1545   vl_api_trace_file_header_t *hp;
1546   int i, fd;
1547   struct stat statb;
1548   size_t file_size;
1549   u8 *msg;
1550   u8 endian_swap_needed = 0;
1551   api_main_t *am = &api_main;
1552   u8 *tmpbuf = 0;
1553   u32 nitems;
1554   void **saved_print_handlers = 0;
1555
1556   fd = open ((char *) filename, O_RDONLY);
1557
1558   if (fd < 0)
1559     {
1560       vlib_cli_output (vm, "Couldn't open %s\n", filename);
1561       return;
1562     }
1563
1564   if (fstat (fd, &statb) < 0)
1565     {
1566       vlib_cli_output (vm, "Couldn't stat %s\n", filename);
1567       close (fd);
1568       return;
1569     }
1570
1571   if (!(statb.st_mode & S_IFREG) || (statb.st_size < sizeof (*hp)))
1572     {
1573       vlib_cli_output (vm, "File not plausible: %s\n", filename);
1574       close (fd);
1575       return;
1576     }
1577
1578   file_size = statb.st_size;
1579   file_size = (file_size + 4095) & ~(4096);
1580
1581   hp = mmap (0, file_size, PROT_READ, MAP_PRIVATE, fd, 0);
1582
1583   if (hp == (vl_api_trace_file_header_t *) MAP_FAILED)
1584     {
1585       vlib_cli_output (vm, "mmap failed: %s\n", filename);
1586       close (fd);
1587       return;
1588     }
1589   close (fd);
1590
1591   if ((clib_arch_is_little_endian && hp->endian == VL_API_BIG_ENDIAN)
1592       || (clib_arch_is_big_endian && hp->endian == VL_API_LITTLE_ENDIAN))
1593     endian_swap_needed = 1;
1594
1595   if (endian_swap_needed)
1596     nitems = ntohl (hp->nitems);
1597   else
1598     nitems = hp->nitems;
1599
1600   if (last_index == (u32) ~ 0)
1601     {
1602       last_index = nitems - 1;
1603     }
1604
1605   if (first_index >= nitems || last_index >= nitems)
1606     {
1607       vlib_cli_output (vm, "Range (%d, %d) outside file range (0, %d)\n",
1608                        first_index, last_index, nitems - 1);
1609       munmap (hp, file_size);
1610       return;
1611     }
1612   if (hp->wrapped)
1613     vlib_cli_output (vm,
1614                      "Note: wrapped/incomplete trace, results may vary\n");
1615
1616   if (which == CUSTOM_DUMP)
1617     {
1618       saved_print_handlers = (void **) vec_dup (am->msg_print_handlers);
1619       vl_msg_api_custom_dump_configure (am);
1620     }
1621
1622
1623   msg = (u8 *) (hp + 1);
1624
1625   for (i = 0; i < first_index; i++)
1626     {
1627       trace_cfg_t *cfgp;
1628       int size;
1629       u16 msg_id;
1630
1631       size = clib_host_to_net_u32 (*(u32 *) msg);
1632       msg += sizeof (u32);
1633
1634       if (clib_arch_is_little_endian)
1635         msg_id = ntohs (*((u16 *) msg));
1636       else
1637         msg_id = *((u16 *) msg);
1638
1639       cfgp = am->api_trace_cfg + msg_id;
1640       if (!cfgp)
1641         {
1642           vlib_cli_output (vm, "Ugh: msg id %d no trace config\n", msg_id);
1643           munmap (hp, file_size);
1644           return;
1645         }
1646       msg += size;
1647     }
1648
1649   if (which == REPLAY)
1650     am->replay_in_progress = 1;
1651
1652   for (; i <= last_index; i++)
1653     {
1654       trace_cfg_t *cfgp;
1655       u16 *msg_idp;
1656       u16 msg_id;
1657       int size;
1658
1659       if (which == DUMP)
1660         vlib_cli_output (vm, "---------- trace %d -----------\n", i);
1661
1662       size = clib_host_to_net_u32 (*(u32 *) msg);
1663       msg += sizeof (u32);
1664
1665       if (clib_arch_is_little_endian)
1666         msg_id = ntohs (*((u16 *) msg));
1667       else
1668         msg_id = *((u16 *) msg);
1669
1670       cfgp = am->api_trace_cfg + msg_id;
1671       if (!cfgp)
1672         {
1673           vlib_cli_output (vm, "Ugh: msg id %d no trace config\n", msg_id);
1674           munmap (hp, file_size);
1675           vec_free (tmpbuf);
1676           am->replay_in_progress = 0;
1677           return;
1678         }
1679
1680       /* Copy the buffer (from the read-only mmap'ed file) */
1681       vec_validate (tmpbuf, size - 1 + sizeof (uword));
1682       clib_memcpy (tmpbuf + sizeof (uword), msg, size);
1683       memset (tmpbuf, 0xf, sizeof (uword));
1684
1685       /*
1686        * Endian swap if needed. All msg data is supposed to be
1687        * in network byte order. All msg handlers are supposed to
1688        * know that. The generic message dumpers don't know that.
1689        * One could fix apigen, I suppose.
1690        */
1691       if ((which == DUMP && clib_arch_is_little_endian) || endian_swap_needed)
1692         {
1693           void (*endian_fp) (void *);
1694           if (msg_id >= vec_len (am->msg_endian_handlers)
1695               || (am->msg_endian_handlers[msg_id] == 0))
1696             {
1697               vlib_cli_output (vm, "Ugh: msg id %d no endian swap\n", msg_id);
1698               munmap (hp, file_size);
1699               vec_free (tmpbuf);
1700               am->replay_in_progress = 0;
1701               return;
1702             }
1703           endian_fp = am->msg_endian_handlers[msg_id];
1704           (*endian_fp) (tmpbuf + sizeof (uword));
1705         }
1706
1707       /* msg_id always in network byte order */
1708       if (clib_arch_is_little_endian)
1709         {
1710           msg_idp = (u16 *) (tmpbuf + sizeof (uword));
1711           *msg_idp = msg_id;
1712         }
1713
1714       switch (which)
1715         {
1716         case CUSTOM_DUMP:
1717         case DUMP:
1718           if (msg_id < vec_len (am->msg_print_handlers) &&
1719               am->msg_print_handlers[msg_id])
1720             {
1721               u8 *(*print_fp) (void *, void *);
1722
1723               print_fp = (void *) am->msg_print_handlers[msg_id];
1724               (*print_fp) (tmpbuf + sizeof (uword), vm);
1725             }
1726           else
1727             {
1728               vlib_cli_output (vm, "Skipping msg id %d: no print fcn\n",
1729                                msg_id);
1730               break;
1731             }
1732           break;
1733
1734         case INITIALIZERS:
1735           if (msg_id < vec_len (am->msg_print_handlers) &&
1736               am->msg_print_handlers[msg_id])
1737             {
1738               u8 *s;
1739               int j;
1740               u8 *(*print_fp) (void *, void *);
1741
1742               print_fp = (void *) am->msg_print_handlers[msg_id];
1743
1744               vlib_cli_output (vm, "/*");
1745
1746               (*print_fp) (tmpbuf + sizeof (uword), vm);
1747               vlib_cli_output (vm, "*/\n");
1748
1749               s = format (0, "static u8 * vl_api_%s_%d[%d] = {",
1750                           am->msg_names[msg_id], i,
1751                           am->api_trace_cfg[msg_id].size);
1752
1753               for (j = 0; j < am->api_trace_cfg[msg_id].size; j++)
1754                 {
1755                   if ((j & 7) == 0)
1756                     s = format (s, "\n    ");
1757                   s = format (s, "0x%02x,", tmpbuf[sizeof (uword) + j]);
1758                 }
1759               s = format (s, "\n};\n%c", 0);
1760               vlib_cli_output (vm, (char *) s);
1761               vec_free (s);
1762             }
1763           break;
1764
1765         case REPLAY:
1766           if (msg_id < vec_len (am->msg_print_handlers) &&
1767               am->msg_print_handlers[msg_id] && cfgp->replay_enable)
1768             {
1769               void (*handler) (void *);
1770
1771               handler = (void *) am->msg_handlers[msg_id];
1772
1773               if (!am->is_mp_safe[msg_id])
1774                 vl_msg_api_barrier_sync ();
1775               (*handler) (tmpbuf + sizeof (uword));
1776               if (!am->is_mp_safe[msg_id])
1777                 vl_msg_api_barrier_release ();
1778             }
1779           else
1780             {
1781               if (cfgp->replay_enable)
1782                 vlib_cli_output (vm, "Skipping msg id %d: no handler\n",
1783                                  msg_id);
1784               break;
1785             }
1786           break;
1787         }
1788
1789       _vec_len (tmpbuf) = 0;
1790       msg += size;
1791     }
1792
1793   if (saved_print_handlers)
1794     {
1795       clib_memcpy (am->msg_print_handlers, saved_print_handlers,
1796                    vec_len (am->msg_print_handlers) * sizeof (void *));
1797       vec_free (saved_print_handlers);
1798     }
1799
1800   munmap (hp, file_size);
1801   vec_free (tmpbuf);
1802   am->replay_in_progress = 0;
1803 }
1804
1805 static clib_error_t *
1806 api_trace_command_fn (vlib_main_t * vm,
1807                       unformat_input_t * input, vlib_cli_command_t * cmd)
1808 {
1809   u32 nitems = 256 << 10;
1810   api_main_t *am = &api_main;
1811   vl_api_trace_which_t which = VL_API_TRACE_RX;
1812   u8 *filename;
1813   u32 first = 0;
1814   u32 last = (u32) ~ 0;
1815   FILE *fp;
1816   int rv;
1817
1818   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1819     {
1820       if (unformat (input, "on") || unformat (input, "enable"))
1821         {
1822           if (unformat (input, "nitems %d", &nitems))
1823             ;
1824           vl_msg_api_trace_configure (am, which, nitems);
1825           vl_msg_api_trace_onoff (am, which, 1 /* on */ );
1826         }
1827       else if (unformat (input, "off"))
1828         {
1829           vl_msg_api_trace_onoff (am, which, 0);
1830         }
1831       else if (unformat (input, "save %s", &filename))
1832         {
1833           u8 *chroot_filename;
1834           if (strstr ((char *) filename, "..")
1835               || index ((char *) filename, '/'))
1836             {
1837               vlib_cli_output (vm, "illegal characters in filename '%s'",
1838                                filename);
1839               return 0;
1840             }
1841
1842           chroot_filename = format (0, "/tmp/%s%c", filename, 0);
1843
1844           vec_free (filename);
1845
1846           fp = fopen ((char *) chroot_filename, "w");
1847           if (fp == NULL)
1848             {
1849               vlib_cli_output (vm, "Couldn't create %s\n", chroot_filename);
1850               return 0;
1851             }
1852           rv = vl_msg_api_trace_save (am, which, fp);
1853           fclose (fp);
1854           if (rv == -1)
1855             vlib_cli_output (vm, "API Trace data not present\n");
1856           else if (rv == -2)
1857             vlib_cli_output (vm, "File for writing is closed\n");
1858           else if (rv == -10)
1859             vlib_cli_output (vm, "Error while writing header to file\n");
1860           else if (rv == -11)
1861             vlib_cli_output (vm, "Error while writing trace to file\n");
1862           else if (rv == -12)
1863             vlib_cli_output (vm,
1864                              "Error while writing end of buffer trace to file\n");
1865           else if (rv == -13)
1866             vlib_cli_output (vm,
1867                              "Error while writing start of buffer trace to file\n");
1868           else if (rv < 0)
1869             vlib_cli_output (vm, "Unkown error while saving: %d", rv);
1870           else
1871             vlib_cli_output (vm, "API trace saved to %s\n", chroot_filename);
1872           vec_free (chroot_filename);
1873         }
1874       else if (unformat (input, "dump %s", &filename))
1875         {
1876           vl_msg_api_process_file (vm, filename, first, last, DUMP);
1877         }
1878       else if (unformat (input, "custom-dump %s", &filename))
1879         {
1880           vl_msg_api_process_file (vm, filename, first, last, CUSTOM_DUMP);
1881         }
1882       else if (unformat (input, "replay %s", &filename))
1883         {
1884           vl_msg_api_process_file (vm, filename, first, last, REPLAY);
1885         }
1886       else if (unformat (input, "initializers %s", &filename))
1887         {
1888           vl_msg_api_process_file (vm, filename, first, last, INITIALIZERS);
1889         }
1890       else if (unformat (input, "tx"))
1891         {
1892           which = VL_API_TRACE_TX;
1893         }
1894       else if (unformat (input, "first %d", &first))
1895         {
1896           ;
1897         }
1898       else if (unformat (input, "last %d", &last))
1899         {
1900           ;
1901         }
1902       else if (unformat (input, "status"))
1903         {
1904           vlib_cli_output (vm, "%U", format_vl_msg_api_trace_status,
1905                            am, which);
1906         }
1907       else if (unformat (input, "free"))
1908         {
1909           vl_msg_api_trace_onoff (am, which, 0);
1910           vl_msg_api_trace_free (am, which);
1911         }
1912       else if (unformat (input, "post-mortem-on"))
1913         vl_msg_api_post_mortem_dump_enable_disable (1 /* enable */ );
1914       else if (unformat (input, "post-mortem-off"))
1915         vl_msg_api_post_mortem_dump_enable_disable (0 /* enable */ );
1916       else
1917         return clib_error_return (0, "unknown input `%U'",
1918                                   format_unformat_error, input);
1919     }
1920   return 0;
1921 }
1922
1923 /* *INDENT-OFF* */
1924 VLIB_CLI_COMMAND (api_trace_command, static) = {
1925     .path = "api trace",
1926     .short_help =
1927     "api trace [on|off][dump|save|replay <file>][status][free][post-mortem-on]",
1928     .function = api_trace_command_fn,
1929 };
1930 /* *INDENT-ON* */
1931
1932 static clib_error_t *
1933 api_config_fn (vlib_main_t * vm, unformat_input_t * input)
1934 {
1935   u32 nitems = 256 << 10;
1936   vl_api_trace_which_t which = VL_API_TRACE_RX;
1937   api_main_t *am = &api_main;
1938
1939   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1940     {
1941       if (unformat (input, "on") || unformat (input, "enable"))
1942         {
1943           if (unformat (input, "nitems %d", &nitems))
1944             ;
1945           vl_msg_api_trace_configure (am, which, nitems);
1946           vl_msg_api_trace_onoff (am, which, 1 /* on */ );
1947           vl_msg_api_post_mortem_dump_enable_disable (1 /* enable */ );
1948         }
1949       else
1950         return clib_error_return (0, "unknown input `%U'",
1951                                   format_unformat_error, input);
1952     }
1953   return 0;
1954 }
1955
1956 VLIB_CONFIG_FUNCTION (api_config_fn, "api-trace");
1957
1958 static clib_error_t *
1959 api_queue_config_fn (vlib_main_t * vm, unformat_input_t * input)
1960 {
1961   api_main_t *am = &api_main;
1962   u32 nitems;
1963
1964   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1965     {
1966       if (unformat (input, "length %d", &nitems) ||
1967           (unformat (input, "len %d", &nitems)))
1968         {
1969           if (nitems >= 1024)
1970             am->vlib_input_queue_length = nitems;
1971           else
1972             clib_warning ("vlib input queue length %d too small, ignored",
1973                           nitems);
1974         }
1975       else
1976         return clib_error_return (0, "unknown input `%U'",
1977                                   format_unformat_error, input);
1978     }
1979   return 0;
1980 }
1981
1982 VLIB_CONFIG_FUNCTION (api_queue_config_fn, "api-queue");
1983
1984 /*
1985  * fd.io coding-style-patch-verification: ON
1986  *
1987  * Local Variables:
1988  * eval: (c-set-style "gnu")
1989  * End:
1990  */