Initial commit of vpp code.
[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_signal (int signum);
347 static void memclnt_queue_callback (vlib_main_t *vm);
348
349 static uword
350 memclnt_process (vlib_main_t * vm,
351                  vlib_node_runtime_t * node,
352                  vlib_frame_t * f)
353 {
354     uword mp;
355     vl_shmem_hdr_t *shm;
356     unix_shared_memory_queue_t *q;
357     clib_error_t *e;
358     int rv;
359     api_main_t *am = &api_main;
360     f64 dead_client_scan_time;
361     f64 sleep_time, start_time;
362     f64 vector_rate;
363     
364     vlib_set_queue_signal_callback (vm, memclnt_queue_callback);
365     am->vlib_signal = SIGUSR1;
366     signal (am->vlib_signal, memclnt_queue_signal);
367     
368     if ((rv = memory_api_init(am->region_name)) < 0) {
369         clib_warning("memory_api_init returned %d, wait for godot...", rv);
370         vlib_process_suspend (vm, 1e70);
371     }
372
373     shm = am->shmem_hdr;
374     ASSERT(shm);
375     q = shm->vl_input_queue;
376     ASSERT(q);
377
378     e = vlib_call_init_exit_functions 
379         (vm, vm->api_init_function_registrations, 1 /* call_once */);
380     if (e)
381       clib_error_report (e);
382
383     sleep_time = 20.0;
384     dead_client_scan_time = vlib_time_now(vm) + 20.0;
385
386     /* $$$ pay attention to frame size, control CPU usage */
387     while (1) {
388         uword event_type __attribute__((unused));
389         i8 *headp;
390         int need_broadcast;
391
392         /*
393          * There's a reason for checking the queue before
394          * sleeping. If the vlib application crashes, it's entirely
395          * possible for a client to enqueue a connect request
396          * during the process restart interval.
397          *
398          * Unless some force of physics causes the new incarnation
399          * of the application to process the request, the client will
400          * sit and wait for Godot...
401          */
402         vector_rate = vlib_last_vector_length_per_node(vm);
403         start_time = vlib_time_now (vm);
404         while (1) {
405             pthread_mutex_lock (&q->mutex);
406             if (q->cursize == 0) {
407                 pthread_mutex_unlock (&q->mutex);
408                 
409                 if (TRACE_VLIB_MEMORY_QUEUE)
410                 {
411                     ELOG_TYPE_DECLARE (e) = {
412                         .format = "q-underflow: len %d",
413                         .format_args = "i4",
414                     };
415                     struct { u32 len; } * ed;
416                     ed = ELOG_DATA (&vm->elog_main, e);
417                     ed->len = 0;
418                 }
419                 sleep_time = 20.0;
420                 break;
421             }
422             
423             headp = (i8 *) (q->data + sizeof(uword)*q->head);
424             memcpy (&mp, headp, sizeof(uword));
425             
426             q->head++;
427             need_broadcast = (q->cursize == q->maxsize/2);
428             q->cursize--;
429             
430             if (PREDICT_FALSE(q->head == q->maxsize))
431                 q->head = 0;
432             pthread_mutex_unlock(&q->mutex);
433             if (need_broadcast)
434                 (void) pthread_cond_broadcast(&q->condvar);
435             
436             vl_msg_api_handler_with_vm_node (am, (void *)mp, vm, node);
437
438             /* Allow no more than 10us without a pause */
439             if (vlib_time_now(vm) > start_time + 10e-6) {
440                 int index = SLEEP_400_US;
441                 if (vector_rate > 40.0) 
442                     sleep_time = 400e-6;
443                 else if (vector_rate > 20.0) {
444                     index = SLEEP_200_US;
445                     sleep_time = 200e-6;
446                 } else if (vector_rate >= 1.0) {
447                     index = SLEEP_100_US;
448                     sleep_time = 100e-6;
449                 }
450                 else {
451                     index = SLEEP_10_US;
452                     sleep_time = 10e-6;
453                 }
454                 vector_rate_histogram[index] += 1;
455                 break;
456             }
457         }
458
459         event_type = vlib_process_wait_for_event_or_clock (vm, sleep_time);
460         vlib_process_get_events (vm, 0 /* event_data */);
461
462         if (vlib_time_now (vm) > dead_client_scan_time) {
463             vl_api_registration_t **regpp;
464             vl_api_registration_t *regp;
465             unix_shared_memory_queue_t *q;
466             static u32 * dead_indices;
467             static u32 * confused_indices;
468
469             vec_reset_length (dead_indices);
470             vec_reset_length (confused_indices);
471
472             pool_foreach (regpp, am->vl_clients, 
473             ({
474                 regp = *regpp;
475
476                 if (regp) {
477                     q = regp->vl_input_queue;
478                     if (kill (q->consumer_pid, 0) < 0) {
479                         vec_add1(dead_indices, regpp - am->vl_clients);
480                     }
481                 } else {
482                     clib_warning ("NULL client registration index %d",
483                                   regpp - am->vl_clients);
484                     vec_add1 (confused_indices, regpp - am->vl_clients);
485                 }
486             }));
487             /* This should "never happen," but if it does, fix it... */
488             if (PREDICT_FALSE (vec_len(confused_indices) > 0)) {
489                 int i;
490                 for (i = 0; i < vec_len (confused_indices); i++) {
491                     pool_put_index (am->vl_clients, confused_indices[i]);
492                 }
493             }
494
495             if (PREDICT_FALSE (vec_len(dead_indices) > 0)) {
496                 int i;
497                 svm_region_t *svm;
498                 void * oldheap;
499
500                 /* Allow the application to clean up its registrations */
501                 for (i = 0; i < vec_len(dead_indices); i++) {
502                     regpp = pool_elt_at_index (am->vl_clients, dead_indices[i]);
503                     if (regpp) {
504                         u32 handle;
505                         
506                         handle = vl_msg_api_handle_from_index_and_epoch 
507                             (dead_indices[i], shm->application_restarts);
508                         (void) vl_api_memclnt_delete_callback (handle);
509                     }
510                 }
511
512                 svm = am->vlib_rp;
513                 pthread_mutex_lock (&svm->mutex);
514                 oldheap = svm_push_data_heap(svm);
515                 
516                 for (i = 0; i < vec_len(dead_indices); i++) {
517                     regpp = pool_elt_at_index (am->vl_clients, dead_indices[i]);
518                     if (regpp) {
519                         /* Poison the old registration */
520                         memset (*regpp, 0xF3, sizeof (**regpp));
521                         clib_mem_free (*regpp);
522                         /* no dangling references, please */
523                         *regpp = 0;
524                     } else {
525                         svm_pop_heap (oldheap);
526                         clib_warning ("Duplicate free, client index %d", 
527                                       regpp - am->vl_clients);
528                         oldheap = svm_push_data_heap(svm);
529                     }
530                 }
531
532                 svm_client_scan_this_region_nolock (am->vlib_rp);
533
534                 pthread_mutex_unlock(&svm->mutex);
535                 svm_pop_heap (oldheap);
536                 for (i = 0; i < vec_len (dead_indices); i++)
537                     pool_put_index (am->vl_clients, dead_indices[i]);
538             }
539
540             dead_client_scan_time = vlib_time_now (vm) + 20.0;
541         }
542             
543         if (TRACE_VLIB_MEMORY_QUEUE)
544         {
545             ELOG_TYPE_DECLARE (e) = {
546                 .format = "q-awake: len %d",
547                 .format_args = "i4",
548             };
549             struct { u32 len; } * ed;
550             ed = ELOG_DATA (&vm->elog_main, e);
551             ed->len = q->cursize;
552         }
553     }
554
555     return 0;
556 }
557
558 static clib_error_t *
559 vl_api_show_histogram_command(vlib_main_t * vm,
560                               unformat_input_t * input, 
561                               vlib_cli_command_t * cli_cmd)
562 {
563     u64 total_counts = 0;
564     int i;
565     
566     for (i = 0; i < SLEEP_N_BUCKETS; i++) {
567         total_counts += vector_rate_histogram [i];
568     }
569
570     if (total_counts == 0) {
571         vlib_cli_output (vm, "No control-plane activity.");
572         return 0;
573     }
574
575 #define _(n)                                                    \
576     do {                                                        \
577         f64 percent;                                            \
578         percent = ((f64) vector_rate_histogram[SLEEP_##n##_US]) \
579             / (f64) total_counts;                               \
580         percent *= 100.0;                                       \
581         vlib_cli_output (vm, "Sleep %3d us: %llu, %.2f%%",n,    \
582                          vector_rate_histogram[SLEEP_##n##_US], \
583                          percent);                              \
584     } while (0);
585     foreach_histogram_bucket;
586 #undef _
587
588     return 0;
589 }
590
591 VLIB_CLI_COMMAND (cli_show_api_histogram_command, static) = {
592     .path = "show api histogram",
593     .short_help = "show api histogram",
594     .function = vl_api_show_histogram_command,
595 };
596
597 static clib_error_t *
598 vl_api_clear_histogram_command(vlib_main_t * vm,
599                                unformat_input_t * input, 
600                                vlib_cli_command_t * cli_cmd)
601 {
602     int i;
603
604     for (i = 0; i < SLEEP_N_BUCKETS; i++)
605         vector_rate_histogram[i] = 0;
606     return 0;
607 }
608
609 VLIB_CLI_COMMAND (cli_clear_api_histogram_command, static) = {
610     .path = "clear api histogram",
611     .short_help = "clear api histogram",
612     .function = vl_api_clear_histogram_command,
613 };
614
615
616 VLIB_REGISTER_NODE (memclnt_node,static) = {
617     .function = memclnt_process,
618     .type = VLIB_NODE_TYPE_PROCESS,
619     .name = "api-rx-from-ring",
620     .state = VLIB_NODE_STATE_DISABLED,
621 };
622
623 static void
624 memclnt_queue_signal (int signum)
625 {
626     vlib_main_t * vm = vlib_get_main();
627
628     vm->queue_signal_pending = 1;
629 }
630
631 static void 
632 memclnt_queue_callback (vlib_main_t *vm)
633 {
634 #if 0
635     /* If we need to manually suspend / resume the memclnt process */
636     vlib_node_t * n = vlib_get_node (vm, memclnt_node.index);
637     vlib_process_t * p = vlib_get_process_from_node (vm, n);
638 #endif
639
640     vm->queue_signal_pending = 0;
641     vlib_process_signal_event 
642         (vm, memclnt_node.index, /* event_type */ 0, /* event_data */ 0);
643 }
644
645 void vl_enable_disable_memory_api (vlib_main_t *vm, int enable)
646 {
647     vlib_node_set_state (vm, memclnt_node.index,
648                          (enable
649                           ? VLIB_NODE_STATE_POLLING
650                           : VLIB_NODE_STATE_DISABLED));
651 }
652
653 static uword
654 api_rx_from_node (vlib_main_t * vm,
655                   vlib_node_runtime_t * node,
656                   vlib_frame_t * frame)
657 {
658     uword n_packets = frame->n_vectors;
659     uword n_left_from;
660     u32 * from;
661     static u8 * long_msg;
662
663     vec_validate (long_msg, 4095);
664     n_left_from = frame->n_vectors;
665     from = vlib_frame_args (frame);
666
667     while (n_left_from > 0) {
668         u32 bi0;
669         vlib_buffer_t * b0;
670         void * msg;
671         uword msg_len;
672
673         bi0 = from[0];
674         b0 = vlib_get_buffer (vm, bi0);
675         from += 1;
676         n_left_from -= 1;
677
678         msg = b0->data + b0->current_data;
679         msg_len = b0->current_length;
680         if (b0->flags & VLIB_BUFFER_NEXT_PRESENT) {
681             ASSERT (long_msg != 0);
682             _vec_len (long_msg) = 0;
683             vec_add (long_msg, msg, msg_len);
684             while (b0->flags & VLIB_BUFFER_NEXT_PRESENT) {
685                 b0 = vlib_get_buffer (vm, b0->next_buffer);
686                 msg = b0->data + b0->current_data;
687                 msg_len = b0->current_length;
688                 vec_add (long_msg, msg, msg_len);
689             }
690             msg = long_msg;
691         }
692         vl_msg_api_handler_no_trace_no_free (msg);
693     }
694
695     /* Free what we've been given. */
696     vlib_buffer_free (vm, vlib_frame_args (frame), n_packets);
697
698     return n_packets;
699 }
700
701 VLIB_REGISTER_NODE (api_rx_from_node_node,static) = {
702     .function = api_rx_from_node,
703     .type = VLIB_NODE_TYPE_INTERNAL,
704     .vector_size = 4,
705     .name = "api-rx-from-node",
706 };
707
708 static clib_error_t *
709 setup_memclnt_exit (vlib_main_t * vm)
710 {
711     atexit (vl_unmap_shmem);
712     return 0;
713 }
714
715 VLIB_INIT_FUNCTION (setup_memclnt_exit);
716
717
718 static clib_error_t *
719 vl_api_ring_command(vlib_main_t * vm,
720                     unformat_input_t * input, 
721                     vlib_cli_command_t * cli_cmd)
722 {
723     int i;
724     ring_alloc_t *ap;
725     vl_shmem_hdr_t *shmem_hdr;
726     api_main_t *am = &api_main;
727
728     shmem_hdr = am->shmem_hdr;
729
730     if (shmem_hdr == 0) {
731         vlib_cli_output (vm, "Shared memory segment not initialized...\n");
732         return 0;
733     }
734
735     vlib_cli_output (vm, "%8s %8s %8s %8s %8s\n",
736                 "Owner", "Size", "Nitems", "Hits", "Misses");
737
738     ap = shmem_hdr->vl_rings;
739
740     for (i = 0; i < vec_len(shmem_hdr->vl_rings); i++) {
741         vlib_cli_output(vm, "%8s %8d %8d %8d %8d\n", 
742                         "vlib", ap->size, ap->nitems, ap->hits, ap->misses);
743         ap++;
744     }
745
746     ap = shmem_hdr->client_rings;
747
748     for (i = 0; i < vec_len(shmem_hdr->client_rings); i++) {
749         vlib_cli_output(vm, "%8s %8d %8d %8d %8d\n", 
750                         "clnt", ap->size, ap->nitems, ap->hits, ap->misses);
751         ap++;
752     }
753
754     vlib_cli_output (vm, "%d ring miss fallback allocations\n",
755                      am->ring_misses);
756
757     vlib_cli_output (vm, "%d application restarts, %d reclaimed msgs\n",
758                      shmem_hdr->application_restarts,
759                      shmem_hdr->restart_reclaims);
760     return 0;
761 }
762
763 void dump_socket_clients (vlib_main_t *vm, api_main_t *am)
764     __attribute__((weak));
765
766 void dump_socket_clients (vlib_main_t *vm, api_main_t *am)
767 {
768 }
769
770 static clib_error_t *
771 vl_api_client_command(vlib_main_t * vm,
772                       unformat_input_t * input, 
773                       vlib_cli_command_t * cli_cmd)
774 {
775     vl_api_registration_t **regpp, *regp;
776     unix_shared_memory_queue_t *q;
777     char *health;
778     api_main_t *am = &api_main;
779     u32 * confused_indices = 0;
780
781     if (!pool_elts (am->vl_clients))
782         goto socket_clients;
783     vlib_cli_output (vm, "Shared memory clients");
784     vlib_cli_output (vm, "%16s %8s %14s %18s %s",
785                      "Name", "PID", "Queue Length", "Queue VA", "Health");
786     
787     pool_foreach (regpp, am->vl_clients, 
788     ({
789         regp = *regpp;
790         
791         if (regp) {
792             q = regp->vl_input_queue;
793             if (kill (q->consumer_pid, 0) < 0) {
794                 health = "DEAD";
795             } else {
796                 health = "alive";
797             }
798             vlib_cli_output (vm, "%16s %8d %14d 0x%016llx %s\n",
799                              regp->name, q->consumer_pid, q->cursize, 
800                              q, health);
801         } else {
802             clib_warning ("NULL client registration index %d",
803                           regpp - am->vl_clients);
804             vec_add1 (confused_indices, regpp - am->vl_clients);
805         }
806     }));
807
808     /* This should "never happen," but if it does, fix it... */
809     if (PREDICT_FALSE (vec_len(confused_indices) > 0)) {
810         int i;
811         for (i = 0; i < vec_len (confused_indices); i++) {
812             pool_put_index (am->vl_clients, confused_indices[i]);
813         }
814     }
815     vec_free (confused_indices);
816
817     if (am->missing_clients)
818         vlib_cli_output (vm, "%u messages with missing clients",
819                          am->missing_clients);
820 socket_clients:
821     dump_socket_clients (vm, am);
822
823     return 0;
824 }
825
826 VLIB_CLI_COMMAND (cli_show_api_command, static) = {
827     .path = "show api",
828     .short_help = "Show API information",
829 };
830
831 VLIB_CLI_COMMAND (cli_show_api_ring_command, static) = {
832     .path = "show api ring-stats",
833     .short_help = "Message ring statistics",
834     .function = vl_api_ring_command,
835 };
836
837 VLIB_CLI_COMMAND (cli_show_api_clients_command, static) = {
838     .path = "show api clients",
839     .short_help = "Client information",
840     .function = vl_api_client_command,
841 };
842
843 static clib_error_t *
844 vl_api_message_table_command(vlib_main_t * vm,
845                              unformat_input_t * input, 
846                              vlib_cli_command_t * cli_cmd)
847 {
848     api_main_t *am = &api_main;
849     int i;
850     int verbose = 0;
851     
852     if (unformat (input, "verbose"))
853         verbose = 1;
854
855
856     if (verbose == 0)
857         vlib_cli_output (vm, "%-4s %s", "ID", "Name");
858     else
859         vlib_cli_output (vm, "%-4s %-40s %6s %7s", "ID", "Name", "Bounce", 
860                          "MP-safe");
861
862     for (i = 1; i < vec_len (am->msg_names); i++) {
863         if (verbose == 0) {
864             vlib_cli_output (vm, "%-4d %s", i, 
865                              am->msg_names[i] ? am->msg_names[i] : 
866                              "  [no handler]");
867         } else {
868             vlib_cli_output (vm, "%-4d %-40s %6d %7d", i, 
869                              am->msg_names[i] ? am->msg_names[i] : 
870                              "  [no handler]", am->message_bounce[i], 
871                              am->is_mp_safe[i]);
872         }
873     }
874
875     return 0;
876 }
877
878 VLIB_CLI_COMMAND (cli_show_api_message_table_command, static) = {
879     .path = "show api message-table",
880     .short_help = "Message Table",
881     .function = vl_api_message_table_command,
882 };
883
884 void vl_api_trace_print_file_cmd(vlib_main_t *vm, u32 first, u32 last, 
885                                  u8 *filename)
886 {
887     FILE *fp;
888     static vl_api_trace_t *tp = 0;
889     int endian_swap = 0;
890     u32 i;
891     u16 msg_id;
892     static u8 *msg_buf=0;
893     void (*endian_fp)(void *);
894     u8 *(*print_fp)(void *, void *);
895     int size;
896     api_main_t *am = &api_main;
897
898     /*
899      * On-demand: allocate enough space for the largest message 
900      */
901     if (msg_buf == 0) {
902         vec_validate(tp, 0);
903         int max_size = 0;
904         for (i = 0; i < vec_len(am->api_trace_cfg); i++) {
905             if (am->api_trace_cfg[i].size > max_size)
906                 max_size = am->api_trace_cfg[i].size;
907         }
908         /* round size to a multiple of the cache-line size */
909         max_size = (max_size + (CLIB_CACHE_LINE_BYTES-1)) & 
910             (~(CLIB_CACHE_LINE_BYTES-1));
911         vec_validate (msg_buf, max_size-1);
912     }
913
914     fp = fopen ((char *)filename, "r");
915
916     if (fp == NULL) {
917         vlib_cli_output (vm, "Couldn't open %s\n", filename);
918         return;
919     }
920
921     /* first, fish the header record from the file */
922
923     if (fread(tp, sizeof(*tp), 1, fp) != 1) {
924         fclose(fp);
925         vlib_cli_output (vm, "Header read error\n");
926         return;
927     }
928
929     /* Endian swap required? */
930     if (clib_arch_is_big_endian != tp->endian) {
931         endian_swap = 1;
932     }
933     
934     for (i = 0; i <= last; i++) {
935         /* First 2 bytes are the message type */
936         if (fread(&msg_id, sizeof(u16), 1, fp) != 1) {
937             break;
938         }
939         msg_id = ntohs(msg_id);
940
941         fseek(fp, -2, SEEK_CUR);
942         
943         /* Mild sanity check */
944         if (msg_id >= vec_len(am->msg_handlers)) {
945             fclose(fp);
946             vlib_cli_output (vm, "msg_id %d out of bounds\n",
947                              msg_id);
948             return;
949         }
950
951         size = am->api_trace_cfg[msg_id].size;
952         
953         if (fread(msg_buf, size, 1, fp) != 1) {
954             fclose(fp);
955             vlib_cli_output (vm, "read error on %s\n", filename);
956             return;
957         }
958
959         if (i < first)
960             continue;
961
962         if (endian_swap) {
963             endian_fp = am->msg_endian_handlers[msg_id];
964             (*endian_fp)(msg_buf);
965         }
966             
967         vlib_cli_output (vm, "[%d]: %s\n", i, 
968                          am->msg_names[msg_id]);
969         
970         print_fp = (void *)am->msg_print_handlers[msg_id];
971         (*print_fp)(msg_buf, vm);
972         vlib_cli_output (vm, "-------------\n");
973     }
974     fclose(fp);
975 }
976
977 static clib_error_t *
978 vl_api_trace_command(vlib_main_t * vm,
979                      unformat_input_t * input, 
980                      vlib_cli_command_t * cli_cmd)
981 {
982     u32 nitems=1024;
983     vl_api_trace_which_t which=VL_API_TRACE_RX;
984     u8 *filename;
985     u32 first = 0;
986     u32 last  = ~0;
987     api_main_t *am = &api_main;
988
989     while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) {
990         if (unformat (input, "rx nitems %u", &nitems)
991             || unformat (input, "rx"))
992             goto configure;
993         else if (unformat (input, "tx nitems %u", &nitems) 
994                  || unformat (input, "tx")) {
995             which = VL_API_TRACE_RX;
996             goto configure;
997         } else if (unformat (input, "on rx")) {
998             vl_msg_api_trace_onoff (am, VL_API_TRACE_RX, 1);
999         } else if (unformat (input, "on tx")) {
1000             vl_msg_api_trace_onoff (am, VL_API_TRACE_TX, 1);
1001         } else if (unformat (input, "on")) {
1002             vl_msg_api_trace_onoff (am, VL_API_TRACE_RX, 1);
1003         } else if (unformat (input, "off")) {
1004             vl_msg_api_trace_onoff (am, VL_API_TRACE_RX, 0);
1005             vl_msg_api_trace_onoff (am, VL_API_TRACE_TX, 0);
1006         } else if (unformat (input, "free")) {
1007             vl_msg_api_trace_onoff (am, VL_API_TRACE_RX, 0);
1008             vl_msg_api_trace_onoff (am, VL_API_TRACE_TX, 0);
1009             vl_msg_api_trace_free (am, VL_API_TRACE_RX);
1010             vl_msg_api_trace_free (am, VL_API_TRACE_TX);
1011         } else if (unformat (input, "print %s from %d to %d", &filename,
1012                              &first, &last)
1013                    || unformat (input, "print %s", &filename)) {
1014             goto print;
1015         } else if (unformat (input, "debug on")) {
1016             am->msg_print_flag = 1;
1017         } else if (unformat (input, "debug off")) {
1018             am->msg_print_flag = 0;
1019         }
1020         else
1021             return clib_error_return (0, "unknown input `%U'",
1022                                       format_unformat_error, input);
1023     }
1024     return 0;
1025
1026  print:
1027     vl_api_trace_print_file_cmd (vm, first, last, filename);
1028     goto out;
1029
1030  configure:
1031     if (vl_msg_api_trace_configure (am, which, nitems)) {
1032         vlib_cli_output (vm, "warning: trace configure error (%d, %d)", 
1033                          which, nitems);
1034     }
1035
1036  out:
1037     return 0;
1038 }
1039
1040 VLIB_CLI_COMMAND (trace, static) = {
1041     .path = "set api-trace",
1042     .short_help = "API trace",
1043     .function = vl_api_trace_command,
1044 };
1045
1046 clib_error_t *
1047 vlibmemory_init (vlib_main_t * vm)
1048 {
1049     /* Do this early, to avoid glibc malloc fubar */
1050     svm_region_init();
1051     return 0;
1052 }
1053
1054 VLIB_INIT_FUNCTION (vlibmemory_init);
1055
1056 void vl_set_memory_region_name (char *name)
1057 {
1058     api_main_t *am = &api_main;
1059
1060     am->region_name = name;
1061 }
1062
1063 void vl_set_memory_root_path (char *name)
1064 {
1065     api_main_t *am = &api_main;
1066
1067     am->root_path = 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 };