api: multiple connections per process
[vpp.git] / src / vlibmemory / memory_client.c
1 /*
2  *------------------------------------------------------------------
3  * memory_client.c - API message handling, client code.
4  *
5  * Copyright (c) 2010 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 <setjmp.h>
21
22 #include <svm/svm.h>
23 #include <svm/ssvm.h>
24 #include <vppinfra/serialize.h>
25 #include <vppinfra/hash.h>
26 #include <vlibmemory/memory_client.h>
27 #include <vlibapi/api_common.h>
28
29 /* A hack. vl_client_get_first_plugin_msg_id depends on it */
30 #include <vlibmemory/socket_client.h>
31
32 #include <vlibmemory/vl_memory_msg_enum.h>
33
34 #define vl_typedefs             /* define message structures */
35 #include <vlibmemory/vl_memory_api_h.h>
36 #undef vl_typedefs
37
38 #define vl_endianfun            /* define message structures */
39 #include <vlibmemory/vl_memory_api_h.h>
40 #undef vl_endianfun
41
42 /* instantiate all the print functions we know about */
43 #define vl_print(handle, ...) clib_warning (__VA_ARGS__)
44 #define vl_printfun
45 #include <vlibmemory/vl_memory_api_h.h>
46 #undef vl_printfun
47
48 memory_client_main_t memory_client_main;
49 __thread memory_client_main_t *my_memory_client_main = &memory_client_main;
50
51 static void *
52 rx_thread_fn (void *arg)
53 {
54   svm_queue_t *q;
55   memory_client_main_t *mm = vlibapi_get_memory_client_main ();
56
57   q = vlibapi_get_main ()->vl_input_queue;
58
59   /* So we can make the rx thread terminate cleanly */
60   if (setjmp (mm->rx_thread_jmpbuf) == 0)
61     {
62       mm->rx_thread_jmpbuf_valid = 1;
63       clib_mem_set_thread_index ();
64       while (1)
65         vl_msg_api_queue_handler (q);
66     }
67   pthread_exit (0);
68 }
69
70 static void
71 vl_api_rx_thread_exit_t_handler (vl_api_rx_thread_exit_t * mp)
72 {
73   memory_client_main_t *mm = vlibapi_get_memory_client_main ();
74   if (mm->rx_thread_jmpbuf_valid)
75     longjmp (mm->rx_thread_jmpbuf, 1);
76 }
77
78 static void
79 vl_api_name_and_crc_free (void)
80 {
81   api_main_t *am = vlibapi_get_main ();
82   int i;
83   u8 **keys = 0;
84   hash_pair_t *hp;
85
86   if (!am->msg_index_by_name_and_crc)
87     return;
88
89   /* *INDENT-OFF* */
90   hash_foreach_pair (hp, am->msg_index_by_name_and_crc,
91       ({
92         vec_add1 (keys, (u8 *) hp->key);
93       }));
94   /* *INDENT-ON* */
95   for (i = 0; i < vec_len (keys); i++)
96     vec_free (keys[i]);
97   vec_free (keys);
98   hash_free (am->msg_index_by_name_and_crc);
99 }
100
101 CLIB_NOSANITIZE_ADDR static void
102 VL_API_VEC_UNPOISON (const void *v)
103 {
104   const vec_header_t *vh = &((vec_header_t *) v)[-1];
105   CLIB_MEM_UNPOISON (vh, sizeof (*vh) + vec_len (v));
106 }
107
108 static void
109 vl_api_memclnt_create_reply_t_handler (vl_api_memclnt_create_reply_t * mp)
110 {
111   serialize_main_t _sm, *sm = &_sm;
112   api_main_t *am = vlibapi_get_main ();
113   u8 *tblv;
114   u32 nmsgs;
115   int i;
116   u8 *name_and_crc;
117   u32 msg_index;
118
119   am->my_client_index = mp->index;
120   am->my_registration = (vl_api_registration_t *) (uword) mp->handle;
121
122   /* Clean out any previous hash table (unlikely) */
123   vl_api_name_and_crc_free ();
124
125   am->msg_index_by_name_and_crc = hash_create_string (0, sizeof (uword));
126
127   /* Recreate the vnet-side API message handler table */
128   tblv = uword_to_pointer (mp->message_table, u8 *);
129   unserialize_open_data (sm, tblv, vec_len (tblv));
130   unserialize_integer (sm, &nmsgs, sizeof (u32));
131
132   VL_API_VEC_UNPOISON (tblv);
133
134   for (i = 0; i < nmsgs; i++)
135     {
136       msg_index = unserialize_likely_small_unsigned_integer (sm);
137       unserialize_cstring (sm, (char **) &name_and_crc);
138       hash_set_mem (am->msg_index_by_name_and_crc, name_and_crc, msg_index);
139     }
140 }
141
142 static void
143 noop_handler (void *notused)
144 {
145 }
146
147 void vl_msg_api_send_shmem (svm_queue_t * q, u8 * elem);
148 int
149 vl_client_connect (const char *name, int ctx_quota, int input_queue_size)
150 {
151   svm_region_t *svm;
152   vl_api_memclnt_create_t *mp;
153   vl_api_memclnt_create_reply_t *rp;
154   svm_queue_t *vl_input_queue;
155   vl_shmem_hdr_t *shmem_hdr;
156   int rv = 0;
157   void *oldheap;
158   api_main_t *am = vlibapi_get_main ();
159
160   if (am->my_registration)
161     {
162       clib_warning ("client %s already connected...", name);
163       return -1;
164     }
165
166   if (am->vlib_rp == 0)
167     {
168       clib_warning ("am->vlib_rp NULL");
169       return -1;
170     }
171
172   svm = am->vlib_rp;
173   shmem_hdr = am->shmem_hdr;
174
175   if (shmem_hdr == 0 || shmem_hdr->vl_input_queue == 0)
176     {
177       clib_warning ("shmem_hdr / input queue NULL");
178       return -1;
179     }
180
181   CLIB_MEM_UNPOISON (shmem_hdr, sizeof (*shmem_hdr));
182   VL_MSG_API_SVM_QUEUE_UNPOISON (shmem_hdr->vl_input_queue);
183
184   pthread_mutex_lock (&svm->mutex);
185   oldheap = svm_push_data_heap (svm);
186   vl_input_queue = svm_queue_alloc_and_init (input_queue_size, sizeof (uword),
187                                              getpid ());
188   svm_pop_heap (oldheap);
189   pthread_mutex_unlock (&svm->mutex);
190
191   am->my_client_index = ~0;
192   am->my_registration = 0;
193   am->vl_input_queue = vl_input_queue;
194
195   mp = vl_msg_api_alloc (sizeof (vl_api_memclnt_create_t));
196   clib_memset (mp, 0, sizeof (*mp));
197   mp->_vl_msg_id = ntohs (VL_API_MEMCLNT_CREATE);
198   mp->ctx_quota = ctx_quota;
199   mp->input_queue = (uword) vl_input_queue;
200   strncpy ((char *) mp->name, name, sizeof (mp->name) - 1);
201
202   vl_msg_api_send_shmem (shmem_hdr->vl_input_queue, (u8 *) & mp);
203
204   while (1)
205     {
206       int qstatus;
207       struct timespec ts, tsrem;
208       int i;
209
210       /* Wait up to 10 seconds */
211       for (i = 0; i < 1000; i++)
212         {
213           qstatus = svm_queue_sub (vl_input_queue, (u8 *) & rp,
214                                    SVM_Q_NOWAIT, 0);
215           if (qstatus == 0)
216             goto read_one_msg;
217           ts.tv_sec = 0;
218           ts.tv_nsec = 10000 * 1000;    /* 10 ms */
219           while (nanosleep (&ts, &tsrem) < 0)
220             ts = tsrem;
221         }
222       /* Timeout... */
223       clib_warning ("memclnt_create_reply timeout");
224       return -1;
225
226     read_one_msg:
227       VL_MSG_API_UNPOISON (rp);
228       if (ntohs (rp->_vl_msg_id) != VL_API_MEMCLNT_CREATE_REPLY)
229         {
230           clib_warning ("unexpected reply: id %d", ntohs (rp->_vl_msg_id));
231           continue;
232         }
233       rv = clib_net_to_host_u32 (rp->response);
234
235       vl_msg_api_handler ((void *) rp);
236       break;
237     }
238   return (rv);
239 }
240
241 static void
242 vl_api_memclnt_delete_reply_t_handler (vl_api_memclnt_delete_reply_t * mp)
243 {
244   void *oldheap;
245   api_main_t *am = vlibapi_get_main ();
246
247   pthread_mutex_lock (&am->vlib_rp->mutex);
248   oldheap = svm_push_data_heap (am->vlib_rp);
249   svm_queue_free (am->vl_input_queue);
250   pthread_mutex_unlock (&am->vlib_rp->mutex);
251   svm_pop_heap (oldheap);
252
253   am->my_client_index = ~0;
254   am->my_registration = 0;
255   am->vl_input_queue = 0;
256 }
257
258 void
259 vl_client_send_disconnect (u8 do_cleanup)
260 {
261   vl_api_memclnt_delete_t *mp;
262   vl_shmem_hdr_t *shmem_hdr;
263   api_main_t *am = vlibapi_get_main ();
264
265   ASSERT (am->vlib_rp);
266   shmem_hdr = am->shmem_hdr;
267   ASSERT (shmem_hdr && shmem_hdr->vl_input_queue);
268
269   mp = vl_msg_api_alloc (sizeof (vl_api_memclnt_delete_t));
270   clib_memset (mp, 0, sizeof (*mp));
271   mp->_vl_msg_id = ntohs (VL_API_MEMCLNT_DELETE);
272   mp->index = am->my_client_index;
273   mp->handle = (uword) am->my_registration;
274   mp->do_cleanup = do_cleanup;
275
276   vl_msg_api_send_shmem (shmem_hdr->vl_input_queue, (u8 *) & mp);
277 }
278
279 int
280 vl_client_disconnect (void)
281 {
282   vl_api_memclnt_delete_reply_t *rp;
283   svm_queue_t *vl_input_queue;
284   api_main_t *am = vlibapi_get_main ();
285   time_t begin;
286
287   vl_input_queue = am->vl_input_queue;
288   vl_client_send_disconnect (0 /* wait for reply */ );
289
290   /*
291    * Have to be careful here, in case the client is disconnecting
292    * because e.g. the vlib process died, or is unresponsive.
293    */
294   begin = time (0);
295   while (1)
296     {
297       time_t now;
298
299       now = time (0);
300
301       if (now >= (begin + 2))
302         {
303           clib_warning ("peer unresponsive, give up");
304           am->my_client_index = ~0;
305           am->my_registration = 0;
306           am->shmem_hdr = 0;
307           return -1;
308         }
309       if (svm_queue_sub (vl_input_queue, (u8 *) & rp, SVM_Q_NOWAIT, 0) < 0)
310         continue;
311
312       VL_MSG_API_UNPOISON (rp);
313
314       /* drain the queue */
315       if (ntohs (rp->_vl_msg_id) != VL_API_MEMCLNT_DELETE_REPLY)
316         {
317           clib_warning ("queue drain: %d", ntohs (rp->_vl_msg_id));
318           vl_msg_api_handler ((void *) rp);
319           continue;
320         }
321       vl_msg_api_handler ((void *) rp);
322       break;
323     }
324
325   vl_api_name_and_crc_free ();
326   return 0;
327 }
328
329 /**
330  * Stave off the binary API dead client reaper
331  * Only sent to inactive clients
332  */
333 static void
334 vl_api_memclnt_keepalive_t_handler (vl_api_memclnt_keepalive_t * mp)
335 {
336   vl_api_memclnt_keepalive_reply_t *rmp;
337   api_main_t *am;
338   vl_shmem_hdr_t *shmem_hdr;
339
340   am = vlibapi_get_main ();
341   shmem_hdr = am->shmem_hdr;
342
343   rmp = vl_msg_api_alloc_as_if_client (sizeof (*rmp));
344   clib_memset (rmp, 0, sizeof (*rmp));
345   rmp->_vl_msg_id = ntohs (VL_API_MEMCLNT_KEEPALIVE_REPLY);
346   rmp->context = mp->context;
347   vl_msg_api_send_shmem (shmem_hdr->vl_input_queue, (u8 *) & rmp);
348 }
349
350 #define foreach_api_msg                         \
351 _(RX_THREAD_EXIT, rx_thread_exit)               \
352 _(MEMCLNT_CREATE_REPLY, memclnt_create_reply)   \
353 _(MEMCLNT_DELETE_REPLY, memclnt_delete_reply)   \
354 _(MEMCLNT_KEEPALIVE, memclnt_keepalive)
355
356 void
357 vl_client_install_client_message_handlers (void)
358 {
359
360 #define _(N,n)                                                  \
361     vl_msg_api_set_handlers(VL_API_##N, #n,                     \
362                             vl_api_##n##_t_handler,             \
363                             noop_handler,                       \
364                             vl_api_##n##_t_endian,              \
365                             vl_api_##n##_t_print,               \
366                             sizeof(vl_api_##n##_t), 1);
367   foreach_api_msg;
368 #undef _
369 }
370
371 int
372 vl_client_api_map (const char *region_name)
373 {
374   int rv;
375
376   if ((rv = vl_map_shmem (region_name, 0 /* is_vlib */ )) < 0)
377     return rv;
378
379   vl_client_install_client_message_handlers ();
380   return 0;
381 }
382
383 void
384 vl_client_api_unmap (void)
385 {
386   vl_unmap_shmem_client ();
387 }
388
389 u8
390 vl_mem_client_is_connected (void)
391 {
392   return (memory_client_main.connected_to_vlib != 0);
393 }
394
395 static int
396 connect_to_vlib_internal (const char *svm_name,
397                           const char *client_name,
398                           int rx_queue_size, void *(*thread_fn) (void *),
399                           void *thread_fn_arg, int do_map)
400 {
401   int rv = 0;
402   memory_client_main_t *mm = vlibapi_get_memory_client_main ();
403   api_main_t *am = vlibapi_get_main ();
404
405   if (do_map && (rv = vl_client_api_map (svm_name)))
406     {
407       clib_warning ("vl_client_api map rv %d", rv);
408       return rv;
409     }
410
411   if (vl_client_connect (client_name, 0 /* punt quota */ ,
412                          rx_queue_size /* input queue */ ) < 0)
413     {
414       vl_client_api_unmap ();
415       return -1;
416     }
417
418   /* Start the rx queue thread */
419
420   if (thread_fn)
421     {
422       rv = pthread_create (&mm->rx_thread_handle,
423                            NULL /*attr */ , thread_fn, thread_fn_arg);
424       if (rv)
425         {
426           clib_warning ("pthread_create returned %d", rv);
427           am->rx_thread_handle = 0;
428         }
429       else
430         {
431           am->rx_thread_handle = mm->rx_thread_handle;
432         }
433     }
434
435   mm->connected_to_vlib = 1;
436   return 0;
437 }
438
439 int
440 vl_client_connect_to_vlib (const char *svm_name,
441                            const char *client_name, int rx_queue_size)
442 {
443   return connect_to_vlib_internal (svm_name, client_name, rx_queue_size,
444                                    rx_thread_fn, 0 /* thread fn arg */ ,
445                                    1 /* do map */ );
446 }
447
448 int
449 vl_client_connect_to_vlib_no_rx_pthread (const char *svm_name,
450                                          const char *client_name,
451                                          int rx_queue_size)
452 {
453   return connect_to_vlib_internal (svm_name, client_name, rx_queue_size,
454                                    0 /* no rx_thread_fn */ ,
455                                    0 /* no thread fn arg */ ,
456                                    1 /* do map */ );
457 }
458
459 int
460 vl_client_connect_to_vlib_no_map (const char *svm_name,
461                                   const char *client_name, int rx_queue_size)
462 {
463   return connect_to_vlib_internal (svm_name, client_name, rx_queue_size,
464                                    rx_thread_fn, 0 /* no thread fn arg */ ,
465                                    0 /* dont map */ );
466 }
467
468 int
469 vl_client_connect_to_vlib_no_rx_pthread_no_map (const char *svm_name,
470                                                 const char *client_name,
471                                                 int rx_queue_size)
472 {
473   return connect_to_vlib_internal (svm_name, client_name, rx_queue_size,
474                                    0 /* no thread_fn */ ,
475                                    0 /* no thread fn arg */ ,
476                                    0 /* dont map */ );
477 }
478
479 int
480 vl_client_connect_to_vlib_thread_fn (const char *svm_name,
481                                      const char *client_name,
482                                      int rx_queue_size,
483                                      void *(*thread_fn) (void *), void *arg)
484 {
485   return connect_to_vlib_internal (svm_name, client_name, rx_queue_size,
486                                    thread_fn, arg, 1 /* do map */ );
487 }
488
489
490 static void
491 disconnect_from_vlib_internal (u8 do_unmap)
492 {
493   memory_client_main_t *mm = vlibapi_get_memory_client_main ();
494   api_main_t *am = vlibapi_get_main ();
495   uword junk;
496
497   if (mm->rx_thread_jmpbuf_valid)
498     {
499       vl_api_rx_thread_exit_t *ep;
500       ep = vl_msg_api_alloc (sizeof (*ep));
501       ep->_vl_msg_id = ntohs (VL_API_RX_THREAD_EXIT);
502       vl_msg_api_send_shmem (am->vl_input_queue, (u8 *) & ep);
503       pthread_join (mm->rx_thread_handle, (void **) &junk);
504     }
505   if (mm->connected_to_vlib)
506     {
507       vl_client_disconnect ();
508       if (do_unmap)
509         vl_client_api_unmap ();
510     }
511   clib_memset (mm, 0, sizeof (*mm));
512 }
513
514 void
515 vl_client_disconnect_from_vlib (void)
516 {
517   disconnect_from_vlib_internal (1);
518 }
519
520 void
521 vl_client_disconnect_from_vlib_no_unmap (void)
522 {
523   disconnect_from_vlib_internal (0);
524 }
525
526 static void vl_api_get_first_msg_id_reply_t_handler
527   (vl_api_get_first_msg_id_reply_t * mp)
528 {
529   memory_client_main_t *mm = vlibapi_get_memory_client_main ();
530   i32 retval = ntohl (mp->retval);
531
532   mm->first_msg_id_reply = (retval >= 0) ? ntohs (mp->first_msg_id) : ~0;
533   mm->first_msg_id_reply_ready = 1;
534 }
535
536 u16
537 vl_client_get_first_plugin_msg_id (const char *plugin_name)
538 {
539   vl_api_get_first_msg_id_t *mp;
540   api_main_t *am = vlibapi_get_main ();
541   memory_client_main_t *mm = vlibapi_get_memory_client_main ();
542   f64 timeout;
543   void *old_handler;
544   clib_time_t clib_time;
545   u16 rv = ~0;
546
547   if (strlen (plugin_name) + 1 > sizeof (mp->name))
548     return (rv);
549
550   clib_memset (&clib_time, 0, sizeof (clib_time));
551   clib_time_init (&clib_time);
552
553   /* Push this plugin's first_msg_id_reply handler */
554   old_handler = am->msg_handlers[VL_API_GET_FIRST_MSG_ID_REPLY];
555   am->msg_handlers[VL_API_GET_FIRST_MSG_ID_REPLY] = (void *)
556     vl_api_get_first_msg_id_reply_t_handler;
557
558   /* Ask the data-plane for the message-ID base of the indicated plugin */
559   mm->first_msg_id_reply_ready = 0;
560
561   /* Not using shm client */
562   if (!am->my_registration)
563     {
564       mp = vl_socket_client_msg_alloc (sizeof (*mp));
565       clib_memset (mp, 0, sizeof (*mp));
566       mp->_vl_msg_id = ntohs (VL_API_GET_FIRST_MSG_ID);
567       mp->client_index = am->my_client_index;
568       strncpy ((char *) mp->name, plugin_name, sizeof (mp->name) - 1);
569
570       if (vl_socket_client_write () <= 0)
571         goto sock_err;
572       if (vl_socket_client_read (1))
573         goto sock_err;
574
575       if (mm->first_msg_id_reply_ready == 1)
576         {
577           rv = mm->first_msg_id_reply;
578           goto result;
579         }
580
581     sock_err:
582       /* Restore old handler */
583       am->msg_handlers[VL_API_GET_FIRST_MSG_ID_REPLY] = old_handler;
584
585       return -1;
586     }
587   else
588     {
589       mp = vl_msg_api_alloc (sizeof (*mp));
590       clib_memset (mp, 0, sizeof (*mp));
591       mp->_vl_msg_id = ntohs (VL_API_GET_FIRST_MSG_ID);
592       mp->client_index = am->my_client_index;
593       strncpy ((char *) mp->name, plugin_name, sizeof (mp->name) - 1);
594
595       vl_msg_api_send_shmem (am->shmem_hdr->vl_input_queue, (u8 *) & mp);
596
597       /* Synchronously wait for the answer */
598       timeout = clib_time_now (&clib_time) + 1.0;
599       while (clib_time_now (&clib_time) < timeout)
600         {
601           if (mm->first_msg_id_reply_ready == 1)
602             {
603               rv = mm->first_msg_id_reply;
604               goto result;
605             }
606         }
607       /* Restore old handler */
608       am->msg_handlers[VL_API_GET_FIRST_MSG_ID_REPLY] = old_handler;
609
610       return rv;
611     }
612
613 result:
614
615   /* Restore the old handler */
616   am->msg_handlers[VL_API_GET_FIRST_MSG_ID_REPLY] = old_handler;
617
618   if (rv == (u16) ~ 0)
619     clib_warning ("plugin '%s' not registered", plugin_name);
620
621   return rv;
622 }
623
624 /*
625  * fd.io coding-style-patch-verification: ON
626  *
627  * Local Variables:
628  * eval: (c-set-style "gnu")
629  * End:
630  */