blob: e4f5dd0d5258c6b934eaab222f1060d0d3f7c65c [file] [log] [blame]
Ed Warnickecb9cada2015-12-08 15:45:58 -07001/*
2 *------------------------------------------------------------------
3 * memclnt_shared.c - API message handling, common code for both clients
4 * and the vlib process itself.
5 *
6 *
7 * Copyright (c) 2009 Cisco and/or its affiliates.
8 * Licensed under the Apache License, Version 2.0 (the "License");
9 * you may not use this file except in compliance with the License.
10 * You may obtain a copy of the License at:
11 *
12 * http://www.apache.org/licenses/LICENSE-2.0
13 *
14 * Unless required by applicable law or agreed to in writing, software
15 * distributed under the License is distributed on an "AS IS" BASIS,
16 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17 * See the License for the specific language governing permissions and
18 * limitations under the License.
19 *------------------------------------------------------------------
20 */
21
22#include <stdio.h>
23#include <stdlib.h>
Ole Troan6855f6c2016-04-09 03:16:30 +020024#include <stddef.h>
Ed Warnickecb9cada2015-12-08 15:45:58 -070025#include <string.h>
26#include <unistd.h>
27#include <signal.h>
28#include <vppinfra/format.h>
29#include <vppinfra/byte_order.h>
30#include <vppinfra/error.h>
31#include <vlib/vlib.h>
32#include <vlib/unix/unix.h>
33#include <vlibmemory/api.h>
34#include <vlibmemory/unix_shared_memory_queue.h>
35
36#include <vlibmemory/vl_memory_msg_enum.h>
37
38#define vl_typedefs
39#include <vlibmemory/vl_memory_api_h.h>
40#undef vl_typedefs
41
Ed Warnickecb9cada2015-12-08 15:45:58 -070042static inline void *vl_msg_api_alloc_internal(int nbytes, int pool)
43{
44 int i;
45 msgbuf_t *rv;
46 ring_alloc_t *ap;
47 unix_shared_memory_queue_t *q;
48 void *oldheap;
49 vl_shmem_hdr_t *shmem_hdr;
50 api_main_t *am = &api_main;
51
52 shmem_hdr = am->shmem_hdr;
53
54 if (shmem_hdr == 0) {
55 clib_warning ("shared memory header NULL");
56 return 0;
57 }
58
59 /* account for the msgbuf_t header*/
60 nbytes += sizeof(msgbuf_t);
61
62 if (shmem_hdr->vl_rings == 0) {
63 clib_warning ("vl_rings NULL");
64 return 0;
65 }
66
67 if (shmem_hdr->client_rings == 0) {
68 clib_warning ("client_rings NULL");
69 return 0;
70 }
71
72 ap = pool ? shmem_hdr->vl_rings : shmem_hdr->client_rings;
73 for (i = 0; i < vec_len (ap); i++) {
74 /* Too big? */
75 if (nbytes > ap[i].size) {
76 continue;
77 }
78
79 q = ap[i].rp;
80 if (pool == 0) {
81 pthread_mutex_lock(&q->mutex);
82 }
83 rv = (msgbuf_t *) (&q->data[0] + q->head*q->elsize);
84 /*
85 * Is this item still in use?
86 */
87 if (rv->q) {
88 /* yes, loser; try next larger pool */
89 ap[i].misses++;
90 if (pool == 0)
91 pthread_mutex_unlock(&q->mutex);
92 continue;
93 }
94 /* OK, we have a winner */
95 ap[i].hits++;
96 /*
97 * Remember the source queue, although we
98 * don't need to know the queue to free the item.
99 */
100 rv->q = q;
101 q->head++;
102 if (q->head == q->maxsize)
103 q->head = 0;
104
105 if (pool == 0)
106 pthread_mutex_unlock(&q->mutex);
107 goto out;
108 }
109
110 /*
111 * Request too big, or head element of all size-compatible rings
112 * still in use. Fall back to shared-memory malloc.
113 */
114 am->ring_misses++;
115
116 pthread_mutex_lock (&am->vlib_rp->mutex);
117 oldheap = svm_push_data_heap (am->vlib_rp);
Ole Troan6855f6c2016-04-09 03:16:30 +0200118 rv = clib_mem_alloc(nbytes);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700119 rv->q = 0;
120 svm_pop_heap (oldheap);
121 pthread_mutex_unlock (&am->vlib_rp->mutex);
122
123 out:
Ole Troan6855f6c2016-04-09 03:16:30 +0200124 rv->data_len = htonl(nbytes - sizeof(msgbuf_t));
Ed Warnickecb9cada2015-12-08 15:45:58 -0700125 return(rv->data);
126}
127
128void *vl_msg_api_alloc (int nbytes)
129{
130 int pool;
131 api_main_t *am = &api_main;
132 vl_shmem_hdr_t *shmem_hdr = am->shmem_hdr;
133
134 /*
135 * Clients use pool-0, vlib proc uses pool 1
136 */
137 pool = (am->our_pid == shmem_hdr->vl_pid);
138 return vl_msg_api_alloc_internal (nbytes, pool);
139}
140
141void *vl_msg_api_alloc_as_if_client (int nbytes)
142{
143 return vl_msg_api_alloc_internal (nbytes, 0);
144}
145
146void vl_msg_api_free(void *a)
147{
148 msgbuf_t *rv;
149 void *oldheap;
150 api_main_t *am = &api_main;
151
Ole Troan6855f6c2016-04-09 03:16:30 +0200152 rv = (msgbuf_t *)(((u8 *)a) - offsetof(msgbuf_t, data));
153
Ed Warnickecb9cada2015-12-08 15:45:58 -0700154 /*
155 * Here's the beauty of the scheme. Only one proc/thread has
156 * control of a given message buffer. To free a buffer, we just clear the
157 * queue field, and leave. No locks, no hits, no errors...
158 */
159 if (rv->q) {
160 rv->q = 0;
161 return;
162 }
163
164 pthread_mutex_lock (&am->vlib_rp->mutex);
165 oldheap = svm_push_data_heap (am->vlib_rp);
166 clib_mem_free (rv);
167 svm_pop_heap (oldheap);
168 pthread_mutex_unlock (&am->vlib_rp->mutex);
169}
170
171static void vl_msg_api_free_nolock (void *a)
172{
173 msgbuf_t *rv;
174 void *oldheap;
175 api_main_t *am = &api_main;
176
Ole Troan6855f6c2016-04-09 03:16:30 +0200177 rv = (msgbuf_t *)(((u8 *)a) - offsetof(msgbuf_t, data));
Ed Warnickecb9cada2015-12-08 15:45:58 -0700178 /*
179 * Here's the beauty of the scheme. Only one proc/thread has
180 * control of a given message buffer. To free a buffer, we just clear the
181 * queue field, and leave. No locks, no hits, no errors...
182 */
183 if (rv->q) {
184 rv->q = 0;
185 return;
186 }
187
188 oldheap = svm_push_data_heap (am->vlib_rp);
189 clib_mem_free (rv);
190 svm_pop_heap (oldheap);
191}
192
Dave Barach309bef22016-01-22 16:09:52 -0500193void vl_set_memory_root_path (char *name)
194{
195 api_main_t *am = &api_main;
196
197 am->root_path = name;
198}
199
Dave Barach16c75df2016-05-31 14:05:46 -0400200void vl_set_memory_uid (int uid)
201{
202 api_main_t *am = &api_main;
203
204 am->api_uid = uid;
205}
206
207void vl_set_memory_gid (int gid)
208{
209 api_main_t *am = &api_main;
210
211 am->api_gid = gid;
212}
213
Ed Warnickecb9cada2015-12-08 15:45:58 -0700214int vl_map_shmem (char *region_name, int is_vlib)
215{
Dave Barach16c75df2016-05-31 14:05:46 -0400216 svm_map_region_args_t _a, *a = &_a;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700217 svm_region_t *vlib_rp, *root_rp;
218 void *oldheap;
219 vl_shmem_hdr_t *shmem_hdr=0;
220 api_main_t *am = &api_main;
221 int i;
222 struct timespec ts, tsrem;
223
224 if (is_vlib == 0)
Dave Barach309bef22016-01-22 16:09:52 -0500225 svm_region_init_chroot(am->root_path);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700226
Dave Barach16c75df2016-05-31 14:05:46 -0400227 memset (a, 0, sizeof (*a));
Ed Warnickecb9cada2015-12-08 15:45:58 -0700228
229 a->name = region_name;
230 a->size = 16<<20;
231 a->flags = SVM_FLAGS_MHEAP;
Dave Barach16c75df2016-05-31 14:05:46 -0400232 a->uid = am->api_uid;
233 a->gid = am->api_gid;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700234
235 vlib_rp = svm_region_find_or_create (a);
236
Ed Warnickecb9cada2015-12-08 15:45:58 -0700237 if (vlib_rp == 0)
238 return (-2);
239
240 pthread_mutex_lock (&vlib_rp->mutex);
241 /* Has someone else set up the shared-memory variable table? */
242 if (vlib_rp->user_ctx) {
243 am->shmem_hdr = (void *) vlib_rp->user_ctx;
244 am->our_pid = getpid();
245 if (is_vlib) {
246 unix_shared_memory_queue_t *q;
247 uword old_msg;
248 /*
249 * application restart. Reset cached pids, API message
250 * rings, list of clients; otherwise, various things
251 * fail. (e.g. queue non-empty notification)
252 */
253
254 /* ghosts keep the region from disappearing properly */
255 svm_client_scan_this_region_nolock(vlib_rp);
256 am->shmem_hdr->application_restarts++;
257 q = am->shmem_hdr->vl_input_queue;
258 am->shmem_hdr->vl_pid = getpid();
259 q->consumer_pid = am->shmem_hdr->vl_pid;
260 /* Drain the input queue, freeing msgs */
261 for (i = 0; i < 10; i++) {
262 if (pthread_mutex_trylock (&q->mutex) == 0) {
263 pthread_mutex_unlock (&q->mutex);
264 goto mutex_ok;
265 }
266 ts.tv_sec = 0;
267 ts.tv_nsec = 10000*1000; /* 10 ms */
268 while (nanosleep(&ts, &tsrem) < 0)
269 ts = tsrem;
270 }
271 /* Mutex buggered, "fix" it */
272 memset (&q->mutex, 0, sizeof (q->mutex));
273 clib_warning ("forcibly release main input queue mutex");
274
275 mutex_ok:
276 am->vlib_rp = vlib_rp;
277 while (unix_shared_memory_queue_sub (q,
278 (u8 *)&old_msg,
279 1 /* nowait */)
280 != -2 /* queue underflow */) {
281 vl_msg_api_free_nolock ((void *)old_msg);
282 am->shmem_hdr->restart_reclaims++;
283 }
284 pthread_mutex_unlock (&vlib_rp->mutex);
285 root_rp = svm_get_root_rp();
286 ASSERT(root_rp);
287 /* Clean up the root region client list */
288 pthread_mutex_lock (&root_rp->mutex);
289 svm_client_scan_this_region_nolock (root_rp);
Dave Barach599839d2016-06-07 17:35:38 -0400290 pthread_mutex_unlock (&root_rp->mutex);
Dave Barachb84ccc62016-06-16 20:14:43 -0400291 } else {
292 pthread_mutex_unlock (&vlib_rp->mutex);
293 }
Ed Warnickecb9cada2015-12-08 15:45:58 -0700294 am->vlib_rp = vlib_rp;
295 vec_add1(am->mapped_shmem_regions, vlib_rp);
296 return 0;
297 }
298 /* Clients simply have to wait... */
299 if (!is_vlib) {
300 pthread_mutex_unlock (&vlib_rp->mutex);
301
302 /* Wait up to 100 seconds... */
303 for (i = 0; i < 10000; i++) {
304 ts.tv_sec = 0;
305 ts.tv_nsec = 10000*1000; /* 10 ms */
306 while (nanosleep(&ts, &tsrem) < 0)
307 ts = tsrem;
308 if (vlib_rp->user_ctx)
309 goto ready;
310 }
311 /* Clean up and leave... */
312 svm_region_unmap (vlib_rp);
313 clib_warning ("region init fail");
314 return (-2);
315
316 ready:
317 am->shmem_hdr = (void *)vlib_rp->user_ctx;
318 am->our_pid = getpid();
319 am->vlib_rp = vlib_rp;
320 vec_add1(am->mapped_shmem_regions, vlib_rp);
321 return 0;
322 }
323
324 /* Nope, it's our problem... */
325
326 oldheap = svm_push_data_heap (vlib_rp);
327
328 vec_validate(shmem_hdr, 0);
329 shmem_hdr->version = VL_SHM_VERSION;
330
331 /* vlib main input queue */
332 shmem_hdr->vl_input_queue =
333 unix_shared_memory_queue_init (1024, sizeof (uword), getpid(),
334 am->vlib_signal);
335
336 /* Set up the msg ring allocator */
337#define _(sz,n) \
338 do { \
339 ring_alloc_t _rp; \
340 _rp.rp = unix_shared_memory_queue_init ((n), (sz), 0, 0); \
341 _rp.size = (sz); \
342 _rp.nitems = n; \
343 _rp.hits = 0; \
344 _rp.misses = 0; \
345 vec_add1(shmem_hdr->vl_rings, _rp); \
346 } while (0);
347
348 foreach_vl_aring_size;
349#undef _
350
351#define _(sz,n) \
352 do { \
353 ring_alloc_t _rp; \
354 _rp.rp = unix_shared_memory_queue_init ((n), (sz), 0, 0); \
355 _rp.size = (sz); \
356 _rp.nitems = n; \
357 _rp.hits = 0; \
358 _rp.misses = 0; \
359 vec_add1(shmem_hdr->client_rings, _rp); \
360 } while (0);
361
362 foreach_clnt_aring_size;
363#undef _
364
365 am->shmem_hdr = shmem_hdr;
366 am->vlib_rp = vlib_rp;
367 am->our_pid = getpid();
368 if (is_vlib)
369 am->shmem_hdr->vl_pid = am->our_pid;
370
371 svm_pop_heap (oldheap);
372
373 /*
374 * After absolutely everything that a client might see is set up,
375 * declare the shmem region valid
376 */
377 vlib_rp->user_ctx = shmem_hdr;
378
379 pthread_mutex_unlock (&vlib_rp->mutex);
380 vec_add1(am->mapped_shmem_regions, vlib_rp);
381 return 0;
382}
383
384void vl_register_mapped_shmem_region(svm_region_t *rp)
385{
386 api_main_t *am = &api_main;
387
388 vec_add1(am->mapped_shmem_regions, rp);
389}
390
391void vl_unmap_shmem (void)
392{
393 svm_region_t *rp;
394 int i;
395 api_main_t *am = &api_main;
396
397 if (! svm_get_root_rp())
398 return;
399
400 for (i = 0; i < vec_len(am->mapped_shmem_regions); i++) {
401 rp = am->mapped_shmem_regions[i];
402 svm_region_unmap (rp);
403 }
404
405 vec_free(am->mapped_shmem_regions);
406 am->shmem_hdr = 0;
407
408 svm_region_exit ();
409 /* $$$ more careful cleanup, valgrind run... */
410 vec_free (am->msg_handlers);
411 vec_free (am->msg_endian_handlers);
412 vec_free (am->msg_print_handlers);
413}
414
415void vl_msg_api_send_shmem (unix_shared_memory_queue_t *q, u8 *elem)
416{
417 api_main_t *am = &api_main;
418 uword *trace = (uword *)elem;
419
420 if (am->tx_trace && am->tx_trace->enabled)
421 vl_msg_api_trace(am, am->tx_trace, (void *)trace[0]);
422
423 (void)unix_shared_memory_queue_add(q, elem, 0 /* nowait */);
424}
425
426void vl_msg_api_send_shmem_nolock (unix_shared_memory_queue_t *q, u8 *elem)
427{
428 api_main_t *am = &api_main;
429 uword *trace = (uword *)elem;
430
431 if (am->tx_trace && am->tx_trace->enabled)
432 vl_msg_api_trace(am, am->tx_trace, (void *)trace[0]);
433
434 (void)unix_shared_memory_queue_add_nolock (q, elem);
435}
436
437static void vl_api_memclnt_create_reply_t_handler (
438 vl_api_memclnt_create_reply_t *mp)
439{
440 api_main_t *am = &api_main;
441 int rv;
442
443 am->my_client_index = mp->index;
444 am->my_registration = (vl_api_registration_t *)(uword)
445 mp->handle;
446
447 rv = ntohl(mp->response);
448
449 if (rv < 0)
450 clib_warning ("WARNING: API mismatch detected");
451}
452
453void vl_client_add_api_signatures (vl_api_memclnt_create_t *mp)
454 __attribute__((weak));
455
456void vl_client_add_api_signatures (vl_api_memclnt_create_t *mp)
457{
458 int i;
459
460 for (i = 0; i < ARRAY_LEN(mp->api_versions); i++)
461 mp->api_versions[i] = 0;
462}
463
464int vl_client_connect (char *name, int ctx_quota, int input_queue_size)
465{
466 svm_region_t *svm;
467 vl_api_memclnt_create_t *mp;
468 vl_api_memclnt_create_reply_t *rp;
469 unix_shared_memory_queue_t *vl_input_queue;
470 vl_shmem_hdr_t *shmem_hdr;
471 int rv=0;
472 void *oldheap;
473 api_main_t *am = &api_main;
474
475 if (am->my_registration) {
476 clib_warning ("client %s already connected...", name);
477 return -1;
478 }
479
480 if (am->vlib_rp == 0) {
481 clib_warning ("am->vlib_rp NULL");
482 return -1;
483 }
484
485 svm = am->vlib_rp;
486 shmem_hdr = am->shmem_hdr;
487
488 if (shmem_hdr == 0 || shmem_hdr->vl_input_queue == 0) {
489 clib_warning ("shmem_hdr / input queue NULL");
490 return -1;
491 }
492
493 pthread_mutex_lock (&svm->mutex);
494 oldheap = svm_push_data_heap(svm);
495 vl_input_queue =
496 unix_shared_memory_queue_init (input_queue_size, sizeof(uword),
497 getpid(), 0);
498 pthread_mutex_unlock(&svm->mutex);
499 svm_pop_heap (oldheap);
500
501 am->my_client_index = ~0;
502 am->my_registration = 0;
503 am->vl_input_queue = vl_input_queue;
504
505 mp = vl_msg_api_alloc(sizeof(vl_api_memclnt_create_t));
506 memset(mp, 0, sizeof (*mp));
507 mp->_vl_msg_id = ntohs(VL_API_MEMCLNT_CREATE);
508 mp->ctx_quota = ctx_quota;
509 mp->input_queue = (uword)vl_input_queue;
510 strncpy ((char *) mp->name, name, sizeof(mp->name)-1);
511
512 vl_client_add_api_signatures(mp);
513
514 vl_msg_api_send_shmem (shmem_hdr->vl_input_queue, (u8 *)&mp);
515
516 while (1) {
517 int qstatus;
518 struct timespec ts, tsrem;
519 int i;
520
521 /* Wait up to 10 seconds */
522 for (i = 0; i < 1000; i++) {
523 qstatus = unix_shared_memory_queue_sub (vl_input_queue, (u8 *)&rp,
524 1 /* nowait */);
525 if (qstatus == 0)
526 goto read_one_msg;
527 ts.tv_sec = 0;
528 ts.tv_nsec = 10000*1000; /* 10 ms */
529 while (nanosleep(&ts, &tsrem) < 0)
530 ts = tsrem;
531 }
532 /* Timeout... */
533 clib_warning ("memclnt_create_reply timeout");
534 return -1;
535
536 read_one_msg:
537 if (ntohs(rp->_vl_msg_id) != VL_API_MEMCLNT_CREATE_REPLY) {
538 clib_warning ("unexpected reply: id %d", ntohs(rp->_vl_msg_id));
539 continue;
540 }
541 rv = clib_net_to_host_u32(rp->response);
542
543 vl_msg_api_handler((void *)rp);
544 break;
545 }
546 return (rv);
547}
548
549static void vl_api_memclnt_delete_reply_t_handler (
550 vl_api_memclnt_delete_reply_t *mp)
551{
552 void *oldheap;
553 api_main_t *am = &api_main;
554
555 pthread_mutex_lock (&am->vlib_rp->mutex);
556 oldheap = svm_push_data_heap(am->vlib_rp);
557 unix_shared_memory_queue_free (am->vl_input_queue);
558 pthread_mutex_unlock (&am->vlib_rp->mutex);
559 svm_pop_heap (oldheap);
560
561 am->my_client_index = ~0;
562 am->my_registration = 0;
563 am->vl_input_queue = 0;
564}
565
566void vl_client_disconnect (void)
567{
568 vl_api_memclnt_delete_t *mp;
569 vl_api_memclnt_delete_reply_t *rp;
570 unix_shared_memory_queue_t *vl_input_queue;
571 vl_shmem_hdr_t *shmem_hdr;
572 time_t begin;
573 api_main_t *am = &api_main;
574
575 ASSERT(am->vlib_rp);
576 shmem_hdr = am->shmem_hdr;
577 ASSERT(shmem_hdr && shmem_hdr->vl_input_queue);
578
579 vl_input_queue = am->vl_input_queue;
580
581 mp = vl_msg_api_alloc(sizeof(vl_api_memclnt_delete_t));
582 memset(mp, 0, sizeof (*mp));
583 mp->_vl_msg_id = ntohs(VL_API_MEMCLNT_DELETE);
584 mp->index = am->my_client_index;
585 mp->handle = (uword) am->my_registration;
586
587 vl_msg_api_send_shmem (shmem_hdr->vl_input_queue, (u8 *)&mp);
588
589 /*
590 * Have to be careful here, in case the client is disconnecting
591 * because e.g. the vlib process died, or is unresponsive.
592 */
593
594 begin = time (0);
595 while (1) {
596 time_t now;
597
598 now = time (0);
599
600 if (now >= (begin + 2)) {
601 clib_warning ("peer unresponsive, give up");
602 am->my_client_index = ~0;
603 am->my_registration = 0;
604 am->shmem_hdr = 0;
605 break;
606 }
607 if (unix_shared_memory_queue_sub (vl_input_queue, (u8 *)&rp, 1) < 0)
608 continue;
609
610 /* drain the queue */
611 if (ntohs(rp->_vl_msg_id) != VL_API_MEMCLNT_DELETE_REPLY) {
612 vl_msg_api_handler ((void *)rp);
613 continue;
614 }
615 vl_msg_api_handler((void *)rp);
616 break;
617 }
618}
619
620static inline vl_api_registration_t
621*vl_api_client_index_to_registration_internal (u32 handle)
622{
623 vl_api_registration_t **regpp;
624 vl_api_registration_t *regp;
625 api_main_t *am = &api_main;
626 u32 index;
627
628 index = vl_msg_api_handle_get_index (handle);
629 if ((am->shmem_hdr->application_restarts & VL_API_EPOCH_MASK)
630 != vl_msg_api_handle_get_epoch (handle)) {
631 vl_msg_api_increment_missing_client_counter();
632 return 0;
633 }
634
635 regpp = am->vl_clients + index;
636
637 if (pool_is_free(am->vl_clients, regpp)) {
638 vl_msg_api_increment_missing_client_counter();
639 return 0;
640 }
641 regp = *regpp;
642 return (regp);
643}
644
645vl_api_registration_t *vl_api_client_index_to_registration (u32 index)
646{
647 return (vl_api_client_index_to_registration_internal (index));
648}
649
650unix_shared_memory_queue_t *vl_api_client_index_to_input_queue (u32 index)
651{
652 vl_api_registration_t *regp;
653
654 regp = vl_api_client_index_to_registration_internal (index);
655 if (!regp)
656 return 0;
657 return (regp->vl_input_queue);
658}
659
660#define foreach_api_client_msg \
661_(MEMCLNT_CREATE_REPLY, memclnt_create_reply) \
662_(MEMCLNT_DELETE_REPLY, memclnt_delete_reply)
663
664int vl_client_api_map (char *region_name)
665{
666 int rv;
667
668 if ((rv = vl_map_shmem (region_name, 0 /* is_vlib */)) < 0) {
669 return rv;
670 }
671
672#define _(N,n) \
673 vl_msg_api_set_handlers(VL_API_##N, 0 /* name */, \
674 vl_api_##n##_t_handler, \
675 0/* cleanup */, 0/* endian */, 0/* print */, \
676 sizeof(vl_api_##n##_t), 1);
677 foreach_api_client_msg;
678#undef _
679 return 0;
680}
681
682void vl_client_api_unmap (void)
683{
684 vl_unmap_shmem();
685}