blob: 0fef64c8dece57b9643b6625160d54fe29a951f7 [file] [log] [blame]
Ed Warnickecb9cada2015-12-08 15:45:58 -07001/*
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
53static inline void *
54vl_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
64static inline void *
65vl_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
78void vl_socket_api_send (vl_api_registration_t *rp, u8 *elem)
79 __attribute__((weak));
80
81void
82vl_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
90void 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
99int vl_msg_api_version_check (vl_api_memclnt_create_t * mp)
100 __attribute__((weak));
101
102int vl_msg_api_version_check (vl_api_memclnt_create_t * mp) { return 0; }
103
104/*
105 * vl_api_memclnt_create_t_handler
106 */
107void 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 */
182int vl_api_memclnt_delete_callback (u32 client_index)
183 __attribute__((weak));
184
185int vl_api_memclnt_delete_callback (u32 client_index)
186{ return 0; }
187
188/*
189 * vl_api_memclnt_delete_t_handler
190 */
191void 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
257void 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
286out:
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 */
304static 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
337typedef enum {
338#define _(n) SLEEP_##n##_US,
339 foreach_histogram_bucket
340#undef _
341 SLEEP_N_BUCKETS,
342} histogram_index_t;
343
344static u64 vector_rate_histogram[SLEEP_N_BUCKETS];
345
Ed Warnickecb9cada2015-12-08 15:45:58 -0700346static void memclnt_queue_callback (vlib_main_t *vm);
347
348static uword
349memclnt_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);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700364
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) {
Dave Barachdae88b92016-04-19 09:38:35 -0400404 vm->api_queue_nonempty = 0;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700405 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);
Damjan Marionf1213b82016-03-13 02:22:06 +0100422 clib_memcpy (&mp, headp, sizeof(uword));
Ed Warnickecb9cada2015-12-08 15:45:58 -0700423
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);
Dave Barach16c75df2016-05-31 14:05:46 -0400458 vm->queue_signal_pending = 0;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700459 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
557static clib_error_t *
558vl_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
590VLIB_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
596static clib_error_t *
597vl_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
608VLIB_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
615VLIB_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
Ed Warnickecb9cada2015-12-08 15:45:58 -0700622static void
623memclnt_queue_callback (vlib_main_t *vm)
624{
Dave Barach16c75df2016-05-31 14:05:46 -0400625 static volatile int * cursizep;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700626
Dave Barach16c75df2016-05-31 14:05:46 -0400627 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 }
Ed Warnickecb9cada2015-12-08 15:45:58 -0700649}
650
651void 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
659static uword
660api_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
707VLIB_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
714static clib_error_t *
715setup_memclnt_exit (vlib_main_t * vm)
716{
717 atexit (vl_unmap_shmem);
718 return 0;
719}
720
721VLIB_INIT_FUNCTION (setup_memclnt_exit);
722
723
724static clib_error_t *
725vl_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
769void dump_socket_clients (vlib_main_t *vm, api_main_t *am)
770 __attribute__((weak));
771
772void dump_socket_clients (vlib_main_t *vm, api_main_t *am)
773{
774}
775
776static clib_error_t *
777vl_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);
826socket_clients:
827 dump_socket_clients (vm, am);
828
829 return 0;
830}
831
832VLIB_CLI_COMMAND (cli_show_api_command, static) = {
833 .path = "show api",
834 .short_help = "Show API information",
835};
836
837VLIB_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
843VLIB_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
849static clib_error_t *
850vl_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
884VLIB_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
890void 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
983static clib_error_t *
984vl_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
1046VLIB_CLI_COMMAND (trace, static) = {
1047 .path = "set api-trace",
1048 .short_help = "API trace",
1049 .function = vl_api_trace_command,
1050};
1051
1052clib_error_t *
1053vlibmemory_init (vlib_main_t * vm)
1054{
Dave Barach309bef22016-01-22 16:09:52 -05001055 api_main_t *am = &api_main;
Dave Barach16c75df2016-05-31 14:05:46 -04001056 /* 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);
Dave Barach309bef22016-01-22 16:09:52 -05001058 return 0;
Ed Warnickecb9cada2015-12-08 15:45:58 -07001059}
1060
1061VLIB_INIT_FUNCTION (vlibmemory_init);
1062
1063void vl_set_memory_region_name (char *name)
1064{
1065 api_main_t *am = &api_main;
1066
1067 am->region_name = name;
1068}
1069
Ed Warnickecb9cada2015-12-08 15:45:58 -07001070static 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
1080static 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
1093static clib_error_t *
1094vl_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
1120VLIB_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};
Dave Barach4e281a42015-12-14 11:13:29 -05001125
1126static 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
Christophe Fontainefef15b42016-04-09 12:38:49 +09001144 fp = uword_to_pointer(mp->function, int(*)(void *));
1145 rv = fp(mp->data);
Dave Barach4e281a42015-12-14 11:13:29 -05001146
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
1170static void vl_api_rpc_reply_t_handler (vl_api_rpc_reply_t * mp)
1171{ clib_warning ("unimplemented"); }
1172
1173void 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));
Damjan Marionf1213b82016-03-13 02:22:06 +01001181 clib_memcpy (mp->data, data, data_length);
Dave Barach4e281a42015-12-14 11:13:29 -05001182 mp->_vl_msg_id = ntohs (VL_API_RPC_CALL);
Christophe Fontainefef15b42016-04-09 12:38:49 +09001183 mp->function = pointer_to_uword(fp);
Dave Barach4e281a42015-12-14 11:13:29 -05001184 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
1194static clib_error_t *
1195rpc_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
1209VLIB_API_INIT_FUNCTION(rpc_api_hookup);