blob: 71150fdc9cb6b37977bf236c6fbafcce2b63eef0 [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 Barach16c75df2016-05-31 14:05:46 -0400290 }
291 pthread_mutex_unlock (&vlib_rp->mutex);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700292 am->vlib_rp = vlib_rp;
293 vec_add1(am->mapped_shmem_regions, vlib_rp);
294 return 0;
295 }
296 /* Clients simply have to wait... */
297 if (!is_vlib) {
298 pthread_mutex_unlock (&vlib_rp->mutex);
299
300 /* Wait up to 100 seconds... */
301 for (i = 0; i < 10000; i++) {
302 ts.tv_sec = 0;
303 ts.tv_nsec = 10000*1000; /* 10 ms */
304 while (nanosleep(&ts, &tsrem) < 0)
305 ts = tsrem;
306 if (vlib_rp->user_ctx)
307 goto ready;
308 }
309 /* Clean up and leave... */
310 svm_region_unmap (vlib_rp);
311 clib_warning ("region init fail");
312 return (-2);
313
314 ready:
315 am->shmem_hdr = (void *)vlib_rp->user_ctx;
316 am->our_pid = getpid();
317 am->vlib_rp = vlib_rp;
318 vec_add1(am->mapped_shmem_regions, vlib_rp);
319 return 0;
320 }
321
322 /* Nope, it's our problem... */
323
324 oldheap = svm_push_data_heap (vlib_rp);
325
326 vec_validate(shmem_hdr, 0);
327 shmem_hdr->version = VL_SHM_VERSION;
328
329 /* vlib main input queue */
330 shmem_hdr->vl_input_queue =
331 unix_shared_memory_queue_init (1024, sizeof (uword), getpid(),
332 am->vlib_signal);
333
334 /* Set up the msg ring allocator */
335#define _(sz,n) \
336 do { \
337 ring_alloc_t _rp; \
338 _rp.rp = unix_shared_memory_queue_init ((n), (sz), 0, 0); \
339 _rp.size = (sz); \
340 _rp.nitems = n; \
341 _rp.hits = 0; \
342 _rp.misses = 0; \
343 vec_add1(shmem_hdr->vl_rings, _rp); \
344 } while (0);
345
346 foreach_vl_aring_size;
347#undef _
348
349#define _(sz,n) \
350 do { \
351 ring_alloc_t _rp; \
352 _rp.rp = unix_shared_memory_queue_init ((n), (sz), 0, 0); \
353 _rp.size = (sz); \
354 _rp.nitems = n; \
355 _rp.hits = 0; \
356 _rp.misses = 0; \
357 vec_add1(shmem_hdr->client_rings, _rp); \
358 } while (0);
359
360 foreach_clnt_aring_size;
361#undef _
362
363 am->shmem_hdr = shmem_hdr;
364 am->vlib_rp = vlib_rp;
365 am->our_pid = getpid();
366 if (is_vlib)
367 am->shmem_hdr->vl_pid = am->our_pid;
368
369 svm_pop_heap (oldheap);
370
371 /*
372 * After absolutely everything that a client might see is set up,
373 * declare the shmem region valid
374 */
375 vlib_rp->user_ctx = shmem_hdr;
376
377 pthread_mutex_unlock (&vlib_rp->mutex);
378 vec_add1(am->mapped_shmem_regions, vlib_rp);
379 return 0;
380}
381
382void vl_register_mapped_shmem_region(svm_region_t *rp)
383{
384 api_main_t *am = &api_main;
385
386 vec_add1(am->mapped_shmem_regions, rp);
387}
388
389void vl_unmap_shmem (void)
390{
391 svm_region_t *rp;
392 int i;
393 api_main_t *am = &api_main;
394
395 if (! svm_get_root_rp())
396 return;
397
398 for (i = 0; i < vec_len(am->mapped_shmem_regions); i++) {
399 rp = am->mapped_shmem_regions[i];
400 svm_region_unmap (rp);
401 }
402
403 vec_free(am->mapped_shmem_regions);
404 am->shmem_hdr = 0;
405
406 svm_region_exit ();
407 /* $$$ more careful cleanup, valgrind run... */
408 vec_free (am->msg_handlers);
409 vec_free (am->msg_endian_handlers);
410 vec_free (am->msg_print_handlers);
411}
412
413void vl_msg_api_send_shmem (unix_shared_memory_queue_t *q, u8 *elem)
414{
415 api_main_t *am = &api_main;
416 uword *trace = (uword *)elem;
417
418 if (am->tx_trace && am->tx_trace->enabled)
419 vl_msg_api_trace(am, am->tx_trace, (void *)trace[0]);
420
421 (void)unix_shared_memory_queue_add(q, elem, 0 /* nowait */);
422}
423
424void vl_msg_api_send_shmem_nolock (unix_shared_memory_queue_t *q, u8 *elem)
425{
426 api_main_t *am = &api_main;
427 uword *trace = (uword *)elem;
428
429 if (am->tx_trace && am->tx_trace->enabled)
430 vl_msg_api_trace(am, am->tx_trace, (void *)trace[0]);
431
432 (void)unix_shared_memory_queue_add_nolock (q, elem);
433}
434
435static void vl_api_memclnt_create_reply_t_handler (
436 vl_api_memclnt_create_reply_t *mp)
437{
438 api_main_t *am = &api_main;
439 int rv;
440
441 am->my_client_index = mp->index;
442 am->my_registration = (vl_api_registration_t *)(uword)
443 mp->handle;
444
445 rv = ntohl(mp->response);
446
447 if (rv < 0)
448 clib_warning ("WARNING: API mismatch detected");
449}
450
451void vl_client_add_api_signatures (vl_api_memclnt_create_t *mp)
452 __attribute__((weak));
453
454void vl_client_add_api_signatures (vl_api_memclnt_create_t *mp)
455{
456 int i;
457
458 for (i = 0; i < ARRAY_LEN(mp->api_versions); i++)
459 mp->api_versions[i] = 0;
460}
461
462int vl_client_connect (char *name, int ctx_quota, int input_queue_size)
463{
464 svm_region_t *svm;
465 vl_api_memclnt_create_t *mp;
466 vl_api_memclnt_create_reply_t *rp;
467 unix_shared_memory_queue_t *vl_input_queue;
468 vl_shmem_hdr_t *shmem_hdr;
469 int rv=0;
470 void *oldheap;
471 api_main_t *am = &api_main;
472
473 if (am->my_registration) {
474 clib_warning ("client %s already connected...", name);
475 return -1;
476 }
477
478 if (am->vlib_rp == 0) {
479 clib_warning ("am->vlib_rp NULL");
480 return -1;
481 }
482
483 svm = am->vlib_rp;
484 shmem_hdr = am->shmem_hdr;
485
486 if (shmem_hdr == 0 || shmem_hdr->vl_input_queue == 0) {
487 clib_warning ("shmem_hdr / input queue NULL");
488 return -1;
489 }
490
491 pthread_mutex_lock (&svm->mutex);
492 oldheap = svm_push_data_heap(svm);
493 vl_input_queue =
494 unix_shared_memory_queue_init (input_queue_size, sizeof(uword),
495 getpid(), 0);
496 pthread_mutex_unlock(&svm->mutex);
497 svm_pop_heap (oldheap);
498
499 am->my_client_index = ~0;
500 am->my_registration = 0;
501 am->vl_input_queue = vl_input_queue;
502
503 mp = vl_msg_api_alloc(sizeof(vl_api_memclnt_create_t));
504 memset(mp, 0, sizeof (*mp));
505 mp->_vl_msg_id = ntohs(VL_API_MEMCLNT_CREATE);
506 mp->ctx_quota = ctx_quota;
507 mp->input_queue = (uword)vl_input_queue;
508 strncpy ((char *) mp->name, name, sizeof(mp->name)-1);
509
510 vl_client_add_api_signatures(mp);
511
512 vl_msg_api_send_shmem (shmem_hdr->vl_input_queue, (u8 *)&mp);
513
514 while (1) {
515 int qstatus;
516 struct timespec ts, tsrem;
517 int i;
518
519 /* Wait up to 10 seconds */
520 for (i = 0; i < 1000; i++) {
521 qstatus = unix_shared_memory_queue_sub (vl_input_queue, (u8 *)&rp,
522 1 /* nowait */);
523 if (qstatus == 0)
524 goto read_one_msg;
525 ts.tv_sec = 0;
526 ts.tv_nsec = 10000*1000; /* 10 ms */
527 while (nanosleep(&ts, &tsrem) < 0)
528 ts = tsrem;
529 }
530 /* Timeout... */
531 clib_warning ("memclnt_create_reply timeout");
532 return -1;
533
534 read_one_msg:
535 if (ntohs(rp->_vl_msg_id) != VL_API_MEMCLNT_CREATE_REPLY) {
536 clib_warning ("unexpected reply: id %d", ntohs(rp->_vl_msg_id));
537 continue;
538 }
539 rv = clib_net_to_host_u32(rp->response);
540
541 vl_msg_api_handler((void *)rp);
542 break;
543 }
544 return (rv);
545}
546
547static void vl_api_memclnt_delete_reply_t_handler (
548 vl_api_memclnt_delete_reply_t *mp)
549{
550 void *oldheap;
551 api_main_t *am = &api_main;
552
553 pthread_mutex_lock (&am->vlib_rp->mutex);
554 oldheap = svm_push_data_heap(am->vlib_rp);
555 unix_shared_memory_queue_free (am->vl_input_queue);
556 pthread_mutex_unlock (&am->vlib_rp->mutex);
557 svm_pop_heap (oldheap);
558
559 am->my_client_index = ~0;
560 am->my_registration = 0;
561 am->vl_input_queue = 0;
562}
563
564void vl_client_disconnect (void)
565{
566 vl_api_memclnt_delete_t *mp;
567 vl_api_memclnt_delete_reply_t *rp;
568 unix_shared_memory_queue_t *vl_input_queue;
569 vl_shmem_hdr_t *shmem_hdr;
570 time_t begin;
571 api_main_t *am = &api_main;
572
573 ASSERT(am->vlib_rp);
574 shmem_hdr = am->shmem_hdr;
575 ASSERT(shmem_hdr && shmem_hdr->vl_input_queue);
576
577 vl_input_queue = am->vl_input_queue;
578
579 mp = vl_msg_api_alloc(sizeof(vl_api_memclnt_delete_t));
580 memset(mp, 0, sizeof (*mp));
581 mp->_vl_msg_id = ntohs(VL_API_MEMCLNT_DELETE);
582 mp->index = am->my_client_index;
583 mp->handle = (uword) am->my_registration;
584
585 vl_msg_api_send_shmem (shmem_hdr->vl_input_queue, (u8 *)&mp);
586
587 /*
588 * Have to be careful here, in case the client is disconnecting
589 * because e.g. the vlib process died, or is unresponsive.
590 */
591
592 begin = time (0);
593 while (1) {
594 time_t now;
595
596 now = time (0);
597
598 if (now >= (begin + 2)) {
599 clib_warning ("peer unresponsive, give up");
600 am->my_client_index = ~0;
601 am->my_registration = 0;
602 am->shmem_hdr = 0;
603 break;
604 }
605 if (unix_shared_memory_queue_sub (vl_input_queue, (u8 *)&rp, 1) < 0)
606 continue;
607
608 /* drain the queue */
609 if (ntohs(rp->_vl_msg_id) != VL_API_MEMCLNT_DELETE_REPLY) {
610 vl_msg_api_handler ((void *)rp);
611 continue;
612 }
613 vl_msg_api_handler((void *)rp);
614 break;
615 }
616}
617
618static inline vl_api_registration_t
619*vl_api_client_index_to_registration_internal (u32 handle)
620{
621 vl_api_registration_t **regpp;
622 vl_api_registration_t *regp;
623 api_main_t *am = &api_main;
624 u32 index;
625
626 index = vl_msg_api_handle_get_index (handle);
627 if ((am->shmem_hdr->application_restarts & VL_API_EPOCH_MASK)
628 != vl_msg_api_handle_get_epoch (handle)) {
629 vl_msg_api_increment_missing_client_counter();
630 return 0;
631 }
632
633 regpp = am->vl_clients + index;
634
635 if (pool_is_free(am->vl_clients, regpp)) {
636 vl_msg_api_increment_missing_client_counter();
637 return 0;
638 }
639 regp = *regpp;
640 return (regp);
641}
642
643vl_api_registration_t *vl_api_client_index_to_registration (u32 index)
644{
645 return (vl_api_client_index_to_registration_internal (index));
646}
647
648unix_shared_memory_queue_t *vl_api_client_index_to_input_queue (u32 index)
649{
650 vl_api_registration_t *regp;
651
652 regp = vl_api_client_index_to_registration_internal (index);
653 if (!regp)
654 return 0;
655 return (regp->vl_input_queue);
656}
657
658#define foreach_api_client_msg \
659_(MEMCLNT_CREATE_REPLY, memclnt_create_reply) \
660_(MEMCLNT_DELETE_REPLY, memclnt_delete_reply)
661
662int vl_client_api_map (char *region_name)
663{
664 int rv;
665
666 if ((rv = vl_map_shmem (region_name, 0 /* is_vlib */)) < 0) {
667 return rv;
668 }
669
670#define _(N,n) \
671 vl_msg_api_set_handlers(VL_API_##N, 0 /* name */, \
672 vl_api_##n##_t_handler, \
673 0/* cleanup */, 0/* endian */, 0/* print */, \
674 sizeof(vl_api_##n##_t), 1);
675 foreach_api_client_msg;
676#undef _
677 return 0;
678}
679
680void vl_client_api_unmap (void)
681{
682 vl_unmap_shmem();
683}