ethernet: check destination mac for L3 in ethernet-input node
[vpp.git] / src / vlibmemory / memory_api.c
1 /*
2  *------------------------------------------------------------------
3  * Copyright (c) 2018 Cisco and/or its affiliates.
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at:
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  *------------------------------------------------------------------
16  */
17 #include <signal.h>
18
19 #include <vlib/vlib.h>
20 #include <vlibapi/api.h>
21 #include <vlibmemory/api.h>
22 #include <vlibmemory/memory_api.h>
23
24 #include <vlibmemory/vl_memory_msg_enum.h>      /* enumerate all vlib messages */
25
26 #define vl_typedefs             /* define message structures */
27 #include <vlibmemory/vl_memory_api_h.h>
28 #undef vl_typedefs
29
30 /* instantiate all the print functions we know about */
31 #define vl_printfun
32 #include <vlibmemory/vl_memory_api_h.h>
33 #undef vl_printfun
34
35 /* instantiate all the endian swap functions we know about */
36 #define vl_endianfun
37 #include <vlibmemory/vl_memory_api_h.h>
38 #undef vl_endianfun
39
40 volatile int **vl_api_queue_cursizes;
41
42 static void
43 memclnt_queue_callback (vlib_main_t * vm)
44 {
45   int i;
46   api_main_t *am = vlibapi_get_main ();
47   int have_pending_rpcs;
48
49   if (PREDICT_FALSE (vec_len (vl_api_queue_cursizes) !=
50                      1 + vec_len (am->vlib_private_rps)))
51     {
52       vl_shmem_hdr_t *shmem_hdr = am->shmem_hdr;
53       svm_queue_t *q;
54
55       if (shmem_hdr == 0)
56         return;
57
58       q = shmem_hdr->vl_input_queue;
59       if (q == 0)
60         return;
61
62       vec_add1 (vl_api_queue_cursizes, &q->cursize);
63
64       for (i = 0; i < vec_len (am->vlib_private_rps); i++)
65         {
66           svm_region_t *vlib_rp = am->vlib_private_rps[i];
67
68           shmem_hdr = (void *) vlib_rp->user_ctx;
69           q = shmem_hdr->vl_input_queue;
70           vec_add1 (vl_api_queue_cursizes, &q->cursize);
71         }
72     }
73
74   for (i = 0; i < vec_len (vl_api_queue_cursizes); i++)
75     {
76       if (*vl_api_queue_cursizes[i])
77         {
78           vm->queue_signal_pending = 1;
79           vm->api_queue_nonempty = 1;
80           vlib_process_signal_event (vm, vl_api_clnt_node.index,
81                                      /* event_type */ QUEUE_SIGNAL_EVENT,
82                                      /* event_data */ 0);
83           break;
84         }
85     }
86
87   clib_spinlock_lock_if_init (&vm->pending_rpc_lock);
88   have_pending_rpcs = vec_len (vm->pending_rpc_requests) > 0;
89   clib_spinlock_unlock_if_init (&vm->pending_rpc_lock);
90
91   if (have_pending_rpcs)
92     {
93       vm->queue_signal_pending = 1;
94       vm->api_queue_nonempty = 1;
95       vlib_process_signal_event (vm, vl_api_clnt_node.index,
96                                  /* event_type */ QUEUE_SIGNAL_EVENT,
97                                  /* event_data */ 0);
98     }
99 }
100
101 /*
102  * vl_api_memclnt_create_internal
103  */
104 u32
105 vl_api_memclnt_create_internal (char *name, svm_queue_t * q)
106 {
107   vl_api_registration_t **regpp;
108   vl_api_registration_t *regp;
109   void *oldheap;
110   api_main_t *am = vlibapi_get_main ();
111
112   ASSERT (vlib_get_thread_index () == 0);
113   pool_get (am->vl_clients, regpp);
114
115
116   oldheap = vl_msg_push_heap ();
117   *regpp = clib_mem_alloc (sizeof (vl_api_registration_t));
118
119   regp = *regpp;
120   clib_memset (regp, 0, sizeof (*regp));
121   regp->registration_type = REGISTRATION_TYPE_SHMEM;
122   regp->vl_api_registration_pool_index = regpp - am->vl_clients;
123   regp->vlib_rp = am->vlib_rp;
124   regp->shmem_hdr = am->shmem_hdr;
125
126   regp->vl_input_queue = q;
127   regp->name = format (0, "%s%c", name, 0);
128
129   vl_msg_pop_heap (oldheap);
130   return vl_msg_api_handle_from_index_and_epoch
131     (regp->vl_api_registration_pool_index,
132      am->shmem_hdr->application_restarts);
133 }
134
135 /*
136  * vl_api_memclnt_create_t_handler
137  */
138 void
139 vl_api_memclnt_create_t_handler (vl_api_memclnt_create_t * mp)
140 {
141   vl_api_registration_t **regpp;
142   vl_api_registration_t *regp;
143   vl_api_memclnt_create_reply_t *rp;
144   svm_queue_t *q;
145   int rv = 0;
146   void *oldheap;
147   api_main_t *am = vlibapi_get_main ();
148   u8 *msg_table;
149
150   /*
151    * This is tortured. Maintain a vlib-address-space private
152    * pool of client registrations. We use the shared-memory virtual
153    * address of client structure as a handle, to allow direct
154    * manipulation of context quota vbls from the client library.
155    *
156    * This scheme causes trouble w/ API message trace replay, since
157    * some random VA from clib_mem_alloc() certainly won't
158    * occur in the Linux sim. The (very) few places
159    * that care need to use the pool index.
160    *
161    * Putting the registration object(s) into a pool in shared memory and
162    * using the pool index as a handle seems like a great idea.
163    * Unfortunately, each and every reference to that pool would need
164    * to be protected by a mutex:
165    *
166    *     Client                      VLIB
167    *     ------                      ----
168    *     convert pool index to
169    *     pointer.
170    *     <deschedule>
171    *                                 expand pool
172    *                                 <deschedule>
173    *     kaboom!
174    */
175
176   pool_get (am->vl_clients, regpp);
177
178   oldheap = vl_msg_push_heap ();
179   *regpp = clib_mem_alloc (sizeof (vl_api_registration_t));
180
181   regp = *regpp;
182   clib_memset (regp, 0, sizeof (*regp));
183   regp->registration_type = REGISTRATION_TYPE_SHMEM;
184   regp->vl_api_registration_pool_index = regpp - am->vl_clients;
185   regp->vlib_rp = am->vlib_rp;
186   regp->shmem_hdr = am->shmem_hdr;
187   regp->clib_file_index = am->shmem_hdr->clib_file_index;
188
189   q = regp->vl_input_queue = (svm_queue_t *) (uword) mp->input_queue;
190   VL_MSG_API_SVM_QUEUE_UNPOISON (q);
191
192   regp->name = format (0, "%s", mp->name);
193   vec_add1 (regp->name, 0);
194   regp->keepalive = true;
195
196   if (am->serialized_message_table_in_shmem == 0)
197     am->serialized_message_table_in_shmem =
198       vl_api_serialize_message_table (am, 0);
199
200   if (am->vlib_rp != am->vlib_primary_rp)
201     msg_table = vl_api_serialize_message_table (am, 0);
202   else
203     msg_table = am->serialized_message_table_in_shmem;
204
205   vl_msg_pop_heap (oldheap);
206
207   rp = vl_msg_api_alloc (sizeof (*rp));
208   rp->_vl_msg_id = ntohs (VL_API_MEMCLNT_CREATE_REPLY);
209   rp->handle = (uword) regp;
210   rp->index = vl_msg_api_handle_from_index_and_epoch
211     (regp->vl_api_registration_pool_index,
212      am->shmem_hdr->application_restarts);
213   rp->context = mp->context;
214   rp->response = ntohl (rv);
215   rp->message_table = pointer_to_uword (msg_table);
216
217   vl_msg_api_send_shmem (q, (u8 *) & rp);
218 }
219
220 void
221 vl_api_memclnt_create_v2_t_handler (vl_api_memclnt_create_v2_t *mp)
222 {
223   vl_api_registration_t **regpp;
224   vl_api_registration_t *regp;
225   vl_api_memclnt_create_v2_reply_t *rp;
226   svm_queue_t *q;
227   int rv = 0;
228   void *oldheap;
229   api_main_t *am = vlibapi_get_main ();
230   u8 *msg_table;
231
232   /*
233    * This is tortured. Maintain a vlib-address-space private
234    * pool of client registrations. We use the shared-memory virtual
235    * address of client structure as a handle, to allow direct
236    * manipulation of context quota vbls from the client library.
237    *
238    * This scheme causes trouble w/ API message trace replay, since
239    * some random VA from clib_mem_alloc() certainly won't
240    * occur in the Linux sim. The (very) few places
241    * that care need to use the pool index.
242    *
243    * Putting the registration object(s) into a pool in shared memory and
244    * using the pool index as a handle seems like a great idea.
245    * Unfortunately, each and every reference to that pool would need
246    * to be protected by a mutex:
247    *
248    *     Client                      VLIB
249    *     ------                      ----
250    *     convert pool index to
251    *     pointer.
252    *     <deschedule>
253    *                                 expand pool
254    *                                 <deschedule>
255    *     kaboom!
256    */
257
258   pool_get (am->vl_clients, regpp);
259
260   oldheap = vl_msg_push_heap ();
261   *regpp = clib_mem_alloc (sizeof (vl_api_registration_t));
262
263   regp = *regpp;
264   clib_memset (regp, 0, sizeof (*regp));
265   regp->registration_type = REGISTRATION_TYPE_SHMEM;
266   regp->vl_api_registration_pool_index = regpp - am->vl_clients;
267   regp->vlib_rp = am->vlib_rp;
268   regp->shmem_hdr = am->shmem_hdr;
269   regp->clib_file_index = am->shmem_hdr->clib_file_index;
270
271   q = regp->vl_input_queue = (svm_queue_t *) (uword) mp->input_queue;
272   VL_MSG_API_SVM_QUEUE_UNPOISON (q);
273
274   regp->name = format (0, "%s", mp->name);
275   vec_add1 (regp->name, 0);
276   regp->keepalive = mp->keepalive;
277
278   if (am->serialized_message_table_in_shmem == 0)
279     am->serialized_message_table_in_shmem =
280       vl_api_serialize_message_table (am, 0);
281
282   if (am->vlib_rp != am->vlib_primary_rp)
283     msg_table = vl_api_serialize_message_table (am, 0);
284   else
285     msg_table = am->serialized_message_table_in_shmem;
286
287   vl_msg_pop_heap (oldheap);
288
289   rp = vl_msg_api_alloc (sizeof (*rp));
290   rp->_vl_msg_id = ntohs (VL_API_MEMCLNT_CREATE_V2_REPLY);
291   rp->handle = (uword) regp;
292   rp->index = vl_msg_api_handle_from_index_and_epoch (
293     regp->vl_api_registration_pool_index, am->shmem_hdr->application_restarts);
294   rp->context = mp->context;
295   rp->response = ntohl (rv);
296   rp->message_table = pointer_to_uword (msg_table);
297
298   vl_msg_api_send_shmem (q, (u8 *) &rp);
299 }
300
301 void
302 vl_api_call_reaper_functions (u32 client_index)
303 {
304   clib_error_t *error = 0;
305   _vl_msg_api_function_list_elt_t *i;
306
307   i = vlibapi_get_main ()->reaper_function_registrations;
308   while (i)
309     {
310       error = i->f (client_index);
311       if (error)
312         clib_error_report (error);
313       i = i->next_init_function;
314     }
315 }
316
317 /*
318  * vl_api_memclnt_delete_t_handler
319  */
320 void
321 vl_api_memclnt_delete_t_handler (vl_api_memclnt_delete_t * mp)
322 {
323   vl_api_registration_t **regpp;
324   vl_api_registration_t *regp;
325   vl_api_memclnt_delete_reply_t *rp;
326   void *oldheap;
327   api_main_t *am = vlibapi_get_main ();
328   u32 handle, client_index, epoch;
329
330   handle = mp->index;
331
332   vl_api_call_reaper_functions (handle);
333
334   epoch = vl_msg_api_handle_get_epoch (handle);
335   client_index = vl_msg_api_handle_get_index (handle);
336
337   if (epoch != (am->shmem_hdr->application_restarts & VL_API_EPOCH_MASK))
338     {
339       clib_warning
340         ("Stale clnt delete index %d old epoch %d cur epoch %d",
341          client_index, epoch,
342          (am->shmem_hdr->application_restarts & VL_API_EPOCH_MASK));
343       return;
344     }
345
346   regpp = pool_elt_at_index (am->vl_clients, client_index);
347
348   if (!pool_is_free (am->vl_clients, regpp))
349     {
350       int i;
351       regp = *regpp;
352       int private_registration = 0;
353
354       /* Send reply unless client asked us to do the cleanup */
355       if (!mp->do_cleanup)
356         {
357           /*
358            * Note: the API message handling path will set am->vlib_rp
359            * as appropriate for pairwise / private memory segments
360            */
361           rp = vl_msg_api_alloc (sizeof (*rp));
362           rp->_vl_msg_id = ntohs (VL_API_MEMCLNT_DELETE_REPLY);
363           rp->handle = mp->handle;
364           rp->response = 1;
365
366           vl_msg_api_send_shmem (regp->vl_input_queue, (u8 *) & rp);
367           if (client_index != regp->vl_api_registration_pool_index)
368             {
369               clib_warning ("mismatch client_index %d pool_index %d",
370                             client_index,
371                             regp->vl_api_registration_pool_index);
372               vl_msg_api_free (rp);
373               return;
374             }
375         }
376
377       /* No dangling references, please */
378       *regpp = 0;
379
380       /* For horizontal scaling, add a hash table... */
381       for (i = 0; i < vec_len (am->vlib_private_rps); i++)
382         {
383           /* Is this a pairwise / private API segment? */
384           if (am->vlib_private_rps[i] == am->vlib_rp)
385             {
386               /* Note: account for the memfd header page */
387               uword virtual_base = am->vlib_rp->virtual_base - MMAP_PAGESIZE;
388               uword virtual_size = am->vlib_rp->virtual_size + MMAP_PAGESIZE;
389
390               /*
391                * Kill the registration pool element before we make
392                * the index vanish forever
393                */
394               pool_put_index (am->vl_clients,
395                               regp->vl_api_registration_pool_index);
396
397               vec_delete (am->vlib_private_rps, 1, i);
398               /* Kill it, accounting for the memfd header page */
399               if (munmap ((void *) virtual_base, virtual_size) < 0)
400                 clib_unix_warning ("munmap");
401               /* Reset the queue-length-address cache */
402               vec_reset_length (vl_api_queue_cursizes);
403               private_registration = 1;
404               break;
405             }
406         }
407
408       if (private_registration == 0)
409         {
410           pool_put_index (am->vl_clients,
411                           regp->vl_api_registration_pool_index);
412           oldheap = vl_msg_push_heap ();
413           if (mp->do_cleanup)
414             svm_queue_free (regp->vl_input_queue);
415           vec_free (regp->name);
416           /* Poison the old registration */
417           clib_memset (regp, 0xF1, sizeof (*regp));
418           clib_mem_free (regp);
419           vl_msg_pop_heap (oldheap);
420           /*
421            * These messages must be freed manually, since they're set up
422            * as "bounce" messages. In the private_registration == 1 case,
423            * we kill the shared-memory segment which contains the message
424            * with munmap.
425            */
426           vl_msg_api_free (mp);
427         }
428     }
429   else
430     {
431       clib_warning ("unknown client ID %d", mp->index);
432     }
433 }
434
435 /**
436  * client answered a ping, stave off the grim reaper...
437  */
438 void
439   vl_api_memclnt_keepalive_reply_t_handler
440   (vl_api_memclnt_keepalive_reply_t * mp)
441 {
442   vl_api_registration_t *regp;
443   vlib_main_t *vm = vlib_get_main ();
444
445   regp = vl_api_client_index_to_registration (mp->context);
446   if (regp)
447     {
448       regp->last_heard = vlib_time_now (vm);
449       regp->unanswered_pings = 0;
450     }
451   else
452     clib_warning ("BUG: anonymous memclnt_keepalive_reply");
453 }
454
455 /**
456  * We can send ourselves these messages if someone uses the
457  * builtin binary api test tool...
458  */
459 static void
460 vl_api_memclnt_keepalive_t_handler (vl_api_memclnt_keepalive_t * mp)
461 {
462   vl_api_memclnt_keepalive_reply_t *rmp;
463   api_main_t *am;
464   vl_shmem_hdr_t *shmem_hdr;
465
466   am = vlibapi_get_main ();
467   shmem_hdr = am->shmem_hdr;
468
469   rmp = vl_msg_api_alloc_as_if_client (sizeof (*rmp));
470   clib_memset (rmp, 0, sizeof (*rmp));
471   rmp->_vl_msg_id = ntohs (VL_API_MEMCLNT_KEEPALIVE_REPLY);
472   rmp->context = mp->context;
473   vl_msg_api_send_shmem (shmem_hdr->vl_input_queue, (u8 *) & rmp);
474 }
475
476 /*
477  * To avoid filling the API trace buffer with boring messages,
478  * don't trace memclnt_keepalive[_reply] msgs
479  */
480
481 #define foreach_vlib_api_msg                                                  \
482   _ (MEMCLNT_CREATE, memclnt_create, 0)                                       \
483   _ (MEMCLNT_CREATE_V2, memclnt_create_v2, 0)                                 \
484   _ (MEMCLNT_DELETE, memclnt_delete, 0)                                       \
485   _ (MEMCLNT_KEEPALIVE, memclnt_keepalive, 0)                                 \
486   _ (MEMCLNT_KEEPALIVE_REPLY, memclnt_keepalive_reply, 0)
487
488 /*
489  * memory_api_init
490  */
491 int
492 vl_mem_api_init (const char *region_name)
493 {
494   int rv;
495   api_main_t *am = vlibapi_get_main ();
496   vl_msg_api_msg_config_t cfg;
497   vl_msg_api_msg_config_t *c = &cfg;
498   vl_shmem_hdr_t *shm;
499   vlib_main_t *vm = vlib_get_main ();
500
501   clib_memset (c, 0, sizeof (*c));
502
503   if ((rv = vl_map_shmem (region_name, 1 /* is_vlib */ )) < 0)
504     return rv;
505
506 #define _(N, n, t)                                                            \
507   do                                                                          \
508     {                                                                         \
509       c->id = VL_API_##N;                                                     \
510       c->name = #n;                                                           \
511       c->handler = vl_api_##n##_t_handler;                                    \
512       c->endian = vl_api_##n##_t_endian;                                      \
513       c->format_fn = vl_api_##n##_t_format;                                   \
514       c->size = sizeof (vl_api_##n##_t);                                      \
515       c->traced = t;         /* trace, so these msgs print */                 \
516       c->replay = 0;         /* don't replay client create/delete msgs */     \
517       c->message_bounce = 0; /* don't bounce this message */                  \
518       vl_msg_api_config (c);                                                  \
519     }                                                                         \
520   while (0);
521
522   foreach_vlib_api_msg;
523 #undef _
524
525 #define vl_msg_name_crc_list
526 #include <vlibmemory/memclnt.api.h>
527 #undef vl_msg_name_crc_list
528
529 #define _(id, n, crc) vl_msg_api_add_msg_name_crc (am, #n "_" #crc, id);
530   foreach_vl_msg_name_crc_memclnt;
531 #undef _
532
533   /*
534    * special-case freeing of memclnt_delete messages, so we can
535    * simply munmap pairwise / private API segments...
536    */
537   am->msg_data[VL_API_MEMCLNT_DELETE].bounce = 1;
538   vl_api_set_msg_thread_safe (am, VL_API_MEMCLNT_KEEPALIVE_REPLY, 1);
539   vl_api_set_msg_thread_safe (am, VL_API_MEMCLNT_KEEPALIVE, 1);
540
541   vlib_set_queue_signal_callback (vm, memclnt_queue_callback);
542
543   shm = am->shmem_hdr;
544   ASSERT (shm && shm->vl_input_queue);
545
546   /* Make a note so we can always find the primary region easily */
547   am->vlib_primary_rp = am->vlib_rp;
548
549   return 0;
550 }
551
552 clib_error_t *
553 map_api_segment_init (vlib_main_t * vm)
554 {
555   api_main_t *am = vlibapi_get_main ();
556   int rv;
557
558   if ((rv = vl_mem_api_init (am->region_name)) < 0)
559     {
560       return clib_error_return (0, "vl_mem_api_init (%s) failed",
561                                 am->region_name);
562     }
563   return 0;
564 }
565
566 static void
567 send_memclnt_keepalive (vl_api_registration_t * regp, f64 now)
568 {
569   vl_api_memclnt_keepalive_t *mp;
570   svm_queue_t *q;
571   api_main_t *am = vlibapi_get_main ();
572
573   q = regp->vl_input_queue;
574
575   /*
576    * If the queue head is moving, assume that the client is processing
577    * messages and skip the ping. This heuristic may fail if the queue
578    * is in the same position as last time, net of wrapping; in which
579    * case, the client will receive a keepalive.
580    */
581   if (regp->last_queue_head != q->head)
582     {
583       regp->last_heard = now;
584       regp->unanswered_pings = 0;
585       regp->last_queue_head = q->head;
586       return;
587     }
588
589   /*
590    * push/pop shared memory segment, so this routine
591    * will work with "normal" as well as "private segment"
592    * memory clients..
593    */
594
595   mp = vl_mem_api_alloc_as_if_client_w_reg (regp, sizeof (*mp));
596   clib_memset (mp, 0, sizeof (*mp));
597   mp->_vl_msg_id = clib_host_to_net_u16 (VL_API_MEMCLNT_KEEPALIVE);
598   mp->context = mp->client_index =
599     vl_msg_api_handle_from_index_and_epoch
600     (regp->vl_api_registration_pool_index,
601      am->shmem_hdr->application_restarts);
602
603   regp->unanswered_pings++;
604
605   /* Failure-to-send due to a stuffed queue is absolutely expected */
606   if (svm_queue_add (q, (u8 *) & mp, 1 /* nowait */ ))
607     vl_msg_api_free_w_region (regp->vlib_rp, mp);
608 }
609
610 static void
611 vl_mem_send_client_keepalive_w_reg (api_main_t * am, f64 now,
612                                     vl_api_registration_t ** regpp,
613                                     u32 ** dead_indices,
614                                     u32 ** confused_indices)
615 {
616   vl_api_registration_t *regp = *regpp;
617   if (regp)
618     {
619       /* If we haven't heard from this client recently... */
620       if (regp->last_heard < (now - 10.0))
621         {
622           if (regp->unanswered_pings == 2)
623             {
624               svm_queue_t *q;
625               q = regp->vl_input_queue;
626               if (kill (q->consumer_pid, 0) >= 0)
627                 {
628                   clib_warning ("REAPER: lazy binary API client '%s'",
629                                 regp->name);
630                   regp->unanswered_pings = 0;
631                   regp->last_heard = now;
632                 }
633               else
634                 {
635                   clib_warning ("REAPER: binary API client '%s' died",
636                                 regp->name);
637                   vec_add1 (*dead_indices, regpp - am->vl_clients);
638                 }
639             }
640           else
641             send_memclnt_keepalive (regp, now);
642         }
643       else
644         regp->unanswered_pings = 0;
645     }
646   else
647     {
648       clib_warning ("NULL client registration index %d",
649                     regpp - am->vl_clients);
650       vec_add1 (*confused_indices, regpp - am->vl_clients);
651     }
652 }
653
654 void
655 vl_mem_api_dead_client_scan (api_main_t * am, vl_shmem_hdr_t * shm, f64 now)
656 {
657   vl_api_registration_t **regpp;
658   static u32 *dead_indices;
659   static u32 *confused_indices;
660
661   vec_reset_length (dead_indices);
662   vec_reset_length (confused_indices);
663
664   pool_foreach (regpp, am->vl_clients)  {
665       if (!(*regpp)->keepalive)
666         continue;
667       vl_mem_send_client_keepalive_w_reg (am, now, regpp, &dead_indices,
668                                           &confused_indices);
669   }
670
671   /* This should "never happen," but if it does, fix it... */
672   if (PREDICT_FALSE (vec_len (confused_indices) > 0))
673     {
674       int i;
675       for (i = 0; i < vec_len (confused_indices); i++)
676         {
677           pool_put_index (am->vl_clients, confused_indices[i]);
678         }
679     }
680
681   if (PREDICT_FALSE (vec_len (dead_indices) > 0))
682     {
683       int i;
684       void *oldheap;
685
686       /* Allow the application to clean up its registrations */
687       for (i = 0; i < vec_len (dead_indices); i++)
688         {
689           regpp = pool_elt_at_index (am->vl_clients, dead_indices[i]);
690           if (regpp)
691             {
692               u32 handle;
693
694               handle = vl_msg_api_handle_from_index_and_epoch
695                 (dead_indices[i], shm->application_restarts);
696               vl_api_call_reaper_functions (handle);
697             }
698         }
699
700       oldheap = vl_msg_push_heap ();
701
702       for (i = 0; i < vec_len (dead_indices); i++)
703         {
704           regpp = pool_elt_at_index (am->vl_clients, dead_indices[i]);
705           if (regpp)
706             {
707               /* Is this a pairwise SVM segment? */
708               if ((*regpp)->vlib_rp != am->vlib_rp)
709                 {
710                   int i;
711                   svm_region_t *dead_rp = (*regpp)->vlib_rp;
712                   /* Note: account for the memfd header page */
713                   uword virtual_base = dead_rp->virtual_base - MMAP_PAGESIZE;
714                   uword virtual_size = dead_rp->virtual_size + MMAP_PAGESIZE;
715
716                   /* For horizontal scaling, add a hash table... */
717                   for (i = 0; i < vec_len (am->vlib_private_rps); i++)
718                     if (am->vlib_private_rps[i] == dead_rp)
719                       {
720                         vec_delete (am->vlib_private_rps, 1, i);
721                         goto found;
722                       }
723                   svm_pop_heap (oldheap);
724                   clib_warning ("private rp %llx AWOL", dead_rp);
725                   oldheap = svm_push_data_heap (am->vlib_rp);
726
727                 found:
728                   /* Kill it, accounting for the memfd header page */
729                   svm_pop_heap (oldheap);
730                   if (munmap ((void *) virtual_base, virtual_size) < 0)
731                     clib_unix_warning ("munmap");
732                   /* Reset the queue-length-address cache */
733                   vec_reset_length (vl_api_queue_cursizes);
734                   oldheap = svm_push_data_heap (am->vlib_rp);
735                 }
736               else
737                 {
738                   /* Poison the old registration */
739                   clib_memset (*regpp, 0xF3, sizeof (**regpp));
740                   clib_mem_free (*regpp);
741                 }
742               /* no dangling references, please */
743               *regpp = 0;
744             }
745           else
746             {
747               svm_pop_heap (oldheap);
748               clib_warning ("Duplicate free, client index %d",
749                             regpp - am->vl_clients);
750               oldheap = svm_push_data_heap (am->vlib_rp);
751             }
752         }
753
754       svm_client_scan_this_region_nolock (am->vlib_rp);
755
756       vl_msg_pop_heap (oldheap);
757       for (i = 0; i < vec_len (dead_indices); i++)
758         pool_put_index (am->vl_clients, dead_indices[i]);
759     }
760 }
761
762 void (*vl_mem_api_fuzz_hook) (u16, void *);
763
764 /* This is only to be called from a vlib/vnet app */
765 static void
766 vl_mem_api_handler_with_vm_node (api_main_t *am, svm_region_t *vlib_rp,
767                                  void *the_msg, vlib_main_t *vm,
768                                  vlib_node_runtime_t *node, u8 is_private)
769 {
770   u16 id = clib_net_to_host_u16 (*((u16 *) the_msg));
771   vl_api_msg_data_t *m = vl_api_get_msg_data (am, id);
772   u8 *(*handler) (void *, void *, void *);
773   svm_region_t *old_vlib_rp;
774   void *save_shmem_hdr;
775   int is_mp_safe = 1;
776
777   if (PREDICT_FALSE (am->elog_trace_api_messages))
778     {
779       ELOG_TYPE_DECLARE (e) = {
780         .format = "api-msg: %s",
781         .format_args = "T4",
782       };
783       struct
784       {
785         u32 c;
786       } * ed;
787       ed = ELOG_DATA (am->elog_main, e);
788       if (m && m->name)
789         ed->c = elog_string (am->elog_main, (char *) m->name);
790       else
791         ed->c = elog_string (am->elog_main, "BOGUS");
792     }
793
794   if (m && m->handler)
795     {
796       handler = (void *) m->handler;
797
798       if (PREDICT_FALSE (am->rx_trace && am->rx_trace->enabled))
799         vl_msg_api_trace (am, am->rx_trace, the_msg);
800
801       if (PREDICT_FALSE (am->msg_print_flag))
802         {
803           fformat (stdout, "[%d]: %s\n", id, m->name);
804           fformat (stdout, "%U", format_vl_api_msg_text, am, id, the_msg);
805         }
806       is_mp_safe = am->msg_data[id].is_mp_safe;
807
808       if (!is_mp_safe)
809         {
810           vl_msg_api_barrier_trace_context (am->msg_data[id].name);
811           vl_msg_api_barrier_sync ();
812         }
813       if (is_private)
814         {
815           old_vlib_rp = am->vlib_rp;
816           save_shmem_hdr = am->shmem_hdr;
817           am->vlib_rp = vlib_rp;
818           am->shmem_hdr = (void *) vlib_rp->user_ctx;
819         }
820
821       if (PREDICT_FALSE (vl_mem_api_fuzz_hook != 0))
822         (*vl_mem_api_fuzz_hook) (id, the_msg);
823
824       if (m->is_autoendian)
825         {
826           void (*endian_fp) (void *, bool);
827           endian_fp = am->msg_data[id].endian_handler;
828           (*endian_fp) (the_msg, 0);
829         }
830       if (PREDICT_FALSE (vec_len (am->perf_counter_cbs) != 0))
831         clib_call_callbacks (am->perf_counter_cbs, am, id, 0 /* before */);
832
833       (*handler) (the_msg, vm, node);
834
835       if (PREDICT_FALSE (vec_len (am->perf_counter_cbs) != 0))
836         clib_call_callbacks (am->perf_counter_cbs, am, id, 1 /* after */);
837       if (is_private)
838         {
839           am->vlib_rp = old_vlib_rp;
840           am->shmem_hdr = save_shmem_hdr;
841         }
842       if (!is_mp_safe)
843         vl_msg_api_barrier_release ();
844     }
845   else
846     {
847       clib_warning ("no handler for msg id %d", id);
848     }
849
850   /*
851    * Special-case, so we can e.g. bounce messages off the vnet
852    * main thread without copying them...
853    */
854   if (!m || !m->bounce)
855     {
856       if (is_private)
857         {
858           old_vlib_rp = am->vlib_rp;
859           save_shmem_hdr = am->shmem_hdr;
860           am->vlib_rp = vlib_rp;
861           am->shmem_hdr = (void *) vlib_rp->user_ctx;
862         }
863       vl_msg_api_free (the_msg);
864       if (is_private)
865         {
866           am->vlib_rp = old_vlib_rp;
867           am->shmem_hdr = save_shmem_hdr;
868         }
869     }
870
871   if (PREDICT_FALSE (am->elog_trace_api_messages))
872     {
873       ELOG_TYPE_DECLARE (e) = { .format = "api-msg-done(%s): %s",
874                                 .format_args = "t4T4",
875                                 .n_enum_strings = 2,
876                                 .enum_strings = {
877                                   "barrier",
878                                   "mp-safe",
879                                 } };
880
881       struct
882       {
883         u32 barrier;
884         u32 c;
885       } * ed;
886       ed = ELOG_DATA (am->elog_main, e);
887       if (m && m->name)
888         ed->c = elog_string (am->elog_main, (char *) m->name);
889       else
890         ed->c = elog_string (am->elog_main, "BOGUS");
891       ed->barrier = is_mp_safe;
892     }
893 }
894
895 static inline int
896 void_mem_api_handle_msg_i (api_main_t * am, svm_region_t * vlib_rp,
897                            vlib_main_t * vm, vlib_node_runtime_t * node,
898                            u8 is_private)
899 {
900   svm_queue_t *q;
901   uword mp;
902
903   q = ((vl_shmem_hdr_t *) (void *) vlib_rp->user_ctx)->vl_input_queue;
904
905   if (!svm_queue_sub2 (q, (u8 *) & mp))
906     {
907       VL_MSG_API_UNPOISON ((void *) mp);
908       vl_mem_api_handler_with_vm_node (am, vlib_rp, (void *) mp, vm, node,
909                                        is_private);
910       return 0;
911     }
912   return -1;
913 }
914
915 int
916 vl_mem_api_handle_msg_main (vlib_main_t * vm, vlib_node_runtime_t * node)
917 {
918   api_main_t *am = vlibapi_get_main ();
919   return void_mem_api_handle_msg_i (am, am->vlib_rp, vm, node,
920                                     0 /* is_private */ );
921 }
922
923 int
924 vl_mem_api_handle_rpc (vlib_main_t * vm, vlib_node_runtime_t * node)
925 {
926   api_main_t *am = vlibapi_get_main ();
927   int i;
928   uword *tmp, mp;
929
930   /*
931    * Swap pending and processing vectors, then process the RPCs
932    * Avoid deadlock conditions by construction.
933    */
934   clib_spinlock_lock_if_init (&vm->pending_rpc_lock);
935   tmp = vm->processing_rpc_requests;
936   vec_reset_length (tmp);
937   vm->processing_rpc_requests = vm->pending_rpc_requests;
938   vm->pending_rpc_requests = tmp;
939   clib_spinlock_unlock_if_init (&vm->pending_rpc_lock);
940
941   /*
942    * RPCs are used to reflect function calls to thread 0
943    * when the underlying code is not thread-safe.
944    *
945    * Grabbing the thread barrier across a set of RPCs
946    * greatly increases efficiency, and avoids
947    * running afoul of the barrier sync holddown timer.
948    * The barrier sync code supports recursive locking.
949    *
950    * We really need to rewrite RPC-based code...
951    */
952   if (PREDICT_TRUE (vec_len (vm->processing_rpc_requests)))
953     {
954       vl_msg_api_barrier_sync ();
955       for (i = 0; i < vec_len (vm->processing_rpc_requests); i++)
956         {
957           mp = vm->processing_rpc_requests[i];
958           vl_mem_api_handler_with_vm_node (am, am->vlib_rp, (void *) mp, vm,
959                                            node, 0 /* is_private */);
960         }
961       vl_msg_api_barrier_release ();
962     }
963
964   return 0;
965 }
966
967 int
968 vl_mem_api_handle_msg_private (vlib_main_t * vm, vlib_node_runtime_t * node,
969                                u32 reg_index)
970 {
971   api_main_t *am = vlibapi_get_main ();
972   return void_mem_api_handle_msg_i (am, am->vlib_private_rps[reg_index], vm,
973                                     node, 1 /* is_private */ );
974 }
975
976 vl_api_registration_t *
977 vl_mem_api_client_index_to_registration (u32 handle)
978 {
979   vl_api_registration_t **regpp;
980   vl_api_registration_t *regp;
981   api_main_t *am = vlibapi_get_main ();
982   vl_shmem_hdr_t *shmem_hdr;
983   u32 index;
984
985   index = vl_msg_api_handle_get_index (handle);
986   regpp = am->vl_clients + index;
987
988   if (pool_is_free (am->vl_clients, regpp))
989     {
990       vl_msg_api_increment_missing_client_counter ();
991       return 0;
992     }
993   regp = *regpp;
994
995   shmem_hdr = (vl_shmem_hdr_t *) regp->shmem_hdr;
996   if (!vl_msg_api_handle_is_valid (handle, shmem_hdr->application_restarts))
997     {
998       vl_msg_api_increment_missing_client_counter ();
999       return 0;
1000     }
1001
1002   return (regp);
1003 }
1004
1005 svm_queue_t *
1006 vl_api_client_index_to_input_queue (u32 index)
1007 {
1008   vl_api_registration_t *regp;
1009   api_main_t *am = vlibapi_get_main ();
1010
1011   /* Special case: vlib trying to send itself a message */
1012   if (index == (u32) ~ 0)
1013     return (am->shmem_hdr->vl_input_queue);
1014
1015   regp = vl_mem_api_client_index_to_registration (index);
1016   if (!regp)
1017     return 0;
1018   return (regp->vl_input_queue);
1019 }
1020
1021 static clib_error_t *
1022 setup_memclnt_exit (vlib_main_t * vm)
1023 {
1024   atexit (vl_unmap_shmem);
1025   return 0;
1026 }
1027
1028 VLIB_INIT_FUNCTION (setup_memclnt_exit);
1029
1030 u8 *
1031 format_api_message_rings (u8 * s, va_list * args)
1032 {
1033   api_main_t *am = va_arg (*args, api_main_t *);
1034   vl_shmem_hdr_t *shmem_hdr = va_arg (*args, vl_shmem_hdr_t *);
1035   int main_segment = va_arg (*args, int);
1036   ring_alloc_t *ap;
1037   int i;
1038
1039   if (shmem_hdr == 0)
1040     return format (s, "%8s %8s %8s %8s %8s\n",
1041                    "Owner", "Size", "Nitems", "Hits", "Misses");
1042
1043   ap = shmem_hdr->vl_rings;
1044
1045   for (i = 0; i < vec_len (shmem_hdr->vl_rings); i++)
1046     {
1047       s = format (s, "%8s %8d %8d %8d %8d\n",
1048                   "vlib", ap->size, ap->nitems, ap->hits, ap->misses);
1049       ap++;
1050     }
1051
1052   ap = shmem_hdr->client_rings;
1053
1054   for (i = 0; i < vec_len (shmem_hdr->client_rings); i++)
1055     {
1056       s = format (s, "%8s %8d %8d %8d %8d\n",
1057                   "clnt", ap->size, ap->nitems, ap->hits, ap->misses);
1058       ap++;
1059     }
1060
1061   if (main_segment)
1062     {
1063       s = format (s, "%d ring miss fallback allocations\n", am->ring_misses);
1064       s = format
1065         (s,
1066          "%d application restarts, %d reclaimed msgs, %d garbage collects\n",
1067          shmem_hdr->application_restarts, shmem_hdr->restart_reclaims,
1068          shmem_hdr->garbage_collects);
1069     }
1070   return s;
1071 }
1072
1073 static clib_error_t *
1074 vl_api_ring_command (vlib_main_t * vm,
1075                      unformat_input_t * input, vlib_cli_command_t * cli_cmd)
1076 {
1077   int i;
1078   vl_shmem_hdr_t *shmem_hdr;
1079   api_main_t *am = vlibapi_get_main ();
1080
1081   /* First, dump the primary region rings.. */
1082
1083   if (am->vlib_primary_rp == 0 || am->vlib_primary_rp->user_ctx == 0)
1084     {
1085       vlib_cli_output (vm, "Shared memory segment not initialized...\n");
1086       return 0;
1087     }
1088
1089   shmem_hdr = (void *) am->vlib_primary_rp->user_ctx;
1090
1091   vlib_cli_output (vm, "Main API segment rings:");
1092
1093   vlib_cli_output (vm, "%U", format_api_message_rings, am,
1094                    0 /* print header */ , 0 /* notused */ );
1095
1096   vlib_cli_output (vm, "%U", format_api_message_rings, am,
1097                    shmem_hdr, 1 /* main segment */ );
1098
1099   for (i = 0; i < vec_len (am->vlib_private_rps); i++)
1100     {
1101       svm_region_t *vlib_rp = am->vlib_private_rps[i];
1102       shmem_hdr = (void *) vlib_rp->user_ctx;
1103       vl_api_registration_t **regpp;
1104       vl_api_registration_t *regp = 0;
1105
1106       /* For horizontal scaling, add a hash table... */
1107       pool_foreach (regpp, am->vl_clients)
1108        {
1109         regp = *regpp;
1110         if (regp && regp->vlib_rp == vlib_rp)
1111           {
1112             vlib_cli_output (vm, "%s segment rings:", regp->name);
1113             goto found;
1114           }
1115       }
1116       vlib_cli_output (vm, "regp %llx not found?", regp);
1117       continue;
1118     found:
1119       vlib_cli_output (vm, "%U", format_api_message_rings, am,
1120                        0 /* print header */ , 0 /* notused */ );
1121       vlib_cli_output (vm, "%U", format_api_message_rings, am,
1122                        shmem_hdr, 0 /* main segment */ );
1123     }
1124
1125   return 0;
1126 }
1127
1128 /*?
1129  * Display binary api message allocation ring statistics
1130 ?*/
1131 VLIB_CLI_COMMAND (cli_show_api_ring_command, static) =
1132 {
1133   .path = "show api ring-stats",
1134   .short_help = "Message ring statistics",
1135   .function = vl_api_ring_command,
1136 };
1137
1138 clib_error_t *
1139 vlibmemory_init (vlib_main_t * vm)
1140 {
1141   api_main_t *am = vlibapi_get_main ();
1142   svm_map_region_args_t _a, *a = &_a;
1143   u8 *remove_path1, *remove_path2;
1144   void vlibsocket_reference (void);
1145
1146   vlibsocket_reference ();
1147
1148   /*
1149    * By popular request / to avoid support fires, remove any old api segment
1150    * files Right Here.
1151    */
1152   if (am->root_path == 0)
1153     {
1154       remove_path1 = format (0, "/dev/shm/global_vm%c", 0);
1155       remove_path2 = format (0, "/dev/shm/vpe-api%c", 0);
1156     }
1157   else
1158     {
1159       remove_path1 = format (0, "/dev/shm/%s-global_vm%c", am->root_path, 0);
1160       remove_path2 = format (0, "/dev/shm/%s-vpe-api%c", am->root_path, 0);
1161     }
1162
1163   (void) unlink ((char *) remove_path1);
1164   (void) unlink ((char *) remove_path2);
1165
1166   vec_free (remove_path1);
1167   vec_free (remove_path2);
1168
1169   clib_memset (a, 0, sizeof (*a));
1170   a->root_path = am->root_path;
1171   a->name = SVM_GLOBAL_REGION_NAME;
1172   a->baseva = (am->global_baseva != 0) ?
1173     am->global_baseva : +svm_get_global_region_base_va ();
1174   a->size = (am->global_size != 0) ? am->global_size : SVM_GLOBAL_REGION_SIZE;
1175   a->flags = SVM_FLAGS_NODATA;
1176   a->uid = am->api_uid;
1177   a->gid = am->api_gid;
1178   a->pvt_heap_size =
1179     (am->global_pvt_heap_size !=
1180      0) ? am->global_pvt_heap_size : SVM_PVT_MHEAP_SIZE;
1181
1182   svm_region_init_args (a);
1183
1184   return 0;
1185 }
1186
1187 void
1188 vl_set_memory_region_name (const char *name)
1189 {
1190   api_main_t *am = vlibapi_get_main ();
1191   am->region_name = name;
1192 }
1193
1194 /*
1195  * fd.io coding-style-patch-verification: ON
1196  *
1197  * Local Variables:
1198  * eval: (c-set-style "gnu")
1199  * End:
1200  */