vls: multi-process and multi-threaded apps improvements
[vpp.git] / src / vcl / vcl_locked.c
1 /*
2  * Copyright (c) 2019 Cisco and/or its affiliates.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this
5  * You may obtain a copy of the License at:
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15
16 #include <vcl/vcl_locked.h>
17 #include <vcl/vcl_private.h>
18
19 typedef struct vcl_locked_session_
20 {
21   clib_spinlock_t lock;
22   u32 session_index;
23   u32 worker_index;
24   u32 vls_index;
25   u32 flags;
26   u32 *workers_subscribed;
27 } vcl_locked_session_t;
28
29 typedef struct vcl_main_
30 {
31   vcl_locked_session_t *vls_pool;
32   clib_rwlock_t vls_table_lock;
33   uword *session_index_to_vlsh_table;
34 } vls_main_t;
35
36 vls_main_t vls_main;
37 vls_main_t *vlsm = &vls_main;
38
39 static inline void
40 vls_table_rlock (void)
41 {
42   clib_rwlock_reader_lock (&vlsm->vls_table_lock);
43 }
44
45 static inline void
46 vls_table_runlock (void)
47 {
48   clib_rwlock_reader_unlock (&vlsm->vls_table_lock);
49 }
50
51 static inline void
52 vls_table_wlock (void)
53 {
54   clib_rwlock_writer_lock (&vlsm->vls_table_lock);
55 }
56
57 static inline void
58 vls_table_wunlock (void)
59 {
60   clib_rwlock_writer_unlock (&vlsm->vls_table_lock);
61 }
62
63 typedef enum
64 {
65   VLS_MT_OP_READ,
66   VLS_MT_OP_WRITE,
67   VLS_MT_OP_SPOOL,
68   VLS_MT_OP_XPOLL,
69 } vls_mt_ops_t;
70
71 typedef enum
72 {
73   VLS_MT_LOCK_MQ = 1 << 0,
74   VLS_MT_LOCK_SPOOL = 1 << 1
75 } vls_mt_lock_type_t;
76
77 static int vls_wrk_index = ~0;
78 static volatile int vls_mt_n_threads;
79 static pthread_mutex_t vls_mt_mq_mlock = PTHREAD_MUTEX_INITIALIZER;
80 static pthread_mutex_t vls_mt_spool_mlock = PTHREAD_MUTEX_INITIALIZER;
81
82 static void
83 vls_mt_add (void)
84 {
85   vls_mt_n_threads += 1;
86   vcl_set_worker_index (vls_wrk_index);
87 }
88
89 static inline void
90 vls_mt_mq_lock (void)
91 {
92   pthread_mutex_lock (&vls_mt_mq_mlock);
93 }
94
95 static inline void
96 vls_mt_mq_unlock (void)
97 {
98   pthread_mutex_unlock (&vls_mt_mq_mlock);
99 }
100
101 static inline void
102 vls_mt_spool_lock (void)
103 {
104   pthread_mutex_lock (&vls_mt_spool_mlock);
105 }
106
107 static inline void
108 vls_mt_create_unlock (void)
109 {
110   pthread_mutex_unlock (&vls_mt_spool_mlock);
111 }
112
113 static inline vcl_session_handle_t
114 vls_to_sh (vcl_locked_session_t * vls)
115 {
116   return vcl_session_handle_from_index (vls->session_index);
117 }
118
119 static inline vcl_session_handle_t
120 vls_to_sh_tu (vcl_locked_session_t * vls)
121 {
122   vcl_session_handle_t sh;
123   sh = vls_to_sh (vls);
124   vls_table_runlock ();
125   return sh;
126 }
127
128 static vls_handle_t
129 vls_alloc (vcl_session_handle_t sh)
130 {
131   vcl_locked_session_t *vls;
132
133   vls_table_wlock ();
134   pool_get (vlsm->vls_pool, vls);
135   vls->session_index = vppcom_session_index (sh);
136   vls->worker_index = vppcom_session_worker (sh);
137   vls->vls_index = vls - vlsm->vls_pool;
138   hash_set (vlsm->session_index_to_vlsh_table, vls->session_index,
139             vls->vls_index);
140   clib_spinlock_init (&vls->lock);
141   vls_table_wunlock ();
142   return vls->vls_index;
143 }
144
145 static vcl_locked_session_t *
146 vls_get (vls_handle_t vlsh)
147 {
148   if (pool_is_free_index (vlsm->vls_pool, vlsh))
149     return 0;
150   return pool_elt_at_index (vlsm->vls_pool, vlsh);
151 }
152
153 static void
154 vls_free (vcl_locked_session_t * vls)
155 {
156   ASSERT (vls != 0);
157   hash_unset (vlsm->session_index_to_vlsh_table, vls->session_index);
158   clib_spinlock_free (&vls->lock);
159   pool_put (vlsm->vls_pool, vls);
160 }
161
162 static vcl_locked_session_t *
163 vls_get_and_lock (vls_handle_t vlsh)
164 {
165   vcl_locked_session_t *vls;
166   if (pool_is_free_index (vlsm->vls_pool, vlsh))
167     return 0;
168   vls = pool_elt_at_index (vlsm->vls_pool, vlsh);
169   clib_spinlock_lock (&vls->lock);
170   return vls;
171 }
172
173 static vcl_locked_session_t *
174 vls_get_w_dlock (vls_handle_t vlsh)
175 {
176   vcl_locked_session_t *vls;
177   vls_table_rlock ();
178   vls = vls_get_and_lock (vlsh);
179   if (!vls)
180     vls_table_runlock ();
181   return vls;
182 }
183
184 static inline void
185 vls_unlock (vcl_locked_session_t * vls)
186 {
187   clib_spinlock_unlock (&vls->lock);
188 }
189
190 static inline void
191 vls_get_and_unlock (vls_handle_t vlsh)
192 {
193   vcl_locked_session_t *vls;
194   vls_table_rlock ();
195   vls = vls_get (vlsh);
196   vls_unlock (vls);
197   vls_table_runlock ();
198 }
199
200 static inline void
201 vls_dunlock (vcl_locked_session_t * vls)
202 {
203   vls_unlock (vls);
204   vls_table_runlock ();
205 }
206
207 u8
208 vls_is_shared (vcl_locked_session_t * vls)
209 {
210   return vec_len (vls->workers_subscribed);
211 }
212
213 u8
214 vls_is_shared_by_wrk (vcl_locked_session_t * vls, u32 wrk_index)
215 {
216   int i;
217   for (i = 0; i < vec_len (vls->workers_subscribed); i++)
218     if (vls->workers_subscribed[i] == wrk_index)
219       return 1;
220   return 0;
221 }
222
223 int
224 vls_unshare_session (vcl_locked_session_t * vls, vcl_worker_t * wrk)
225 {
226   vcl_session_t *s;
227   int i;
228
229   for (i = 0; i < vec_len (vls->workers_subscribed); i++)
230     {
231       if (vls->workers_subscribed[i] != wrk->wrk_index)
232         continue;
233
234       s = vcl_session_get (wrk, vls->session_index);
235       if (s->rx_fifo)
236         {
237           svm_fifo_del_subscriber (s->rx_fifo, wrk->vpp_wrk_index);
238           svm_fifo_del_subscriber (s->tx_fifo, wrk->vpp_wrk_index);
239         }
240       vec_del1 (vls->workers_subscribed, i);
241       vcl_session_cleanup (wrk, s, vcl_session_handle (s),
242                            0 /* do_disconnect */ );
243       return 0;
244     }
245
246   /* Return, if this is not the owning worker */
247   if (vls->worker_index != wrk->wrk_index)
248     return 0;
249
250   s = vcl_session_get (wrk, vls->session_index);
251
252   /* Check if we can change owner or close */
253   if (vec_len (vls->workers_subscribed))
254     {
255       vls->worker_index = vls->workers_subscribed[0];
256       vec_del1 (vls->workers_subscribed, 0);
257       vcl_send_session_worker_update (wrk, s, vls->worker_index);
258       if (vec_len (vls->workers_subscribed))
259         clib_warning ("more workers need to be updated");
260     }
261   else
262     {
263       vcl_session_cleanup (wrk, s, vcl_session_handle (s),
264                            1 /* do_disconnect */ );
265     }
266
267   return 0;
268 }
269
270 void
271 vls_share_vcl_session (vcl_worker_t * wrk, vcl_session_t * s)
272 {
273   vcl_locked_session_t *vls;
274
275   vls = vls_get_w_dlock (vls_session_index_to_vlsh (s->session_index));
276   if (!vls)
277     return;
278   vec_add1 (vls->workers_subscribed, wrk->wrk_index);
279   if (s->rx_fifo)
280     {
281       svm_fifo_add_subscriber (s->rx_fifo, wrk->vpp_wrk_index);
282       svm_fifo_add_subscriber (s->tx_fifo, wrk->vpp_wrk_index);
283     }
284   vls_dunlock (vls);
285 }
286
287 void
288 vls_worker_copy_on_fork (vcl_worker_t * parent_wrk)
289 {
290   vcl_worker_t *wrk = vcl_worker_get_current ();
291   vcl_session_t *s;
292
293   wrk->vpp_event_queues = vec_dup (parent_wrk->vpp_event_queues);
294   wrk->sessions = pool_dup (parent_wrk->sessions);
295   wrk->session_index_by_vpp_handles =
296     hash_dup (parent_wrk->session_index_by_vpp_handles);
297
298   /* *INDENT-OFF* */
299   pool_foreach (s, wrk->sessions, ({
300     vls_share_vcl_session (wrk, s);
301   }));
302   /* *INDENT-ON* */
303 }
304
305 static void
306 vls_mt_acq_locks (vcl_locked_session_t * vls, vls_mt_ops_t op, int *locks_acq)
307 {
308   vcl_worker_t *wrk = vcl_worker_get_current ();
309   vcl_session_t *s = 0;
310   int is_nonblk = 0;
311
312   if (vls)
313     {
314       s = vcl_session_get (wrk, vls->session_index);
315       if (PREDICT_FALSE (!s))
316         return;
317       is_nonblk = VCL_SESS_ATTR_TEST (s->attr, VCL_SESS_ATTR_NONBLOCK);
318     }
319
320   switch (op)
321     {
322     case VLS_MT_OP_READ:
323       if (!is_nonblk)
324         is_nonblk = vcl_session_read_ready (s) != 0;
325       if (!is_nonblk)
326         {
327           vls_mt_mq_lock ();
328           *locks_acq |= VLS_MT_LOCK_MQ;
329         }
330       break;
331     case VLS_MT_OP_WRITE:
332       if (!is_nonblk)
333         is_nonblk = vcl_session_write_ready (s) != 0;
334       if (!is_nonblk)
335         {
336           vls_mt_mq_lock ();
337           *locks_acq |= VLS_MT_LOCK_MQ;
338         }
339       break;
340     case VLS_MT_OP_XPOLL:
341       vls_mt_mq_lock ();
342       *locks_acq |= VLS_MT_LOCK_MQ;
343       break;
344     case VLS_MT_OP_SPOOL:
345       vls_mt_spool_lock ();
346       *locks_acq |= VLS_MT_LOCK_SPOOL;
347       break;
348     default:
349       break;
350     }
351 }
352
353 static void
354 vls_mt_rel_locks (int locks_acq)
355 {
356   if (locks_acq & VLS_MT_LOCK_MQ)
357     vls_mt_mq_unlock ();
358   if (locks_acq & VLS_MT_LOCK_SPOOL)
359     vls_mt_create_unlock ();
360 }
361
362 #define vls_mt_guard(_vls, _op)                         \
363   int _locks_acq = 0;                                   \
364   if (PREDICT_FALSE (vcl_get_worker_index () == ~0));   \
365     vls_mt_add ();                                      \
366   if (PREDICT_FALSE (vls_mt_n_threads > 1))             \
367     vls_mt_acq_locks (_vls, _op, &_locks_acq);          \
368
369 #define vls_mt_unguard()                                \
370   if (PREDICT_FALSE (_locks_acq))                       \
371     vls_mt_rel_locks (_locks_acq)
372
373 int
374 vls_write (vls_handle_t vlsh, void *buf, size_t nbytes)
375 {
376   vcl_locked_session_t *vls;
377   int rv;
378
379   if (!(vls = vls_get_w_dlock (vlsh)))
380     return VPPCOM_EBADFD;
381
382   vls_mt_guard (vls, VLS_MT_OP_WRITE);
383   rv = vppcom_session_write (vls_to_sh_tu (vls), buf, nbytes);
384   vls_mt_unguard ();
385   vls_get_and_unlock (vlsh);
386   return rv;
387 }
388
389 int
390 vls_write_msg (vls_handle_t vlsh, void *buf, size_t nbytes)
391 {
392   vcl_locked_session_t *vls;
393   int rv;
394
395   if (!(vls = vls_get_w_dlock (vlsh)))
396     return VPPCOM_EBADFD;
397   vls_mt_guard (vls, VLS_MT_OP_WRITE);
398   rv = vppcom_session_write_msg (vls_to_sh_tu (vls), buf, nbytes);
399   vls_mt_unguard ();
400   vls_get_and_unlock (vlsh);
401   return rv;
402 }
403
404 int
405 vls_sendto (vls_handle_t vlsh, void *buf, int buflen, int flags,
406             vppcom_endpt_t * ep)
407 {
408   vcl_locked_session_t *vls;
409   int rv;
410
411   if (!(vls = vls_get_w_dlock (vlsh)))
412     return VPPCOM_EBADFD;
413   vls_mt_guard (vls, VLS_MT_OP_WRITE);
414   rv = vppcom_session_sendto (vls_to_sh_tu (vls), buf, buflen, flags, ep);
415   vls_mt_unguard ();
416   vls_get_and_unlock (vlsh);
417   return rv;
418 }
419
420 ssize_t
421 vls_read (vls_handle_t vlsh, void *buf, size_t nbytes)
422 {
423   vcl_locked_session_t *vls;
424   int rv;
425
426   if (!(vls = vls_get_w_dlock (vlsh)))
427     return VPPCOM_EBADFD;
428   vls_mt_guard (vls, VLS_MT_OP_READ);
429   rv = vppcom_session_read (vls_to_sh_tu (vls), buf, nbytes);
430   vls_mt_unguard ();
431   vls_get_and_unlock (vlsh);
432   return rv;
433 }
434
435 ssize_t
436 vls_recvfrom (vls_handle_t vlsh, void *buffer, uint32_t buflen, int flags,
437               vppcom_endpt_t * ep)
438 {
439   vcl_locked_session_t *vls;
440   int rv;
441
442   if (!(vls = vls_get_w_dlock (vlsh)))
443     return VPPCOM_EBADFD;
444   vls_mt_guard (vls, VLS_MT_OP_READ);
445   rv = vppcom_session_recvfrom (vls_to_sh_tu (vls), buffer, buflen, flags,
446                                 ep);
447   vls_mt_unguard ();
448   vls_get_and_unlock (vlsh);
449   return rv;
450 }
451
452 int
453 vls_attr (vls_handle_t vlsh, uint32_t op, void *buffer, uint32_t * buflen)
454 {
455   vcl_locked_session_t *vls;
456   int rv;
457
458   if (!(vls = vls_get_w_dlock (vlsh)))
459     return VPPCOM_EBADFD;
460   rv = vppcom_session_attr (vls_to_sh_tu (vls), op, buffer, buflen);
461   vls_get_and_unlock (vlsh);
462   return rv;
463 }
464
465 int
466 vls_bind (vls_handle_t vlsh, vppcom_endpt_t * ep)
467 {
468   vcl_locked_session_t *vls;
469   int rv;
470
471   if (!(vls = vls_get_w_dlock (vlsh)))
472     return VPPCOM_EBADFD;
473   rv = vppcom_session_bind (vls_to_sh_tu (vls), ep);
474   vls_get_and_unlock (vlsh);
475   return rv;
476 }
477
478 int
479 vls_listen (vls_handle_t vlsh, int q_len)
480 {
481   vcl_locked_session_t *vls;
482   int rv;
483
484   if (!(vls = vls_get_w_dlock (vlsh)))
485     return VPPCOM_EBADFD;
486   vls_mt_guard (vls, VLS_MT_OP_XPOLL);
487   rv = vppcom_session_listen (vls_to_sh_tu (vls), q_len);
488   vls_mt_unguard ();
489   vls_get_and_unlock (vlsh);
490   return rv;
491 }
492
493 int
494 vls_connect (vls_handle_t vlsh, vppcom_endpt_t * server_ep)
495 {
496   vcl_locked_session_t *vls;
497   int rv;
498
499   if (!(vls = vls_get_w_dlock (vlsh)))
500     return VPPCOM_EBADFD;
501   vls_mt_guard (vls, VLS_MT_OP_XPOLL);
502   rv = vppcom_session_connect (vls_to_sh_tu (vls), server_ep);
503   vls_mt_unguard ();
504   vls_get_and_unlock (vlsh);
505   return rv;
506 }
507
508 vls_handle_t
509 vls_accept (vls_handle_t listener_vlsh, vppcom_endpt_t * ep, int flags)
510 {
511   vls_handle_t accepted_vlsh;
512   vcl_locked_session_t *vls;
513   int sh;
514
515   if (!(vls = vls_get_w_dlock (listener_vlsh)))
516     return VPPCOM_EBADFD;
517   vls_mt_guard (vls, VLS_MT_OP_SPOOL);
518   sh = vppcom_session_accept (vls_to_sh_tu (vls), ep, flags);
519   vls_mt_unguard ();
520   vls_get_and_unlock (listener_vlsh);
521   if (sh < 0)
522     return sh;
523   accepted_vlsh = vls_alloc (sh);
524   if (PREDICT_FALSE (accepted_vlsh == VLS_INVALID_HANDLE))
525     vppcom_session_close (sh);
526   return accepted_vlsh;
527 }
528
529 vls_handle_t
530 vls_create (uint8_t proto, uint8_t is_nonblocking)
531 {
532   vcl_session_handle_t sh;
533   vls_handle_t vlsh;
534
535   vls_mt_guard (0, VLS_MT_OP_SPOOL);
536   sh = vppcom_session_create (proto, is_nonblocking);
537   vls_mt_unguard ();
538   if (sh == INVALID_SESSION_ID)
539     return VLS_INVALID_HANDLE;
540
541   vlsh = vls_alloc (sh);
542   if (PREDICT_FALSE (vlsh == VLS_INVALID_HANDLE))
543     vppcom_session_close (sh);
544
545   return vlsh;
546 }
547
548 int
549 vls_close (vls_handle_t vlsh)
550 {
551   vcl_locked_session_t *vls;
552   int rv;
553
554   vls_table_wlock ();
555
556   vls = vls_get_and_lock (vlsh);
557   if (!vls)
558     {
559       vls_table_wunlock ();
560       return VPPCOM_EBADFD;
561     }
562
563   vls_mt_guard (0, VLS_MT_OP_SPOOL);
564   if (vls_is_shared (vls))
565     {
566       /* At least two workers share the session so vls won't be freed */
567       vls_unshare_session (vls, vcl_worker_get_current ());
568       vls_unlock (vls);
569       vls_mt_unguard ();
570       vls_table_wunlock ();
571       return VPPCOM_OK;
572     }
573
574   rv = vppcom_session_close (vls_to_sh (vls));
575   vls_free (vls);
576   vls_mt_unguard ();
577
578   vls_table_wunlock ();
579
580   return rv;
581 }
582
583 vls_handle_t
584 vls_epoll_create (void)
585 {
586   vcl_session_handle_t sh;
587   vls_handle_t vlsh;
588
589   sh = vppcom_epoll_create ();
590   if (sh == INVALID_SESSION_ID)
591     return VLS_INVALID_HANDLE;
592
593   vlsh = vls_alloc (sh);
594   if (vlsh == VLS_INVALID_HANDLE)
595     vppcom_session_close (sh);
596
597   return vlsh;
598 }
599
600 int
601 vls_epoll_ctl (vls_handle_t ep_vlsh, int op, vls_handle_t vlsh,
602                struct epoll_event *event)
603 {
604   vcl_locked_session_t *ep_vls, *vls;
605   vcl_session_handle_t ep_sh, sh;
606   int rv;
607
608   vls_table_rlock ();
609   ep_vls = vls_get_and_lock (ep_vlsh);
610   vls = vls_get_and_lock (vlsh);
611   ep_sh = vls_to_sh (ep_vls);
612   sh = vls_to_sh (vls);
613   vls_table_runlock ();
614
615   rv = vppcom_epoll_ctl (ep_sh, op, sh, event);
616
617   vls_table_rlock ();
618   ep_vls = vls_get (ep_vlsh);
619   vls = vls_get (vlsh);
620   vls_unlock (vls);
621   vls_unlock (ep_vls);
622   vls_table_runlock ();
623   return rv;
624 }
625
626 int
627 vls_epoll_wait (vls_handle_t ep_vlsh, struct epoll_event *events,
628                 int maxevents, double wait_for_time)
629 {
630   vcl_locked_session_t *vls;
631   int rv;
632
633   if (!(vls = vls_get_w_dlock (ep_vlsh)))
634     return VPPCOM_EBADFD;
635   vls_mt_guard (0, VLS_MT_OP_XPOLL);
636   rv = vppcom_epoll_wait (vls_to_sh_tu (vls), events, maxevents,
637                           wait_for_time);
638   vls_mt_unguard ();
639   vls_get_and_unlock (ep_vlsh);
640   return rv;
641 }
642
643 int
644 vls_select (int n_bits, vcl_si_set * read_map, vcl_si_set * write_map,
645             vcl_si_set * except_map, double wait_for_time)
646 {
647   int rv;
648   vls_mt_guard (0, VLS_MT_OP_XPOLL);
649   rv = vppcom_select (n_bits, read_map, write_map, except_map, wait_for_time);
650   vls_mt_unguard ();
651   return rv;
652 }
653
654 vcl_session_handle_t
655 vlsh_to_sh (vls_handle_t vlsh)
656 {
657   vcl_locked_session_t *vls;
658   int rv;
659
660   vls = vls_get_w_dlock (vlsh);
661   if (!vls)
662     return INVALID_SESSION_ID;
663   rv = vls_to_sh (vls);
664   vls_dunlock (vls);
665   return rv;
666 }
667
668 vcl_session_handle_t
669 vlsh_to_session_index (vls_handle_t vlsh)
670 {
671   vcl_session_handle_t sh;
672   sh = vlsh_to_sh (vlsh);
673   return vppcom_session_index (sh);
674 }
675
676 vls_handle_t
677 vls_si_to_vlsh (u32 session_index)
678 {
679   uword *vlshp;
680   vlshp = hash_get (vlsm->session_index_to_vlsh_table, session_index);
681   return vlshp ? *vlshp : VLS_INVALID_HANDLE;
682 }
683
684 vls_handle_t
685 vls_session_index_to_vlsh (uint32_t session_index)
686 {
687   vls_handle_t vlsh;
688
689   vls_table_rlock ();
690   vlsh = vls_si_to_vlsh (session_index);
691   vls_table_runlock ();
692
693   return vlsh;
694 }
695
696 static void
697 vls_unshare_vcl_worker_sessions (vcl_worker_t * wrk)
698 {
699   u32 current_wrk, is_current;
700   vcl_locked_session_t *vls;
701   vcl_session_t *s;
702
703   current_wrk = vcl_get_worker_index ();
704   is_current = current_wrk == wrk->wrk_index;
705   vls_table_wlock ();
706
707   /* *INDENT-OFF* */
708   pool_foreach (s, wrk->sessions, ({
709     vls = vls_get (vls_si_to_vlsh (s->session_index));
710     if (vls && (is_current || vls_is_shared_by_wrk (vls, current_wrk)))
711       vls_unshare_session (vls, wrk);
712   }));
713   /* *INDENT-ON* */
714
715   vls_table_wunlock ();
716 }
717
718 static void
719 vls_cleanup_vcl_worker (vcl_worker_t * wrk)
720 {
721   /* Unshare sessions and also cleanup worker since child may have
722    * called _exit () and therefore vcl may not catch the event */
723   vls_unshare_vcl_worker_sessions (wrk);
724   vcl_worker_cleanup (wrk, 1 /* notify vpp */ );
725 }
726
727 static void
728 vls_cleanup_forked_child (vcl_worker_t * wrk, vcl_worker_t * child_wrk)
729 {
730   vcl_worker_t *sub_child;
731   int tries = 0;
732
733   if (child_wrk->forked_child != ~0)
734     {
735       sub_child = vcl_worker_get_if_valid (child_wrk->forked_child);
736       if (sub_child)
737         {
738           /* Wait a bit, maybe the process is going away */
739           while (kill (sub_child->current_pid, 0) >= 0 && tries++ < 50)
740             usleep (1e3);
741           if (kill (sub_child->current_pid, 0) < 0)
742             vls_cleanup_forked_child (child_wrk, sub_child);
743         }
744     }
745   vls_cleanup_vcl_worker (child_wrk);
746   VDBG (0, "Cleaned up forked child wrk %u", child_wrk->wrk_index);
747   wrk->forked_child = ~0;
748 }
749
750 static struct sigaction old_sa;
751
752 static void
753 vls_intercept_sigchld_handler (int signum, siginfo_t * si, void *uc)
754 {
755   vcl_worker_t *wrk, *child_wrk;
756
757   if (vcl_get_worker_index () == ~0)
758     return;
759
760   if (sigaction (SIGCHLD, &old_sa, 0))
761     {
762       VERR ("couldn't restore sigchld");
763       exit (-1);
764     }
765
766   wrk = vcl_worker_get_current ();
767   if (wrk->forked_child == ~0)
768     return;
769
770   child_wrk = vcl_worker_get_if_valid (wrk->forked_child);
771   if (!child_wrk)
772     goto done;
773
774   if (si && si->si_pid != child_wrk->current_pid)
775     {
776       VDBG (0, "unexpected child pid %u", si->si_pid);
777       goto done;
778     }
779   vls_cleanup_forked_child (wrk, child_wrk);
780
781 done:
782   if (old_sa.sa_flags & SA_SIGINFO)
783     {
784       void (*fn) (int, siginfo_t *, void *) = old_sa.sa_sigaction;
785       fn (signum, si, uc);
786     }
787   else
788     {
789       void (*fn) (int) = old_sa.sa_handler;
790       if (fn)
791         fn (signum);
792     }
793 }
794
795 static void
796 vls_incercept_sigchld ()
797 {
798   struct sigaction sa;
799   clib_memset (&sa, 0, sizeof (sa));
800   sa.sa_sigaction = vls_intercept_sigchld_handler;
801   sa.sa_flags = SA_SIGINFO;
802   if (sigaction (SIGCHLD, &sa, &old_sa))
803     {
804       VERR ("couldn't intercept sigchld");
805       exit (-1);
806     }
807 }
808
809 static void
810 vls_app_pre_fork (void)
811 {
812   vls_incercept_sigchld ();
813   vcl_flush_mq_events ();
814 }
815
816 static void
817 vls_app_fork_child_handler (void)
818 {
819   vcl_worker_t *parent_wrk;
820   int rv, parent_wrk_index;
821   u8 *child_name;
822
823   parent_wrk_index = vcl_get_worker_index ();
824   VDBG (0, "initializing forked child %u with parent wrk %u", getpid (),
825         parent_wrk_index);
826
827   /*
828    * Allocate worker
829    */
830   vcl_set_worker_index (~0);
831   if (!vcl_worker_alloc_and_init ())
832     VERR ("couldn't allocate new worker");
833
834   /*
835    * Attach to binary api
836    */
837   child_name = format (0, "%v-child-%u%c", vcm->app_name, getpid (), 0);
838   vcl_cleanup_bapi ();
839   vppcom_api_hookup ();
840   vcm->app_state = STATE_APP_START;
841   rv = vppcom_connect_to_vpp ((char *) child_name);
842   vec_free (child_name);
843   if (rv)
844     {
845       VERR ("couldn't connect to VPP!");
846       return;
847     }
848
849   /*
850    * Register worker with vpp and share sessions
851    */
852   vcl_worker_register_with_vpp ();
853   parent_wrk = vcl_worker_get (parent_wrk_index);
854   vls_worker_copy_on_fork (parent_wrk);
855   parent_wrk->forked_child = vcl_get_worker_index ();
856
857   /* Reset number of threads and set wrk index */
858   vls_mt_n_threads = 0;
859   vls_wrk_index = vcl_get_worker_index ();
860
861   VDBG (0, "forked child main worker initialized");
862   vcm->forking = 0;
863 }
864
865 static void
866 vls_app_fork_parent_handler (void)
867 {
868   vcm->forking = 1;
869   while (vcm->forking)
870     ;
871 }
872
873 void
874 vls_app_exit (void)
875 {
876   /* Unshare the sessions. VCL will clean up the worker */
877   vls_unshare_vcl_worker_sessions (vcl_worker_get_current ());
878 }
879
880 int
881 vls_app_create (char *app_name)
882 {
883   int rv;
884
885   if ((rv = vppcom_app_create (app_name)))
886     return rv;
887
888   clib_rwlock_init (&vlsm->vls_table_lock);
889   pthread_atfork (vls_app_pre_fork, vls_app_fork_parent_handler,
890                   vls_app_fork_child_handler);
891   atexit (vls_app_exit);
892   vls_wrk_index = vcl_get_worker_index ();
893   return VPPCOM_OK;
894 }
895
896 /*
897  * fd.io coding-style-patch-verification: ON
898  *
899  * Local Variables:
900  * eval: (c-set-style "gnu")
901  * End:
902  */