VPP-117: Add trace to ip4 and ip6 lookup nodes
[vpp.git] / vlib-api / 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 <signal.h>
26 #include <pthread.h>
27 #include <vppinfra/vec.h>
28 #include <vppinfra/hash.h>
29 #include <vppinfra/pool.h>
30 #include <vppinfra/format.h>
31 #include <vppinfra/byte_order.h>
32 #include <vppinfra/elog.h>
33 #include <stdarg.h>
34 #include <vlib/vlib.h>
35 #include <vlib/unix/unix.h>
36 #include <vlibapi/api.h>
37 #include <vlibmemory/api.h>
38
39 #define TRACE_VLIB_MEMORY_QUEUE 0
40
41 #include <vlibmemory/vl_memory_msg_enum.h> /* enumerate all vlib messages */
42
43 #define vl_typedefs             /* define message structures */
44 #include <vlibmemory/vl_memory_api_h.h> 
45 #undef vl_typedefs
46
47 /* instantiate all the print functions we know about */
48 #define vl_print(handle, ...) vlib_cli_output (handle, __VA_ARGS__)
49 #define vl_printfun
50 #include <vlibmemory/vl_memory_api_h.h> 
51 #undef vl_printfun
52
53 static inline void *
54 vl_api_memclnt_create_t_print (vl_api_memclnt_create_t *a,void *handle)
55 {
56     vl_print(handle, "vl_api_memclnt_create_t:\n");
57     vl_print(handle, "name: %s\n", a->name);
58     vl_print(handle, "input_queue: 0x%wx\n", a->input_queue);
59     vl_print(handle, "context: %u\n", (unsigned) a->context);
60     vl_print(handle, "ctx_quota: %ld\n", (long) a->ctx_quota);
61     return handle;
62 }
63
64 static inline void *
65 vl_api_memclnt_delete_t_print (vl_api_memclnt_delete_t *a,void *handle)
66 {
67     vl_print(handle, "vl_api_memclnt_delete_t:\n");
68     vl_print(handle, "index: %u\n", (unsigned) a->index);
69     vl_print(handle, "handle: 0x%wx\n", a->handle);
70     return handle;
71 }
72
73 /* instantiate all the endian swap functions we know about */
74 #define vl_endianfun
75 #include <vlibmemory/vl_memory_api_h.h> 
76 #undef vl_endianfun
77
78 void vl_socket_api_send (vl_api_registration_t *rp, u8 *elem) 
79     __attribute__((weak));
80
81 void 
82 vl_socket_api_send (vl_api_registration_t *rp, u8 *elem) 
83 {
84     static int count;
85
86     if (count++ < 5)
87         clib_warning ("need to link against -lvlibsocket, msg not sent!");
88 }
89
90 void vl_msg_api_send (vl_api_registration_t *rp, u8 *elem)
91 {
92     if (PREDICT_FALSE(rp->registration_type > REGISTRATION_TYPE_SHMEM)) {
93         vl_socket_api_send (rp, elem);
94     } else {
95         vl_msg_api_send_shmem (rp->vl_input_queue, elem);
96     }
97 }
98
99 int vl_msg_api_version_check (vl_api_memclnt_create_t * mp) 
100     __attribute__((weak));
101
102 int vl_msg_api_version_check (vl_api_memclnt_create_t * mp) { return 0; }
103
104 /*
105  * vl_api_memclnt_create_t_handler
106  */
107 void vl_api_memclnt_create_t_handler (vl_api_memclnt_create_t *mp)
108 {
109     vl_api_registration_t **regpp;
110     vl_api_registration_t *regp;
111     vl_api_memclnt_create_reply_t *rp;
112     svm_region_t *svm;
113     unix_shared_memory_queue_t *q;
114     int rv;
115     void *oldheap;
116     api_main_t *am = &api_main;
117
118     /* Indicate API version mismatch if appropriate */
119     rv = vl_msg_api_version_check (mp);
120
121     /*
122      * This is tortured. Maintain a vlib-address-space private
123      * pool of client registrations. We use the shared-memory virtual 
124      * address of client structure as a handle, to allow direct 
125      * manipulation of context quota vbls from the client library.
126      *
127      * This scheme causes trouble w/ API message trace replay, since
128      * some random VA from clib_mem_alloc() certainly won't 
129      * occur in the Linux sim. The (very) few places
130      * that care need to use the pool index.
131      *
132      * Putting the registration object(s) into a pool in shared memory and
133      * using the pool index as a handle seems like a great idea.
134      * Unfortunately, each and every reference to that pool would need 
135      * to be protected by a mutex:
136      *
137      *     Client                      VLIB
138      *     ------                      ----
139      *     convert pool index to 
140      *     pointer.
141      *     <deschedule>
142      *                                 expand pool
143      *                                 <deschedule>
144      *     kaboom!
145      */
146
147     pool_get(am->vl_clients, regpp);
148
149     svm = am->vlib_rp;
150
151     pthread_mutex_lock (&svm->mutex);
152     oldheap = svm_push_data_heap(svm);
153     *regpp = clib_mem_alloc(sizeof(vl_api_registration_t));
154
155     regp = *regpp;
156     memset(regp, 0, sizeof(*regp));
157     regp->registration_type = REGISTRATION_TYPE_SHMEM;
158     regp->vl_api_registration_pool_index = regpp - am->vl_clients;
159
160     q = regp->vl_input_queue = (unix_shared_memory_queue_t *)(uword)
161         mp->input_queue;
162
163     regp->name = format(0, "%s", mp->name);
164     vec_add1(regp->name, 0);
165
166     pthread_mutex_unlock(&svm->mutex);
167     svm_pop_heap (oldheap);
168
169     rp = vl_msg_api_alloc(sizeof(*rp));
170     rp->_vl_msg_id = ntohs(VL_API_MEMCLNT_CREATE_REPLY);
171     rp->handle = (uword)regp;
172     rp->index = vl_msg_api_handle_from_index_and_epoch 
173         (regp->vl_api_registration_pool_index, 
174          am->shmem_hdr->application_restarts);
175     rp->context = mp->context;
176     rp->response = ntohl(rv);
177
178     vl_msg_api_send_shmem(q, (u8 *)&rp);
179 }
180
181 /* Application callback to clean up leftover registrations from this client */
182 int vl_api_memclnt_delete_callback (u32 client_index) 
183     __attribute__((weak));
184
185 int vl_api_memclnt_delete_callback (u32 client_index) 
186 { return 0; }
187
188 /*
189  * vl_api_memclnt_delete_t_handler
190  */
191 void vl_api_memclnt_delete_t_handler (vl_api_memclnt_delete_t *mp)
192 {
193     vl_api_registration_t **regpp;
194     vl_api_registration_t *regp;
195     vl_api_memclnt_delete_reply_t *rp;
196     svm_region_t *svm;
197     void *oldheap;
198     api_main_t *am = &api_main;
199     u32 handle, client_index, epoch;
200
201     handle = mp->index;
202
203     if (vl_api_memclnt_delete_callback (handle))
204         return;
205
206     epoch = vl_msg_api_handle_get_epoch (handle);
207     client_index = vl_msg_api_handle_get_index (handle);
208
209     if (epoch != (am->shmem_hdr->application_restarts & VL_API_EPOCH_MASK)) {
210         clib_warning 
211             ("Stale clnt delete index %d old epoch %d cur epoch %d", 
212              client_index, epoch, 
213              (am->shmem_hdr->application_restarts & VL_API_EPOCH_MASK));
214         return;
215     }
216
217     regpp = am->vl_clients + client_index;
218
219     if (!pool_is_free(am->vl_clients, regpp)) {
220         regp = *regpp;
221         svm = am->vlib_rp;
222
223         /* $$$ check the input queue for e.g. punted sf's */
224
225         rp = vl_msg_api_alloc(sizeof(*rp));
226         rp->_vl_msg_id = ntohs(VL_API_MEMCLNT_DELETE_REPLY);
227         rp->handle = mp->handle;
228         rp->response = 1;
229         
230         vl_msg_api_send_shmem (regp->vl_input_queue, (u8 *)&rp);
231         
232         if (client_index != regp->vl_api_registration_pool_index) {
233             clib_warning ("mismatch client_index %d pool_index %d", 
234                           client_index, regp->vl_api_registration_pool_index);
235             vl_msg_api_free (rp);
236             return;
237         }
238
239         /* No dangling references, please */
240         *regpp = 0;
241
242         pool_put_index(am->vl_clients, 
243                        regp->vl_api_registration_pool_index);
244
245         pthread_mutex_lock (&svm->mutex);
246         oldheap = svm_push_data_heap(svm);
247         /* Poison the old registration */
248         memset (regp, 0xF1, sizeof (*regp));
249         clib_mem_free (regp);
250         pthread_mutex_unlock(&svm->mutex);
251         svm_pop_heap (oldheap);
252     } else {
253         clib_warning("unknown client ID %d", mp->index);
254     }
255 }
256
257 void vl_api_get_first_msg_id_t_handler (vl_api_get_first_msg_id_t * mp)
258 {
259     vl_api_get_first_msg_id_reply_t * rmp;
260     unix_shared_memory_queue_t * q;
261     uword * p;
262     api_main_t *am = &api_main;
263     vl_api_msg_range_t * rp;
264     u8 name[64];
265     u16 first_msg_id = ~0;
266     int rv = -7; /* VNET_API_ERROR_INVALID_VALUE */
267
268     q = vl_api_client_index_to_input_queue (mp->client_index);
269     if (!q)
270         return;
271
272     if (am->msg_range_by_name == 0)
273         goto out;
274
275     strncpy ((char *)name, (char *) mp->name, ARRAY_LEN(name)-1);
276
277     p = hash_get_mem (am->msg_range_by_name, name);
278     if (p == 0)
279         goto out;
280
281     rp = vec_elt_at_index (am->msg_ranges, p[0]);
282
283     first_msg_id = rp->first_msg_id;
284     rv = 0;
285
286 out:
287
288     rmp = vl_msg_api_alloc (sizeof (*rmp));
289     rmp->_vl_msg_id = ntohs(VL_API_GET_FIRST_MSG_ID_REPLY);
290     rmp->context = mp->context;
291     rmp->retval = ntohl(rv);
292     rmp->first_msg_id = ntohs(first_msg_id);
293     vl_msg_api_send_shmem (q, (u8 *)&rmp);
294 }
295
296 #define foreach_vlib_api_msg                    \
297 _(MEMCLNT_CREATE, memclnt_create)               \
298 _(MEMCLNT_DELETE, memclnt_delete)               \
299 _(GET_FIRST_MSG_ID, get_first_msg_id)
300
301 /*
302  * vl_api_init
303  */
304 static int memory_api_init(char *region_name)
305 {
306     int rv;
307     vl_msg_api_msg_config_t cfg;
308     vl_msg_api_msg_config_t *c = &cfg;
309     
310     if ((rv = vl_map_shmem(region_name, 1 /* is_vlib */)) < 0)
311         return rv;
312
313 #define _(N,n) do {                                             \
314     c->id = VL_API_##N;                                         \
315     c->name = #n;                                               \
316     c->handler = vl_api_##n##_t_handler;                        \
317     c->cleanup = vl_noop_handler;                               \
318     c->endian = vl_api_##n##_t_endian;                          \
319     c->print = vl_api_##n##_t_print;                            \
320     c->size = sizeof(vl_api_##n##_t);                           \
321     c->traced = 1; /* trace, so these msgs print */             \
322     c->replay = 0; /* don't replay client create/delete msgs */ \
323     vl_msg_api_config(c);} while (0);
324     
325     foreach_vlib_api_msg;
326 #undef _
327
328     return 0;
329 }
330
331 #define foreach_histogram_bucket                \
332 _(400)                                          \
333 _(200)                                          \
334 _(100)                                          \
335 _(10)
336
337 typedef enum {
338 #define _(n) SLEEP_##n##_US,
339     foreach_histogram_bucket
340 #undef _
341     SLEEP_N_BUCKETS,
342 } histogram_index_t;
343
344 static u64 vector_rate_histogram[SLEEP_N_BUCKETS];
345
346 static void memclnt_queue_callback (vlib_main_t *vm);
347
348 static uword
349 memclnt_process (vlib_main_t * vm,
350                  vlib_node_runtime_t * node,
351                  vlib_frame_t * f)
352 {
353     uword mp;
354     vl_shmem_hdr_t *shm;
355     unix_shared_memory_queue_t *q;
356     clib_error_t *e;
357     int rv;
358     api_main_t *am = &api_main;
359     f64 dead_client_scan_time;
360     f64 sleep_time, start_time;
361     f64 vector_rate;
362     
363     vlib_set_queue_signal_callback (vm, memclnt_queue_callback);
364     
365     if ((rv = memory_api_init(am->region_name)) < 0) {
366         clib_warning("memory_api_init returned %d, wait for godot...", rv);
367         vlib_process_suspend (vm, 1e70);
368     }
369
370     shm = am->shmem_hdr;
371     ASSERT(shm);
372     q = shm->vl_input_queue;
373     ASSERT(q);
374
375     e = vlib_call_init_exit_functions 
376         (vm, vm->api_init_function_registrations, 1 /* call_once */);
377     if (e)
378       clib_error_report (e);
379
380     sleep_time = 20.0;
381     dead_client_scan_time = vlib_time_now(vm) + 20.0;
382
383     /* $$$ pay attention to frame size, control CPU usage */
384     while (1) {
385         uword event_type __attribute__((unused));
386         i8 *headp;
387         int need_broadcast;
388
389         /*
390          * There's a reason for checking the queue before
391          * sleeping. If the vlib application crashes, it's entirely
392          * possible for a client to enqueue a connect request
393          * during the process restart interval.
394          *
395          * Unless some force of physics causes the new incarnation
396          * of the application to process the request, the client will
397          * sit and wait for Godot...
398          */
399         vector_rate = vlib_last_vector_length_per_node(vm);
400         start_time = vlib_time_now (vm);
401         while (1) {
402             pthread_mutex_lock (&q->mutex);
403             if (q->cursize == 0) {
404                 vm->api_queue_nonempty = 0;
405                 pthread_mutex_unlock (&q->mutex);
406                 
407                 if (TRACE_VLIB_MEMORY_QUEUE)
408                 {
409                     ELOG_TYPE_DECLARE (e) = {
410                         .format = "q-underflow: len %d",
411                         .format_args = "i4",
412                     };
413                     struct { u32 len; } * ed;
414                     ed = ELOG_DATA (&vm->elog_main, e);
415                     ed->len = 0;
416                 }
417                 sleep_time = 20.0;
418                 break;
419             }
420             
421             headp = (i8 *) (q->data + sizeof(uword)*q->head);
422             clib_memcpy (&mp, headp, sizeof(uword));
423             
424             q->head++;
425             need_broadcast = (q->cursize == q->maxsize/2);
426             q->cursize--;
427             
428             if (PREDICT_FALSE(q->head == q->maxsize))
429                 q->head = 0;
430             pthread_mutex_unlock(&q->mutex);
431             if (need_broadcast)
432                 (void) pthread_cond_broadcast(&q->condvar);
433             
434             vl_msg_api_handler_with_vm_node (am, (void *)mp, vm, node);
435
436             /* Allow no more than 10us without a pause */
437             if (vlib_time_now(vm) > start_time + 10e-6) {
438                 int index = SLEEP_400_US;
439                 if (vector_rate > 40.0) 
440                     sleep_time = 400e-6;
441                 else if (vector_rate > 20.0) {
442                     index = SLEEP_200_US;
443                     sleep_time = 200e-6;
444                 } else if (vector_rate >= 1.0) {
445                     index = SLEEP_100_US;
446                     sleep_time = 100e-6;
447                 }
448                 else {
449                     index = SLEEP_10_US;
450                     sleep_time = 10e-6;
451                 }
452                 vector_rate_histogram[index] += 1;
453                 break;
454             }
455         }
456
457         event_type = vlib_process_wait_for_event_or_clock (vm, sleep_time);
458         vm->queue_signal_pending = 0;
459         vlib_process_get_events (vm, 0 /* event_data */);
460
461         if (vlib_time_now (vm) > dead_client_scan_time) {
462             vl_api_registration_t **regpp;
463             vl_api_registration_t *regp;
464             unix_shared_memory_queue_t *q;
465             static u32 * dead_indices;
466             static u32 * confused_indices;
467
468             vec_reset_length (dead_indices);
469             vec_reset_length (confused_indices);
470
471             pool_foreach (regpp, am->vl_clients, 
472             ({
473                 regp = *regpp;
474
475                 if (regp) {
476                     q = regp->vl_input_queue;
477                     if (kill (q->consumer_pid, 0) < 0) {
478                         vec_add1(dead_indices, regpp - am->vl_clients);
479                     }
480                 } else {
481                     clib_warning ("NULL client registration index %d",
482                                   regpp - am->vl_clients);
483                     vec_add1 (confused_indices, regpp - am->vl_clients);
484                 }
485             }));
486             /* This should "never happen," but if it does, fix it... */
487             if (PREDICT_FALSE (vec_len(confused_indices) > 0)) {
488                 int i;
489                 for (i = 0; i < vec_len (confused_indices); i++) {
490                     pool_put_index (am->vl_clients, confused_indices[i]);
491                 }
492             }
493
494             if (PREDICT_FALSE (vec_len(dead_indices) > 0)) {
495                 int i;
496                 svm_region_t *svm;
497                 void * oldheap;
498
499                 /* Allow the application to clean up its registrations */
500                 for (i = 0; i < vec_len(dead_indices); i++) {
501                     regpp = pool_elt_at_index (am->vl_clients, dead_indices[i]);
502                     if (regpp) {
503                         u32 handle;
504                         
505                         handle = vl_msg_api_handle_from_index_and_epoch 
506                             (dead_indices[i], shm->application_restarts);
507                         (void) vl_api_memclnt_delete_callback (handle);
508                     }
509                 }
510
511                 svm = am->vlib_rp;
512                 pthread_mutex_lock (&svm->mutex);
513                 oldheap = svm_push_data_heap(svm);
514                 
515                 for (i = 0; i < vec_len(dead_indices); i++) {
516                     regpp = pool_elt_at_index (am->vl_clients, dead_indices[i]);
517                     if (regpp) {
518                         /* Poison the old registration */
519                         memset (*regpp, 0xF3, sizeof (**regpp));
520                         clib_mem_free (*regpp);
521                         /* no dangling references, please */
522                         *regpp = 0;
523                     } else {
524                         svm_pop_heap (oldheap);
525                         clib_warning ("Duplicate free, client index %d", 
526                                       regpp - am->vl_clients);
527                         oldheap = svm_push_data_heap(svm);
528                     }
529                 }
530
531                 svm_client_scan_this_region_nolock (am->vlib_rp);
532
533                 pthread_mutex_unlock(&svm->mutex);
534                 svm_pop_heap (oldheap);
535                 for (i = 0; i < vec_len (dead_indices); i++)
536                     pool_put_index (am->vl_clients, dead_indices[i]);
537             }
538
539             dead_client_scan_time = vlib_time_now (vm) + 20.0;
540         }
541             
542         if (TRACE_VLIB_MEMORY_QUEUE)
543         {
544             ELOG_TYPE_DECLARE (e) = {
545                 .format = "q-awake: len %d",
546                 .format_args = "i4",
547             };
548             struct { u32 len; } * ed;
549             ed = ELOG_DATA (&vm->elog_main, e);
550             ed->len = q->cursize;
551         }
552     }
553
554     return 0;
555 }
556
557 static clib_error_t *
558 vl_api_show_histogram_command(vlib_main_t * vm,
559                               unformat_input_t * input, 
560                               vlib_cli_command_t * cli_cmd)
561 {
562     u64 total_counts = 0;
563     int i;
564     
565     for (i = 0; i < SLEEP_N_BUCKETS; i++) {
566         total_counts += vector_rate_histogram [i];
567     }
568
569     if (total_counts == 0) {
570         vlib_cli_output (vm, "No control-plane activity.");
571         return 0;
572     }
573
574 #define _(n)                                                    \
575     do {                                                        \
576         f64 percent;                                            \
577         percent = ((f64) vector_rate_histogram[SLEEP_##n##_US]) \
578             / (f64) total_counts;                               \
579         percent *= 100.0;                                       \
580         vlib_cli_output (vm, "Sleep %3d us: %llu, %.2f%%",n,    \
581                          vector_rate_histogram[SLEEP_##n##_US], \
582                          percent);                              \
583     } while (0);
584     foreach_histogram_bucket;
585 #undef _
586
587     return 0;
588 }
589
590 VLIB_CLI_COMMAND (cli_show_api_histogram_command, static) = {
591     .path = "show api histogram",
592     .short_help = "show api histogram",
593     .function = vl_api_show_histogram_command,
594 };
595
596 static clib_error_t *
597 vl_api_clear_histogram_command(vlib_main_t * vm,
598                                unformat_input_t * input, 
599                                vlib_cli_command_t * cli_cmd)
600 {
601     int i;
602
603     for (i = 0; i < SLEEP_N_BUCKETS; i++)
604         vector_rate_histogram[i] = 0;
605     return 0;
606 }
607
608 VLIB_CLI_COMMAND (cli_clear_api_histogram_command, static) = {
609     .path = "clear api histogram",
610     .short_help = "clear api histogram",
611     .function = vl_api_clear_histogram_command,
612 };
613
614
615 VLIB_REGISTER_NODE (memclnt_node,static) = {
616     .function = memclnt_process,
617     .type = VLIB_NODE_TYPE_PROCESS,
618     .name = "api-rx-from-ring",
619     .state = VLIB_NODE_STATE_DISABLED,
620 };
621
622 static void 
623 memclnt_queue_callback (vlib_main_t *vm)
624 {
625   static volatile int * cursizep;
626
627   if (PREDICT_FALSE (cursizep == 0))
628     {
629       api_main_t *am = &api_main;
630       vl_shmem_hdr_t *shmem_hdr = am->shmem_hdr;
631       unix_shared_memory_queue_t * q;            
632       
633       if (shmem_hdr == 0)
634         return;
635       
636       q = shmem_hdr->vl_input_queue;
637       if (q == 0)
638         return;
639       cursizep = &q->cursize;
640     }
641   
642   if (*cursizep >= 1)
643     {
644       vm->queue_signal_pending = 1;
645       vm->api_queue_nonempty = 1;
646       vlib_process_signal_event (vm, memclnt_node.index, 
647                                  /* event_type */ 0, /* event_data */ 0);
648     }
649 }
650
651 void vl_enable_disable_memory_api (vlib_main_t *vm, int enable)
652 {
653     vlib_node_set_state (vm, memclnt_node.index,
654                          (enable
655                           ? VLIB_NODE_STATE_POLLING
656                           : VLIB_NODE_STATE_DISABLED));
657 }
658
659 static uword
660 api_rx_from_node (vlib_main_t * vm,
661                   vlib_node_runtime_t * node,
662                   vlib_frame_t * frame)
663 {
664     uword n_packets = frame->n_vectors;
665     uword n_left_from;
666     u32 * from;
667     static u8 * long_msg;
668
669     vec_validate (long_msg, 4095);
670     n_left_from = frame->n_vectors;
671     from = vlib_frame_args (frame);
672
673     while (n_left_from > 0) {
674         u32 bi0;
675         vlib_buffer_t * b0;
676         void * msg;
677         uword msg_len;
678
679         bi0 = from[0];
680         b0 = vlib_get_buffer (vm, bi0);
681         from += 1;
682         n_left_from -= 1;
683
684         msg = b0->data + b0->current_data;
685         msg_len = b0->current_length;
686         if (b0->flags & VLIB_BUFFER_NEXT_PRESENT) {
687             ASSERT (long_msg != 0);
688             _vec_len (long_msg) = 0;
689             vec_add (long_msg, msg, msg_len);
690             while (b0->flags & VLIB_BUFFER_NEXT_PRESENT) {
691                 b0 = vlib_get_buffer (vm, b0->next_buffer);
692                 msg = b0->data + b0->current_data;
693                 msg_len = b0->current_length;
694                 vec_add (long_msg, msg, msg_len);
695             }
696             msg = long_msg;
697         }
698         vl_msg_api_handler_no_trace_no_free (msg);
699     }
700
701     /* Free what we've been given. */
702     vlib_buffer_free (vm, vlib_frame_args (frame), n_packets);
703
704     return n_packets;
705 }
706
707 VLIB_REGISTER_NODE (api_rx_from_node_node,static) = {
708     .function = api_rx_from_node,
709     .type = VLIB_NODE_TYPE_INTERNAL,
710     .vector_size = 4,
711     .name = "api-rx-from-node",
712 };
713
714 static clib_error_t *
715 setup_memclnt_exit (vlib_main_t * vm)
716 {
717     atexit (vl_unmap_shmem);
718     return 0;
719 }
720
721 VLIB_INIT_FUNCTION (setup_memclnt_exit);
722
723
724 static clib_error_t *
725 vl_api_ring_command(vlib_main_t * vm,
726                     unformat_input_t * input, 
727                     vlib_cli_command_t * cli_cmd)
728 {
729     int i;
730     ring_alloc_t *ap;
731     vl_shmem_hdr_t *shmem_hdr;
732     api_main_t *am = &api_main;
733
734     shmem_hdr = am->shmem_hdr;
735
736     if (shmem_hdr == 0) {
737         vlib_cli_output (vm, "Shared memory segment not initialized...\n");
738         return 0;
739     }
740
741     vlib_cli_output (vm, "%8s %8s %8s %8s %8s\n",
742                 "Owner", "Size", "Nitems", "Hits", "Misses");
743
744     ap = shmem_hdr->vl_rings;
745
746     for (i = 0; i < vec_len(shmem_hdr->vl_rings); i++) {
747         vlib_cli_output(vm, "%8s %8d %8d %8d %8d\n", 
748                         "vlib", ap->size, ap->nitems, ap->hits, ap->misses);
749         ap++;
750     }
751
752     ap = shmem_hdr->client_rings;
753
754     for (i = 0; i < vec_len(shmem_hdr->client_rings); i++) {
755         vlib_cli_output(vm, "%8s %8d %8d %8d %8d\n", 
756                         "clnt", ap->size, ap->nitems, ap->hits, ap->misses);
757         ap++;
758     }
759
760     vlib_cli_output (vm, "%d ring miss fallback allocations\n",
761                      am->ring_misses);
762
763     vlib_cli_output (vm, "%d application restarts, %d reclaimed msgs\n",
764                      shmem_hdr->application_restarts,
765                      shmem_hdr->restart_reclaims);
766     return 0;
767 }
768
769 void dump_socket_clients (vlib_main_t *vm, api_main_t *am)
770     __attribute__((weak));
771
772 void dump_socket_clients (vlib_main_t *vm, api_main_t *am)
773 {
774 }
775
776 static clib_error_t *
777 vl_api_client_command(vlib_main_t * vm,
778                       unformat_input_t * input, 
779                       vlib_cli_command_t * cli_cmd)
780 {
781     vl_api_registration_t **regpp, *regp;
782     unix_shared_memory_queue_t *q;
783     char *health;
784     api_main_t *am = &api_main;
785     u32 * confused_indices = 0;
786
787     if (!pool_elts (am->vl_clients))
788         goto socket_clients;
789     vlib_cli_output (vm, "Shared memory clients");
790     vlib_cli_output (vm, "%16s %8s %14s %18s %s",
791                      "Name", "PID", "Queue Length", "Queue VA", "Health");
792     
793     pool_foreach (regpp, am->vl_clients, 
794     ({
795         regp = *regpp;
796         
797         if (regp) {
798             q = regp->vl_input_queue;
799             if (kill (q->consumer_pid, 0) < 0) {
800                 health = "DEAD";
801             } else {
802                 health = "alive";
803             }
804             vlib_cli_output (vm, "%16s %8d %14d 0x%016llx %s\n",
805                              regp->name, q->consumer_pid, q->cursize, 
806                              q, health);
807         } else {
808             clib_warning ("NULL client registration index %d",
809                           regpp - am->vl_clients);
810             vec_add1 (confused_indices, regpp - am->vl_clients);
811         }
812     }));
813
814     /* This should "never happen," but if it does, fix it... */
815     if (PREDICT_FALSE (vec_len(confused_indices) > 0)) {
816         int i;
817         for (i = 0; i < vec_len (confused_indices); i++) {
818             pool_put_index (am->vl_clients, confused_indices[i]);
819         }
820     }
821     vec_free (confused_indices);
822
823     if (am->missing_clients)
824         vlib_cli_output (vm, "%u messages with missing clients",
825                          am->missing_clients);
826 socket_clients:
827     dump_socket_clients (vm, am);
828
829     return 0;
830 }
831
832 VLIB_CLI_COMMAND (cli_show_api_command, static) = {
833     .path = "show api",
834     .short_help = "Show API information",
835 };
836
837 VLIB_CLI_COMMAND (cli_show_api_ring_command, static) = {
838     .path = "show api ring-stats",
839     .short_help = "Message ring statistics",
840     .function = vl_api_ring_command,
841 };
842
843 VLIB_CLI_COMMAND (cli_show_api_clients_command, static) = {
844     .path = "show api clients",
845     .short_help = "Client information",
846     .function = vl_api_client_command,
847 };
848
849 static clib_error_t *
850 vl_api_message_table_command(vlib_main_t * vm,
851                              unformat_input_t * input, 
852                              vlib_cli_command_t * cli_cmd)
853 {
854     api_main_t *am = &api_main;
855     int i;
856     int verbose = 0;
857     
858     if (unformat (input, "verbose"))
859         verbose = 1;
860
861
862     if (verbose == 0)
863         vlib_cli_output (vm, "%-4s %s", "ID", "Name");
864     else
865         vlib_cli_output (vm, "%-4s %-40s %6s %7s", "ID", "Name", "Bounce", 
866                          "MP-safe");
867
868     for (i = 1; i < vec_len (am->msg_names); i++) {
869         if (verbose == 0) {
870             vlib_cli_output (vm, "%-4d %s", i, 
871                              am->msg_names[i] ? am->msg_names[i] : 
872                              "  [no handler]");
873         } else {
874             vlib_cli_output (vm, "%-4d %-40s %6d %7d", i, 
875                              am->msg_names[i] ? am->msg_names[i] : 
876                              "  [no handler]", am->message_bounce[i], 
877                              am->is_mp_safe[i]);
878         }
879     }
880
881     return 0;
882 }
883
884 VLIB_CLI_COMMAND (cli_show_api_message_table_command, static) = {
885     .path = "show api message-table",
886     .short_help = "Message Table",
887     .function = vl_api_message_table_command,
888 };
889
890 void vl_api_trace_print_file_cmd(vlib_main_t *vm, u32 first, u32 last, 
891                                  u8 *filename)
892 {
893     FILE *fp;
894     static vl_api_trace_t *tp = 0;
895     int endian_swap = 0;
896     u32 i;
897     u16 msg_id;
898     static u8 *msg_buf=0;
899     void (*endian_fp)(void *);
900     u8 *(*print_fp)(void *, void *);
901     int size;
902     api_main_t *am = &api_main;
903
904     /*
905      * On-demand: allocate enough space for the largest message 
906      */
907     if (msg_buf == 0) {
908         vec_validate(tp, 0);
909         int max_size = 0;
910         for (i = 0; i < vec_len(am->api_trace_cfg); i++) {
911             if (am->api_trace_cfg[i].size > max_size)
912                 max_size = am->api_trace_cfg[i].size;
913         }
914         /* round size to a multiple of the cache-line size */
915         max_size = (max_size + (CLIB_CACHE_LINE_BYTES-1)) & 
916             (~(CLIB_CACHE_LINE_BYTES-1));
917         vec_validate (msg_buf, max_size-1);
918     }
919
920     fp = fopen ((char *)filename, "r");
921
922     if (fp == NULL) {
923         vlib_cli_output (vm, "Couldn't open %s\n", filename);
924         return;
925     }
926
927     /* first, fish the header record from the file */
928
929     if (fread(tp, sizeof(*tp), 1, fp) != 1) {
930         fclose(fp);
931         vlib_cli_output (vm, "Header read error\n");
932         return;
933     }
934
935     /* Endian swap required? */
936     if (clib_arch_is_big_endian != tp->endian) {
937         endian_swap = 1;
938     }
939     
940     for (i = 0; i <= last; i++) {
941         /* First 2 bytes are the message type */
942         if (fread(&msg_id, sizeof(u16), 1, fp) != 1) {
943             break;
944         }
945         msg_id = ntohs(msg_id);
946
947         fseek(fp, -2, SEEK_CUR);
948         
949         /* Mild sanity check */
950         if (msg_id >= vec_len(am->msg_handlers)) {
951             fclose(fp);
952             vlib_cli_output (vm, "msg_id %d out of bounds\n",
953                              msg_id);
954             return;
955         }
956
957         size = am->api_trace_cfg[msg_id].size;
958         
959         if (fread(msg_buf, size, 1, fp) != 1) {
960             fclose(fp);
961             vlib_cli_output (vm, "read error on %s\n", filename);
962             return;
963         }
964
965         if (i < first)
966             continue;
967
968         if (endian_swap) {
969             endian_fp = am->msg_endian_handlers[msg_id];
970             (*endian_fp)(msg_buf);
971         }
972             
973         vlib_cli_output (vm, "[%d]: %s\n", i, 
974                          am->msg_names[msg_id]);
975         
976         print_fp = (void *)am->msg_print_handlers[msg_id];
977         (*print_fp)(msg_buf, vm);
978         vlib_cli_output (vm, "-------------\n");
979     }
980     fclose(fp);
981 }
982
983 static clib_error_t *
984 vl_api_trace_command(vlib_main_t * vm,
985                      unformat_input_t * input, 
986                      vlib_cli_command_t * cli_cmd)
987 {
988     u32 nitems=1024;
989     vl_api_trace_which_t which=VL_API_TRACE_RX;
990     u8 *filename;
991     u32 first = 0;
992     u32 last  = ~0;
993     api_main_t *am = &api_main;
994
995     while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) {
996         if (unformat (input, "rx nitems %u", &nitems)
997             || unformat (input, "rx"))
998             goto configure;
999         else if (unformat (input, "tx nitems %u", &nitems) 
1000                  || unformat (input, "tx")) {
1001             which = VL_API_TRACE_RX;
1002             goto configure;
1003         } else if (unformat (input, "on rx")) {
1004             vl_msg_api_trace_onoff (am, VL_API_TRACE_RX, 1);
1005         } else if (unformat (input, "on tx")) {
1006             vl_msg_api_trace_onoff (am, VL_API_TRACE_TX, 1);
1007         } else if (unformat (input, "on")) {
1008             vl_msg_api_trace_onoff (am, VL_API_TRACE_RX, 1);
1009         } else if (unformat (input, "off")) {
1010             vl_msg_api_trace_onoff (am, VL_API_TRACE_RX, 0);
1011             vl_msg_api_trace_onoff (am, VL_API_TRACE_TX, 0);
1012         } else if (unformat (input, "free")) {
1013             vl_msg_api_trace_onoff (am, VL_API_TRACE_RX, 0);
1014             vl_msg_api_trace_onoff (am, VL_API_TRACE_TX, 0);
1015             vl_msg_api_trace_free (am, VL_API_TRACE_RX);
1016             vl_msg_api_trace_free (am, VL_API_TRACE_TX);
1017         } else if (unformat (input, "print %s from %d to %d", &filename,
1018                              &first, &last)
1019                    || unformat (input, "print %s", &filename)) {
1020             goto print;
1021         } else if (unformat (input, "debug on")) {
1022             am->msg_print_flag = 1;
1023         } else if (unformat (input, "debug off")) {
1024             am->msg_print_flag = 0;
1025         }
1026         else
1027             return clib_error_return (0, "unknown input `%U'",
1028                                       format_unformat_error, input);
1029     }
1030     return 0;
1031
1032  print:
1033     vl_api_trace_print_file_cmd (vm, first, last, filename);
1034     goto out;
1035
1036  configure:
1037     if (vl_msg_api_trace_configure (am, which, nitems)) {
1038         vlib_cli_output (vm, "warning: trace configure error (%d, %d)", 
1039                          which, nitems);
1040     }
1041
1042  out:
1043     return 0;
1044 }
1045
1046 VLIB_CLI_COMMAND (trace, static) = {
1047     .path = "set api-trace",
1048     .short_help = "API trace",
1049     .function = vl_api_trace_command,
1050 };
1051
1052 clib_error_t *
1053 vlibmemory_init (vlib_main_t * vm)
1054 {
1055   api_main_t *am = &api_main;
1056   /* Normally NULL / 0, set by cmd line "api-segment" */
1057   svm_region_init_chroot_uid_gid (am->root_path, am->api_uid, am->api_gid);
1058   return 0;
1059 }
1060
1061 VLIB_INIT_FUNCTION (vlibmemory_init);
1062
1063 void vl_set_memory_region_name (char *name)
1064 {
1065     api_main_t *am = &api_main;
1066
1067     am->region_name = name;
1068 }
1069
1070 static int range_compare (vl_api_msg_range_t * a0, vl_api_msg_range_t * a1)
1071 {
1072     int len0, len1, clen;
1073
1074     len0 = vec_len (a0->name);
1075     len1 = vec_len (a1->name);
1076     clen = len0 < len1 ? len0 : len1;
1077     return (strncmp ((char *) a0->name, (char *)a1->name, clen)); 
1078 }
1079
1080 static u8 * format_api_msg_range (u8 * s, va_list * args)
1081 {
1082     vl_api_msg_range_t * rp = va_arg (*args, vl_api_msg_range_t *);
1083
1084     if (rp == 0)
1085         s = format (s, "%-20s%9s%9s", "Name", "First-ID", "Last-ID");
1086     else
1087         s = format (s, "%-20s%9d%9d", rp->name, rp->first_msg_id,
1088                     rp->last_msg_id);
1089             
1090     return s;
1091 }
1092
1093 static clib_error_t *
1094 vl_api_show_plugin_command(vlib_main_t * vm,
1095                            unformat_input_t * input, 
1096                            vlib_cli_command_t * cli_cmd)
1097 {
1098     api_main_t *am = &api_main;
1099     vl_api_msg_range_t * rp = 0;
1100     int i;
1101     
1102     if (vec_len (am->msg_ranges) == 0) {
1103         vlib_cli_output (vm, "No plugin API message ranges configured...");
1104         return 0;
1105     }
1106         
1107     rp = vec_dup (am->msg_ranges);
1108
1109     vec_sort_with_function (rp, range_compare);
1110     
1111     vlib_cli_output (vm, "Plugin API message ID ranges...\n");
1112     vlib_cli_output (vm, "%U", format_api_msg_range, 0 /* header */);
1113
1114     for (i = 0; i < vec_len (rp); i++)
1115         vlib_cli_output (vm, "%U", format_api_msg_range, rp+i);
1116
1117     return 0;
1118 }
1119
1120 VLIB_CLI_COMMAND (cli_show_api_plugin_command, static) = {
1121     .path = "show api plugin",
1122     .short_help = "show api plugin",
1123     .function = vl_api_show_plugin_command,
1124 };
1125
1126 static void vl_api_rpc_call_t_handler (vl_api_rpc_call_t * mp)
1127 {
1128   vl_api_rpc_reply_t * rmp;
1129   int (*fp)(void *);
1130   i32 rv = 0;
1131   vlib_main_t * vm = vlib_get_main();
1132
1133   if (mp->function == 0)
1134     {
1135       rv = -1;
1136       clib_warning ("rpc NULL function pointer");
1137     }
1138   
1139   else
1140     {
1141       if (mp->need_barrier_sync)
1142         vlib_worker_thread_barrier_sync (vm);
1143
1144       fp = uword_to_pointer(mp->function, int(*)(void *));
1145       rv = fp(mp->data);
1146
1147       if (mp->need_barrier_sync)
1148         vlib_worker_thread_barrier_release (vm);
1149     }
1150
1151   if (mp->send_reply)
1152     {
1153       unix_shared_memory_queue_t * q =
1154         vl_api_client_index_to_input_queue (mp->client_index);
1155       if (q)
1156         {
1157           rmp = vl_msg_api_alloc_as_if_client (sizeof (*rmp));
1158           rmp->_vl_msg_id = ntohs (VL_API_RPC_REPLY);
1159           rmp->context = mp->context;
1160           rmp->retval = rv;
1161           vl_msg_api_send_shmem (q, (u8 *)&rmp);
1162         }
1163     }
1164   if (mp->multicast)
1165     {
1166       clib_warning ("multicast not yet implemented...");
1167     }
1168 }
1169
1170 static void vl_api_rpc_reply_t_handler (vl_api_rpc_reply_t * mp)
1171 { clib_warning ("unimplemented"); }
1172
1173 void vl_api_rpc_call_main_thread (void *fp, u8 * data, u32 data_length)
1174 {
1175   vl_api_rpc_call_t * mp;
1176   api_main_t *am = &api_main;
1177   vl_shmem_hdr_t *shmem_hdr = am->shmem_hdr;
1178
1179   mp = vl_msg_api_alloc_as_if_client (sizeof (*mp) + data_length);
1180   memset (mp, 0, sizeof (*mp));
1181   clib_memcpy (mp->data, data, data_length);
1182   mp->_vl_msg_id = ntohs (VL_API_RPC_CALL);
1183   mp->function = pointer_to_uword(fp);
1184   mp->need_barrier_sync = 1;
1185   
1186   /* Use the "normal" control-plane mechanism for the main thread */
1187   vl_msg_api_send_shmem (shmem_hdr->vl_input_queue, (u8 *)&mp);
1188 }
1189
1190 #define foreach_rpc_api_msg                     \
1191 _(RPC_CALL,rpc_call)                            \
1192 _(RPC_REPLY,rpc_reply)
1193
1194 static clib_error_t *
1195 rpc_api_hookup (vlib_main_t *vm)
1196 {
1197 #define _(N,n)                                                  \
1198     vl_msg_api_set_handlers(VL_API_##N, #n,                     \
1199                            vl_api_##n##_t_handler,              \
1200                            vl_noop_handler,                     \
1201                            vl_noop_handler,                     \
1202                            vl_api_##n##_t_print,                \
1203                            sizeof(vl_api_##n##_t), 0 /* do not trace */); 
1204     foreach_rpc_api_msg;
1205 #undef _
1206     return 0;
1207 }
1208
1209 VLIB_API_INIT_FUNCTION(rpc_api_hookup);