memory_client.c revision 39d69112
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
48memory_client_main_t memory_client_main;
49__thread memory_client_main_t *my_memory_client_main = &memory_client_main;
50
51static void *
52rx_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
70static void
71vl_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
78static void
79vl_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
101CLIB_NOSANITIZE_ADDR static void
102VL_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
108static void
109vl_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
142static void
143noop_handler (void *notused)
144{
145}
146
147void vl_msg_api_send_shmem (svm_queue_t * q, u8 * elem);
148int
149vl_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
241static void
242vl_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
258void
259vl_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
279int
280vl_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 */
333static void
334vl_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
356void
357vl_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
371int
372vl_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
383void
384vl_client_api_unmap (void)
385{
386  vl_unmap_shmem_client ();
387}
388
389u8
390vl_mem_client_is_connected (void)
391{
392  return (memory_client_main.connected_to_vlib != 0);
393}
394
395static int
396connect_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
439int
440vl_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
448int
449vl_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
459int
460vl_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
468int
469vl_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
479int
480vl_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
490static void
491disconnect_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
514void
515vl_client_disconnect_from_vlib (void)
516{
517  disconnect_from_vlib_internal (1);
518}
519
520void
521vl_client_disconnect_from_vlib_no_unmap (void)
522{
523  disconnect_from_vlib_internal (0);
524}
525
526static 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
536u16
537vl_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
613result:
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 */
631