Python-API: Inital commit of Python bindings for the VPP API.
[vpp.git] / vlib-api / vlibmemory / memory_shared.c
1 /* 
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>
24 #include <stddef.h>
25 #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
42 static 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);
118     rv = clib_mem_alloc(nbytes);
119     rv->q = 0;
120     svm_pop_heap (oldheap);
121     pthread_mutex_unlock (&am->vlib_rp->mutex);
122
123  out:
124     rv->data_len = htonl(nbytes - sizeof(msgbuf_t));
125     return(rv->data);
126 }
127
128 void *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
141 void *vl_msg_api_alloc_as_if_client (int nbytes)
142 {
143     return vl_msg_api_alloc_internal (nbytes, 0);
144 }
145
146 void vl_msg_api_free(void *a)
147 {
148     msgbuf_t *rv;
149     void *oldheap;
150     api_main_t *am = &api_main;
151     
152     rv = (msgbuf_t *)(((u8 *)a) - offsetof(msgbuf_t, data));
153
154     /*
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
171 static void vl_msg_api_free_nolock (void *a)
172 {
173     msgbuf_t *rv;
174     void *oldheap;
175     api_main_t *am = &api_main;
176     
177     rv = (msgbuf_t *)(((u8 *)a) - offsetof(msgbuf_t, data));
178     /*
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
193 void vl_set_memory_root_path (char *name)
194 {
195     api_main_t *am = &api_main;
196
197     am->root_path = name;
198 }
199
200 int vl_map_shmem (char *region_name, int is_vlib)
201 {
202     svm_map_region_args_t *a = 0;
203     svm_region_t *vlib_rp, *root_rp;
204     void *oldheap;
205     vl_shmem_hdr_t *shmem_hdr=0;
206     api_main_t *am = &api_main;
207     int i;
208     struct timespec ts, tsrem;
209
210     if (is_vlib == 0)
211         svm_region_init_chroot(am->root_path);
212
213     vec_validate (a, 0);
214
215     a->name = region_name;
216     a->size = 16<<20;
217     a->flags = SVM_FLAGS_MHEAP;
218
219     vlib_rp = svm_region_find_or_create (a);
220     
221     vec_free (a);
222
223     if (vlib_rp == 0)
224         return (-2);
225
226     pthread_mutex_lock (&vlib_rp->mutex);
227     /* Has someone else set up the shared-memory variable table? */
228     if (vlib_rp->user_ctx) {
229         am->shmem_hdr = (void *) vlib_rp->user_ctx;
230         am->our_pid = getpid();
231         if (is_vlib) {
232             unix_shared_memory_queue_t *q;
233             uword old_msg;
234             /* 
235              * application restart. Reset cached pids, API message
236              * rings, list of clients; otherwise, various things
237              * fail. (e.g. queue non-empty notification) 
238              */
239
240             /* ghosts keep the region from disappearing properly */
241             svm_client_scan_this_region_nolock(vlib_rp);
242             am->shmem_hdr->application_restarts++;
243             q = am->shmem_hdr->vl_input_queue;
244             am->shmem_hdr->vl_pid = getpid();
245             q->consumer_pid = am->shmem_hdr->vl_pid;
246             /* Drain the input queue, freeing msgs */
247             for (i = 0; i < 10; i++) {
248                 if (pthread_mutex_trylock (&q->mutex) == 0) {
249                     pthread_mutex_unlock (&q->mutex);
250                     goto mutex_ok;
251                 }
252                 ts.tv_sec = 0;
253                 ts.tv_nsec = 10000*1000;  /* 10 ms */
254                 while (nanosleep(&ts, &tsrem) < 0)
255                     ts = tsrem;
256             }
257             /* Mutex buggered, "fix" it */
258             memset (&q->mutex, 0, sizeof (q->mutex));
259             clib_warning ("forcibly release main input queue mutex");
260
261         mutex_ok:
262             am->vlib_rp = vlib_rp;
263             while (unix_shared_memory_queue_sub (q, 
264                                                  (u8 *)&old_msg, 
265                                                  1 /* nowait */) 
266                    != -2 /* queue underflow */) {
267                 vl_msg_api_free_nolock ((void *)old_msg);
268                 am->shmem_hdr->restart_reclaims++;
269             }
270             pthread_mutex_unlock (&vlib_rp->mutex);
271             root_rp = svm_get_root_rp();
272             ASSERT(root_rp);
273             /* Clean up the root region client list */
274             pthread_mutex_lock (&root_rp->mutex);
275             svm_client_scan_this_region_nolock (root_rp);
276             pthread_mutex_unlock (&root_rp->mutex);
277         } else {
278             pthread_mutex_unlock (&vlib_rp->mutex);
279             /* 
280              * Make sure the vlib app is really there...
281              * Wait up to 100 seconds... 
282              */
283             for (i = 0; i < 10000; i++) {
284                 /* Yup, it's there, off we go... */
285                 if (kill (am->shmem_hdr->vl_pid, 0) >= 0)
286                     break;
287
288                 ts.tv_sec = 0;
289                 ts.tv_nsec = 10000*1000;  /* 10 ms */
290                 while (nanosleep(&ts, &tsrem) < 0)
291                     ts = tsrem;
292             }
293         }
294
295         am->vlib_rp = vlib_rp;
296         vec_add1(am->mapped_shmem_regions, vlib_rp);
297         return 0;
298     }
299     /* Clients simply have to wait... */
300     if (!is_vlib) {
301         pthread_mutex_unlock (&vlib_rp->mutex);
302
303         /* Wait up to 100 seconds... */
304         for (i = 0; i < 10000; i++) {
305             ts.tv_sec = 0;
306             ts.tv_nsec = 10000*1000;  /* 10 ms */
307             while (nanosleep(&ts, &tsrem) < 0)
308                 ts = tsrem;
309             if (vlib_rp->user_ctx)
310                 goto ready;
311         }
312         /* Clean up and leave... */
313         svm_region_unmap (vlib_rp);
314         clib_warning ("region init fail");
315         return (-2);
316
317     ready:
318         am->shmem_hdr = (void *)vlib_rp->user_ctx;
319         am->our_pid = getpid();
320         am->vlib_rp = vlib_rp;
321         vec_add1(am->mapped_shmem_regions, vlib_rp);
322         return 0;
323     }
324
325     /* Nope, it's our problem... */
326
327     oldheap = svm_push_data_heap (vlib_rp);
328
329     vec_validate(shmem_hdr, 0);
330     shmem_hdr->version = VL_SHM_VERSION;
331
332     /* vlib main input queue */
333     shmem_hdr->vl_input_queue = 
334         unix_shared_memory_queue_init (1024, sizeof (uword), getpid(),
335                                        am->vlib_signal);
336
337     /* Set up the msg ring allocator */
338 #define _(sz,n)                                                 \
339     do {                                                        \
340         ring_alloc_t _rp;                                       \
341         _rp.rp = unix_shared_memory_queue_init ((n), (sz), 0, 0); \
342         _rp.size = (sz);                                        \
343         _rp.nitems = n;                                         \
344         _rp.hits = 0;                                           \
345         _rp.misses = 0;                                         \
346         vec_add1(shmem_hdr->vl_rings, _rp);                     \
347     } while (0);
348
349     foreach_vl_aring_size;
350 #undef _
351
352 #define _(sz,n)                                                 \
353     do {                                                        \
354         ring_alloc_t _rp;                                       \
355         _rp.rp = unix_shared_memory_queue_init ((n), (sz), 0, 0); \
356         _rp.size = (sz);                                        \
357         _rp.nitems = n;                                         \
358         _rp.hits = 0;                                           \
359         _rp.misses = 0;                                         \
360         vec_add1(shmem_hdr->client_rings, _rp);                 \
361     } while (0);
362
363     foreach_clnt_aring_size;
364 #undef _
365
366     am->shmem_hdr = shmem_hdr;
367     am->vlib_rp = vlib_rp;
368     am->our_pid = getpid();
369     if (is_vlib)
370         am->shmem_hdr->vl_pid = am->our_pid;
371     
372     svm_pop_heap (oldheap);
373
374     /* 
375      * After absolutely everything that a client might see is set up,
376      * declare the shmem region valid
377      */
378     vlib_rp->user_ctx = shmem_hdr;
379
380     pthread_mutex_unlock (&vlib_rp->mutex);
381     vec_add1(am->mapped_shmem_regions, vlib_rp);
382     return 0;
383 }
384
385 void vl_register_mapped_shmem_region(svm_region_t *rp)
386 {
387     api_main_t *am = &api_main;
388
389     vec_add1(am->mapped_shmem_regions, rp);
390 }
391
392 void vl_unmap_shmem (void)
393 {
394     svm_region_t *rp;
395     int i;
396     api_main_t *am = &api_main;
397
398     if (! svm_get_root_rp())
399       return;
400
401     for (i = 0; i < vec_len(am->mapped_shmem_regions); i++) {
402         rp = am->mapped_shmem_regions[i];
403         svm_region_unmap (rp);
404     }
405
406     vec_free(am->mapped_shmem_regions);
407     am->shmem_hdr = 0;
408
409     svm_region_exit ();
410     /* $$$ more careful cleanup, valgrind run... */
411     vec_free (am->msg_handlers);
412     vec_free (am->msg_endian_handlers);
413     vec_free (am->msg_print_handlers);
414 }
415
416 void vl_msg_api_send_shmem (unix_shared_memory_queue_t *q, u8 *elem)
417 {
418     api_main_t *am = &api_main;
419     uword *trace = (uword *)elem;
420
421     if (am->tx_trace && am->tx_trace->enabled)
422         vl_msg_api_trace(am, am->tx_trace, (void *)trace[0]);
423
424     (void)unix_shared_memory_queue_add(q, elem, 0 /* nowait */);
425 }
426
427 void vl_msg_api_send_shmem_nolock (unix_shared_memory_queue_t *q, u8 *elem)
428 {
429     api_main_t *am = &api_main;
430     uword *trace = (uword *)elem;
431
432     if (am->tx_trace && am->tx_trace->enabled)
433         vl_msg_api_trace(am, am->tx_trace, (void *)trace[0]);
434
435     (void)unix_shared_memory_queue_add_nolock (q, elem);
436 }
437
438 static void vl_api_memclnt_create_reply_t_handler (
439     vl_api_memclnt_create_reply_t *mp)
440 {
441     api_main_t *am = &api_main;
442     int rv;
443
444     am->my_client_index = mp->index;
445     am->my_registration = (vl_api_registration_t *)(uword)
446         mp->handle;
447
448     rv = ntohl(mp->response);
449
450     if (rv < 0)
451         clib_warning ("WARNING: API mismatch detected");
452 }        
453
454 void vl_client_add_api_signatures (vl_api_memclnt_create_t *mp) 
455     __attribute__((weak));
456
457 void vl_client_add_api_signatures (vl_api_memclnt_create_t *mp) 
458 {
459     int i;
460
461     for (i = 0; i < ARRAY_LEN(mp->api_versions); i++)
462         mp->api_versions[i] = 0;
463 }
464
465 int vl_client_connect (char *name, int ctx_quota, int input_queue_size)
466 {
467     svm_region_t *svm;
468     vl_api_memclnt_create_t *mp;
469     vl_api_memclnt_create_reply_t *rp;
470     unix_shared_memory_queue_t *vl_input_queue;
471     vl_shmem_hdr_t *shmem_hdr;
472     int rv=0;
473     void *oldheap;
474     api_main_t *am = &api_main;
475
476     if (am->my_registration) {
477         clib_warning ("client %s already connected...", name);
478         return -1;
479     }
480
481     if (am->vlib_rp == 0) {
482         clib_warning ("am->vlib_rp NULL");
483         return -1;
484     }
485
486     svm = am->vlib_rp;
487     shmem_hdr = am->shmem_hdr;
488
489     if (shmem_hdr == 0 || shmem_hdr->vl_input_queue == 0) {
490         clib_warning ("shmem_hdr / input queue NULL");
491         return -1;
492     }
493
494     pthread_mutex_lock (&svm->mutex);
495     oldheap = svm_push_data_heap(svm);
496     vl_input_queue = 
497         unix_shared_memory_queue_init (input_queue_size, sizeof(uword), 
498                                        getpid(), 0);
499     pthread_mutex_unlock(&svm->mutex);
500     svm_pop_heap (oldheap);
501
502     am->my_client_index = ~0;
503     am->my_registration = 0;
504     am->vl_input_queue = vl_input_queue;
505
506     mp = vl_msg_api_alloc(sizeof(vl_api_memclnt_create_t));
507     memset(mp, 0, sizeof (*mp));
508     mp->_vl_msg_id = ntohs(VL_API_MEMCLNT_CREATE);
509     mp->ctx_quota = ctx_quota;
510     mp->input_queue = (uword)vl_input_queue;
511     strncpy ((char *) mp->name, name, sizeof(mp->name)-1);
512
513     vl_client_add_api_signatures(mp);
514     
515     vl_msg_api_send_shmem (shmem_hdr->vl_input_queue, (u8 *)&mp);
516
517     while (1) {
518         int qstatus;
519         struct timespec ts, tsrem;
520         int i;
521
522         /* Wait up to 10 seconds */
523         for (i = 0; i < 1000; i++) {
524             qstatus = unix_shared_memory_queue_sub (vl_input_queue, (u8 *)&rp, 
525                                                     1 /* nowait */);
526             if (qstatus == 0)
527                 goto read_one_msg;
528             ts.tv_sec = 0;
529             ts.tv_nsec = 10000*1000;  /* 10 ms */
530             while (nanosleep(&ts, &tsrem) < 0)
531                 ts = tsrem;
532         }
533         /* Timeout... */
534         clib_warning ("memclnt_create_reply timeout");
535         return -1;
536
537     read_one_msg:
538         if (ntohs(rp->_vl_msg_id) != VL_API_MEMCLNT_CREATE_REPLY) {
539             clib_warning ("unexpected reply: id %d", ntohs(rp->_vl_msg_id));
540             continue;
541         }
542         rv = clib_net_to_host_u32(rp->response);
543
544         vl_msg_api_handler((void *)rp);
545         break;
546     }
547     return (rv);
548 }
549
550 static void vl_api_memclnt_delete_reply_t_handler (
551     vl_api_memclnt_delete_reply_t *mp)
552 {
553     void *oldheap;
554     api_main_t *am = &api_main;
555
556     pthread_mutex_lock (&am->vlib_rp->mutex);
557     oldheap = svm_push_data_heap(am->vlib_rp);
558     unix_shared_memory_queue_free (am->vl_input_queue);
559     pthread_mutex_unlock (&am->vlib_rp->mutex);
560     svm_pop_heap (oldheap);
561
562     am->my_client_index = ~0;
563     am->my_registration = 0;
564     am->vl_input_queue = 0;
565 }        
566
567 void vl_client_disconnect (void)
568 {
569     vl_api_memclnt_delete_t *mp;
570     vl_api_memclnt_delete_reply_t *rp;
571     unix_shared_memory_queue_t *vl_input_queue;
572     vl_shmem_hdr_t *shmem_hdr;
573     time_t begin;
574     api_main_t *am = &api_main;
575     
576     ASSERT(am->vlib_rp);
577     shmem_hdr = am->shmem_hdr;
578     ASSERT(shmem_hdr && shmem_hdr->vl_input_queue);
579
580     vl_input_queue = am->vl_input_queue;
581
582     mp = vl_msg_api_alloc(sizeof(vl_api_memclnt_delete_t));
583     memset(mp, 0, sizeof (*mp));
584     mp->_vl_msg_id = ntohs(VL_API_MEMCLNT_DELETE);
585     mp->index = am->my_client_index;
586     mp->handle = (uword) am->my_registration;
587
588     vl_msg_api_send_shmem (shmem_hdr->vl_input_queue, (u8 *)&mp);
589
590     /* 
591      * Have to be careful here, in case the client is disconnecting
592      * because e.g. the vlib process died, or is unresponsive.
593      */
594     
595     begin = time (0);
596     while (1) {
597         time_t now;
598
599         now = time (0);
600
601         if (now >= (begin + 2)) {
602             clib_warning ("peer unresponsive, give up");
603             am->my_client_index = ~0;
604             am->my_registration = 0;
605             am->shmem_hdr = 0;
606             break;
607         }
608         if (unix_shared_memory_queue_sub (vl_input_queue, (u8 *)&rp, 1) < 0)
609             continue;
610         
611         /* drain the queue */
612         if (ntohs(rp->_vl_msg_id) != VL_API_MEMCLNT_DELETE_REPLY) {
613             vl_msg_api_handler ((void *)rp);
614             continue;
615         }
616         vl_msg_api_handler((void *)rp);
617         break;
618     }
619 }
620
621 static inline vl_api_registration_t 
622 *vl_api_client_index_to_registration_internal (u32 handle)
623 {
624     vl_api_registration_t **regpp;
625     vl_api_registration_t *regp;
626     api_main_t *am = &api_main;
627     u32 index;
628
629     index = vl_msg_api_handle_get_index (handle);
630     if ((am->shmem_hdr->application_restarts & VL_API_EPOCH_MASK)
631         != vl_msg_api_handle_get_epoch (handle)) {
632         vl_msg_api_increment_missing_client_counter();
633         return 0;
634     }
635
636     regpp = am->vl_clients + index;
637
638     if (pool_is_free(am->vl_clients, regpp)) {
639         vl_msg_api_increment_missing_client_counter();
640         return 0;
641     }
642     regp = *regpp;
643     return (regp);
644 }
645
646 vl_api_registration_t *vl_api_client_index_to_registration (u32 index)
647 {
648     return (vl_api_client_index_to_registration_internal (index));
649 }
650
651 unix_shared_memory_queue_t *vl_api_client_index_to_input_queue (u32 index)
652 {
653     vl_api_registration_t *regp;
654
655     regp = vl_api_client_index_to_registration_internal (index);
656     if (!regp)
657         return 0;
658     return (regp->vl_input_queue);
659 }
660
661 #define foreach_api_client_msg                  \
662 _(MEMCLNT_CREATE_REPLY, memclnt_create_reply)   \
663 _(MEMCLNT_DELETE_REPLY, memclnt_delete_reply)
664
665 int vl_client_api_map (char *region_name)
666 {
667     int rv;
668
669     if ((rv = vl_map_shmem (region_name, 0 /* is_vlib */)) < 0) {
670         return rv;
671     }
672
673 #define _(N,n)                                                          \
674     vl_msg_api_set_handlers(VL_API_##N, 0 /* name */,                   \
675                            vl_api_##n##_t_handler,                      \
676                            0/* cleanup */, 0/* endian */, 0/* print */, \
677                            sizeof(vl_api_##n##_t), 1); 
678     foreach_api_client_msg;
679 #undef _
680     return 0;
681 }
682
683 void vl_client_api_unmap (void)
684 {
685     vl_unmap_shmem();
686 }