2 * Copyright (c) 2019 Cisco and/or its affiliates.
3 * Licensed under the Apache License, Version 2.0 (the "License");
5 * You may obtain a copy of the License at:
7 * http://www.apache.org/licenses/LICENSE-2.0
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.
16 #include <vcl/vcl_locked.h>
17 #include <vcl/vcl_private.h>
19 typedef struct vcl_locked_session_
26 u32 *workers_subscribed;
27 } vcl_locked_session_t;
29 typedef struct vcl_main_
31 vcl_locked_session_t *vls_pool;
32 clib_rwlock_t vls_table_lock;
33 uword *session_index_to_vlsh_table;
37 vls_main_t *vlsm = &vls_main;
40 vls_table_rlock (void)
42 clib_rwlock_reader_lock (&vlsm->vls_table_lock);
46 vls_table_runlock (void)
48 clib_rwlock_reader_unlock (&vlsm->vls_table_lock);
52 vls_table_wlock (void)
54 clib_rwlock_writer_lock (&vlsm->vls_table_lock);
58 vls_table_wunlock (void)
60 clib_rwlock_writer_unlock (&vlsm->vls_table_lock);
63 static inline vcl_session_handle_t
64 vls_to_sh (vcl_locked_session_t * vls)
66 return vcl_session_handle_from_index (vls->session_index);
69 static inline vcl_session_handle_t
70 vls_to_sh_tu (vcl_locked_session_t * vls)
72 vcl_session_handle_t sh;
79 vls_alloc (vcl_session_handle_t sh)
81 vcl_locked_session_t *vls;
84 pool_get (vlsm->vls_pool, vls);
85 vls->session_index = vppcom_session_index (sh);
86 vls->worker_index = vppcom_session_worker (sh);
87 vls->vls_index = vls - vlsm->vls_pool;
88 hash_set (vlsm->session_index_to_vlsh_table, vls->session_index,
90 clib_spinlock_init (&vls->lock);
92 return vls->vls_index;
95 static vcl_locked_session_t *
96 vls_get (vls_handle_t vlsh)
98 if (pool_is_free_index (vlsm->vls_pool, vlsh))
100 return pool_elt_at_index (vlsm->vls_pool, vlsh);
104 vls_free (vcl_locked_session_t * fde)
107 hash_unset (vlsm->session_index_to_vlsh_table, fde->session_index);
108 clib_spinlock_free (&fde->lock);
109 pool_put (vlsm->vls_pool, fde);
112 static vcl_locked_session_t *
113 vls_get_and_lock (vls_handle_t vlsh)
115 vcl_locked_session_t *vls;
116 if (pool_is_free_index (vlsm->vls_pool, vlsh))
118 vls = pool_elt_at_index (vlsm->vls_pool, vlsh);
119 clib_spinlock_lock (&vls->lock);
123 static vcl_locked_session_t *
124 vls_get_w_dlock (vls_handle_t vlsh)
126 vcl_locked_session_t *vls;
128 vls = vls_get_and_lock (vlsh);
130 vls_table_runlock ();
135 vls_unlock (vcl_locked_session_t * vls)
137 clib_spinlock_unlock (&vls->lock);
141 vls_get_and_unlock (vls_handle_t vlsh)
143 vcl_locked_session_t *vls;
145 vls = vls_get (vlsh);
147 vls_table_runlock ();
151 vls_dunlock (vcl_locked_session_t * vls)
154 vls_table_runlock ();
158 vls_get_and_free (vls_handle_t vlsh)
160 vcl_locked_session_t *vls;
163 vls = vls_get (vlsh);
165 vls_table_wunlock ();
169 vls_is_shared (vcl_locked_session_t * vls)
171 return vec_len (vls->workers_subscribed);
175 vls_unshare_session (vcl_locked_session_t * vls)
177 vcl_worker_t *wrk = vcl_worker_get_current ();
181 for (i = 0; i < vec_len (vls->workers_subscribed); i++)
183 if (vls->workers_subscribed[i] != wrk->wrk_index)
186 s = vcl_session_get (wrk, vls->session_index);
189 svm_fifo_del_subscriber (s->rx_fifo, wrk->vpp_wrk_index);
190 svm_fifo_del_subscriber (s->tx_fifo, wrk->vpp_wrk_index);
192 vec_del1 (vls->workers_subscribed, i);
193 vcl_session_cleanup (wrk, s, vcl_session_handle (s),
194 0 /* do_disconnect */ );
198 /* Assumption is that unshare is only called if session is shared.
199 * So shared_workers must be non-empty if the worker is the owner */
200 if (vls->worker_index == wrk->wrk_index)
202 s = vcl_session_get (wrk, vls->session_index);
203 vls->worker_index = vls->workers_subscribed[0];
204 vec_del1 (vls->workers_subscribed, 0);
205 vcl_send_session_worker_update (wrk, s, vls->worker_index);
206 if (vec_len (vls->workers_subscribed))
207 clib_warning ("more workers need to be updated");
214 vls_share_vcl_session (vcl_worker_t * wrk, vcl_session_t * s)
216 vcl_locked_session_t *vls;
218 vls = vls_get_w_dlock (vls_session_index_to_vlsh (s->session_index));
221 vec_add1 (vls->workers_subscribed, wrk->wrk_index);
224 svm_fifo_add_subscriber (s->rx_fifo, wrk->vpp_wrk_index);
225 svm_fifo_add_subscriber (s->tx_fifo, wrk->vpp_wrk_index);
231 vls_worker_copy_on_fork (vcl_worker_t * parent_wrk)
233 vcl_worker_t *wrk = vcl_worker_get_current ();
236 wrk->vpp_event_queues = vec_dup (parent_wrk->vpp_event_queues);
237 wrk->sessions = pool_dup (parent_wrk->sessions);
238 wrk->session_index_by_vpp_handles =
239 hash_dup (parent_wrk->session_index_by_vpp_handles);
242 pool_foreach (s, wrk->sessions, ({
243 vls_share_vcl_session (wrk, s);
249 vls_write (vls_handle_t vlsh, void *buf, size_t nbytes)
251 vcl_locked_session_t *vls;
254 if (!(vls = vls_get_w_dlock (vlsh)))
255 return VPPCOM_EBADFD;
256 rv = vppcom_session_write (vls_to_sh_tu (vls), buf, nbytes);
257 vls_get_and_unlock (vlsh);
262 vls_write_msg (vls_handle_t vlsh, void *buf, size_t nbytes)
264 vcl_locked_session_t *vls;
267 if (!(vls = vls_get_w_dlock (vlsh)))
268 return VPPCOM_EBADFD;
269 rv = vppcom_session_write_msg (vls_to_sh_tu (vls), buf, nbytes);
270 vls_get_and_unlock (vlsh);
275 vls_sendto (vls_handle_t vlsh, void *buf, int buflen, int flags,
278 vcl_locked_session_t *vls;
281 if (!(vls = vls_get_w_dlock (vlsh)))
282 return VPPCOM_EBADFD;
283 rv = vppcom_session_sendto (vls_to_sh_tu (vls), buf, buflen, flags, ep);
284 vls_get_and_unlock (vlsh);
289 vls_read (vls_handle_t vlsh, void *buf, size_t nbytes)
291 vcl_locked_session_t *vls;
294 if (!(vls = vls_get_w_dlock (vlsh)))
295 return VPPCOM_EBADFD;
296 rv = vppcom_session_read (vls_to_sh_tu (vls), buf, nbytes);
297 vls_get_and_unlock (vlsh);
302 vls_recvfrom (vls_handle_t vlsh, void *buffer, uint32_t buflen, int flags,
305 vcl_locked_session_t *vls;
308 if (!(vls = vls_get_w_dlock (vlsh)))
309 return VPPCOM_EBADFD;
310 rv = vppcom_session_recvfrom (vls_to_sh_tu (vls), buffer, buflen, flags,
312 vls_get_and_unlock (vlsh);
317 vls_attr (vls_handle_t vlsh, uint32_t op, void *buffer, uint32_t * buflen)
319 vcl_locked_session_t *vls;
322 if (!(vls = vls_get_w_dlock (vlsh)))
323 return VPPCOM_EBADFD;
324 rv = vppcom_session_attr (vls_to_sh_tu (vls), op, buffer, buflen);
325 vls_get_and_unlock (vlsh);
330 vls_bind (vls_handle_t vlsh, vppcom_endpt_t * ep)
332 vcl_locked_session_t *vls;
335 if (!(vls = vls_get_w_dlock (vlsh)))
336 return VPPCOM_EBADFD;
337 rv = vppcom_session_bind (vls_to_sh_tu (vls), ep);
338 vls_get_and_unlock (vlsh);
343 vls_listen (vls_handle_t vlsh, int q_len)
345 vcl_locked_session_t *vls;
348 if (!(vls = vls_get_w_dlock (vlsh)))
349 return VPPCOM_EBADFD;
350 rv = vppcom_session_listen (vls_to_sh_tu (vls), q_len);
351 vls_get_and_unlock (vlsh);
356 vls_connect (vls_handle_t vlsh, vppcom_endpt_t * server_ep)
358 vcl_locked_session_t *vls;
361 if (!(vls = vls_get_w_dlock (vlsh)))
362 return VPPCOM_EBADFD;
363 rv = vppcom_session_connect (vls_to_sh_tu (vls), server_ep);
364 vls_get_and_unlock (vlsh);
369 vls_accept (vls_handle_t listener_vlsh, vppcom_endpt_t * ep, int flags)
371 vls_handle_t accepted_vlsh;
372 vcl_locked_session_t *vls;
375 if (!(vls = vls_get_w_dlock (listener_vlsh)))
376 return VPPCOM_EBADFD;
377 sh = vppcom_session_accept (vls_to_sh_tu (vls), ep, flags);
378 vls_get_and_unlock (listener_vlsh);
381 accepted_vlsh = vls_alloc (sh);
382 if (PREDICT_FALSE (accepted_vlsh == VLS_INVALID_HANDLE))
383 vppcom_session_close (sh);
384 return accepted_vlsh;
388 vls_create (uint8_t proto, uint8_t is_nonblocking)
390 vcl_session_handle_t sh;
393 sh = vppcom_session_create (proto, is_nonblocking);
394 if (sh == INVALID_SESSION_ID)
395 return VLS_INVALID_HANDLE;
397 vlsh = vls_alloc (sh);
398 if (PREDICT_FALSE (vlsh == VLS_INVALID_HANDLE))
399 vppcom_session_close (sh);
405 vls_close (vls_handle_t vlsh)
407 vcl_locked_session_t *vls;
408 vcl_session_handle_t sh;
411 if (!(vls = vls_get_w_dlock (vlsh)))
412 return VPPCOM_EBADFD;
414 if (vls_is_shared (vls))
416 vls_unshare_session (vls);
421 sh = vls_to_sh (vls);
422 if ((rv = vppcom_session_close (sh)))
429 vls_get_and_free (vlsh);
434 vls_epoll_create (void)
436 vcl_session_handle_t sh;
439 sh = vppcom_epoll_create ();
440 if (sh == INVALID_SESSION_ID)
441 return VLS_INVALID_HANDLE;
443 vlsh = vls_alloc (sh);
444 if (vlsh == VLS_INVALID_HANDLE)
445 vppcom_session_close (sh);
451 vls_epoll_ctl (vls_handle_t ep_vlsh, int op, vls_handle_t vlsh,
452 struct epoll_event *event)
454 vcl_locked_session_t *ep_vls, *vls;
455 vcl_session_handle_t ep_sh, sh;
459 ep_vls = vls_get_and_lock (ep_vlsh);
460 vls = vls_get_and_lock (vlsh);
461 ep_sh = vls_to_sh (ep_vls);
462 sh = vls_to_sh (vls);
463 vls_table_runlock ();
465 rv = vppcom_epoll_ctl (ep_sh, op, sh, event);
468 ep_vls = vls_get (ep_vlsh);
469 vls = vls_get (vlsh);
472 vls_table_runlock ();
477 vls_epoll_wait (vls_handle_t ep_vlsh, struct epoll_event *events,
478 int maxevents, double wait_for_time)
480 vcl_locked_session_t *vls;
483 if (!(vls = vls_get_w_dlock (ep_vlsh)))
484 return VPPCOM_EBADFD;
485 rv = vppcom_epoll_wait (vls_to_sh_tu (vls), events, maxevents,
487 vls_get_and_unlock (ep_vlsh);
492 vlsh_to_sh (vls_handle_t vlsh)
494 vcl_locked_session_t *vls;
497 vls = vls_get_w_dlock (vlsh);
499 return INVALID_SESSION_ID;
500 rv = vls_to_sh (vls);
506 vlsh_to_session_index (vls_handle_t vlsh)
508 vcl_session_handle_t sh;
509 sh = vlsh_to_sh (vlsh);
510 return vppcom_session_index (sh);
514 vls_session_index_to_vlsh (uint32_t session_index)
520 vlshp = hash_get (vlsm->session_index_to_vlsh_table, session_index);
521 vlsh = vlshp ? *vlshp : VLS_INVALID_HANDLE;
522 vls_table_runlock ();
528 vls_cleanup_forked_child (vcl_worker_t * wrk, vcl_worker_t * child_wrk)
530 vcl_worker_t *sub_child;
533 if (child_wrk->forked_child != ~0)
535 sub_child = vcl_worker_get_if_valid (child_wrk->forked_child);
538 /* Wait a bit, maybe the process is going away */
539 while (kill (sub_child->current_pid, 0) >= 0 && tries++ < 50)
541 if (kill (sub_child->current_pid, 0) < 0)
542 vls_cleanup_forked_child (child_wrk, sub_child);
545 vcl_worker_cleanup (child_wrk, 1 /* notify vpp */ );
546 VDBG (0, "Cleaned up wrk %u", child_wrk->wrk_index);
547 wrk->forked_child = ~0;
550 static struct sigaction old_sa;
553 vls_intercept_sigchld_handler (int signum, siginfo_t * si, void *uc)
555 vcl_worker_t *wrk, *child_wrk;
557 if (vcl_get_worker_index () == ~0)
560 if (sigaction (SIGCHLD, &old_sa, 0))
562 VERR ("couldn't restore sigchld");
566 wrk = vcl_worker_get_current ();
567 if (wrk->forked_child == ~0)
570 child_wrk = vcl_worker_get_if_valid (wrk->forked_child);
574 if (si && si->si_pid != child_wrk->current_pid)
576 VDBG (0, "unexpected child pid %u", si->si_pid);
579 vls_cleanup_forked_child (wrk, child_wrk);
582 if (old_sa.sa_flags & SA_SIGINFO)
584 void (*fn) (int, siginfo_t *, void *) = old_sa.sa_sigaction;
589 void (*fn) (int) = old_sa.sa_handler;
596 vls_incercept_sigchld ()
599 clib_memset (&sa, 0, sizeof (sa));
600 sa.sa_sigaction = vls_intercept_sigchld_handler;
601 sa.sa_flags = SA_SIGINFO;
602 if (sigaction (SIGCHLD, &sa, &old_sa))
604 VERR ("couldn't intercept sigchld");
610 vls_app_pre_fork (void)
612 vls_incercept_sigchld ();
613 vcl_flush_mq_events ();
617 vls_app_fork_child_handler (void)
619 vcl_worker_t *parent_wrk;
620 int rv, parent_wrk_index;
623 parent_wrk_index = vcl_get_worker_index ();
624 VDBG (0, "initializing forked child %u with parent wrk %u", getpid (),
630 vcl_set_worker_index (~0);
631 if (!vcl_worker_alloc_and_init ())
632 VERR ("couldn't allocate new worker");
635 * Attach to binary api
637 child_name = format (0, "%v-child-%u%c", vcm->app_name, getpid (), 0);
639 vppcom_api_hookup ();
640 vcm->app_state = STATE_APP_START;
641 rv = vppcom_connect_to_vpp ((char *) child_name);
642 vec_free (child_name);
645 VERR ("couldn't connect to VPP!");
650 * Register worker with vpp and share sessions
652 vcl_worker_register_with_vpp ();
653 parent_wrk = vcl_worker_get (parent_wrk_index);
654 vls_worker_copy_on_fork (parent_wrk);
655 parent_wrk->forked_child = vcl_get_worker_index ();
657 VDBG (0, "forked child main worker initialized");
662 vls_app_fork_parent_handler (void)
670 vls_app_create (char *app_name)
673 if ((rv = vppcom_app_create (app_name)))
675 clib_rwlock_init (&vlsm->vls_table_lock);
676 pthread_atfork (vls_app_pre_fork, vls_app_fork_parent_handler,
677 vls_app_fork_child_handler);
682 * fd.io coding-style-patch-verification: ON
685 * eval: (c-set-style "gnu")