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