vcl: basic support for apps that fork 98/16198/18
authorFlorin Coras <fcoras@cisco.com>
Tue, 27 Nov 2018 01:01:36 +0000 (17:01 -0800)
committerDamjan Marion <dmarion@me.com>
Thu, 29 Nov 2018 11:27:18 +0000 (11:27 +0000)
- intercept fork and register a new worker with vpp
- share sessions between parent and forked child
- keep binary api state per worker

Change-Id: Ib177517d661724fa042bd2d98d18e777056352a2
Signed-off-by: Florin Coras <fcoras@cisco.com>
src/vcl/ldp.c
src/vcl/vcl_bapi.c
src/vcl/vcl_cfg.c
src/vcl/vcl_debug.h
src/vcl/vcl_private.c
src/vcl/vcl_private.h
src/vcl/vppcom.c
src/vcl/vppcom.h
src/vnet/session/application.c
src/vppinfra/pool.h

index 5c70643..915d1ca 100644 (file)
@@ -346,7 +346,7 @@ ldp_init (void)
 int
 close (int fd)
 {
-  int rv;
+  int rv, refcnt;
   const char *func_str;
   u32 sid = ldp_sid_from_fd (fd);
 
@@ -388,13 +388,15 @@ close (int fd)
       LDBG (0, "LDP<%d>: fd %d (0x%x): calling %s(): sid %u (0x%x)",
            getpid (), fd, fd, func_str, sid, sid);
 
+      refcnt = vppcom_session_attr (sid, VPPCOM_ATTR_GET_REFCNT, 0, 0);
       rv = vppcom_session_close (sid);
       if (rv != VPPCOM_OK)
        {
          errno = -rv;
          rv = -1;
        }
-      ldp_fd_free_w_sid (sid);
+      if (refcnt == 1)
+       ldp_fd_free_w_sid (sid);
     }
   else
     {
index cc494ed..93ba0ab 100644 (file)
@@ -354,20 +354,22 @@ vppcom_api_hookup (void)
 void
 vppcom_send_session_enable_disable (u8 is_enable)
 {
+  vcl_worker_t *wrk = vcl_worker_get_current ();
   vl_api_session_enable_disable_t *bmp;
   bmp = vl_msg_api_alloc (sizeof (*bmp));
   memset (bmp, 0, sizeof (*bmp));
 
   bmp->_vl_msg_id = ntohs (VL_API_SESSION_ENABLE_DISABLE);
-  bmp->client_index = vcm->my_client_index;
+  bmp->client_index = wrk->my_client_index;
   bmp->context = htonl (0xfeedface);
   bmp->is_enable = is_enable;
-  vl_msg_api_send_shmem (vcm->vl_input_queue, (u8 *) & bmp);
+  vl_msg_api_send_shmem (wrk->vl_input_queue, (u8 *) & bmp);
 }
 
 void
 vppcom_app_send_attach (void)
 {
+  vcl_worker_t *wrk = vcl_worker_get_current ();
   vl_api_application_attach_t *bmp;
   u8 nsid_len = vec_len (vcm->cfg.namespace_id);
   u8 app_is_proxy = (vcm->cfg.app_proxy_transport_tcp ||
@@ -377,7 +379,7 @@ vppcom_app_send_attach (void)
   memset (bmp, 0, sizeof (*bmp));
 
   bmp->_vl_msg_id = ntohs (VL_API_APPLICATION_ATTACH);
-  bmp->client_index = vcm->my_client_index;
+  bmp->client_index = wrk->my_client_index;
   bmp->context = htonl (0xfeedface);
   bmp->options[APP_OPTIONS_FLAGS] =
     APP_OPTIONS_FLAGS_ACCEPT_REDIRECT | APP_OPTIONS_FLAGS_ADD_SEGMENT |
@@ -402,20 +404,21 @@ vppcom_app_send_attach (void)
       clib_memcpy_fast (bmp->namespace_id, vcm->cfg.namespace_id, nsid_len);
       bmp->options[APP_OPTIONS_NAMESPACE_SECRET] = vcm->cfg.namespace_secret;
     }
-  vl_msg_api_send_shmem (vcm->vl_input_queue, (u8 *) & bmp);
+  vl_msg_api_send_shmem (wrk->vl_input_queue, (u8 *) & bmp);
 }
 
 void
 vppcom_app_send_detach (void)
 {
+  vcl_worker_t *wrk = vcl_worker_get_current ();
   vl_api_application_detach_t *bmp;
   bmp = vl_msg_api_alloc (sizeof (*bmp));
   memset (bmp, 0, sizeof (*bmp));
 
   bmp->_vl_msg_id = ntohs (VL_API_APPLICATION_DETACH);
-  bmp->client_index = vcm->my_client_index;
+  bmp->client_index = wrk->my_client_index;
   bmp->context = htonl (0xfeedface);
-  vl_msg_api_send_shmem (vcm->vl_input_queue, (u8 *) & bmp);
+  vl_msg_api_send_shmem (wrk->vl_input_queue, (u8 *) & bmp);
 }
 
 void
@@ -429,14 +432,14 @@ vcl_send_app_worker_add_del (u8 is_add)
   memset (mp, 0, sizeof (*mp));
 
   mp->_vl_msg_id = ntohs (VL_API_APP_WORKER_ADD_DEL);
-  mp->client_index = vcm->my_client_index;
+  mp->client_index = wrk->my_client_index;
   mp->app_index = clib_host_to_net_u32 (vcm->app_index);
   mp->context = wrk_index;
   mp->is_add = is_add;
   if (!is_add)
     mp->wrk_index = clib_host_to_net_u32 (wrk_index);
 
-  vl_msg_api_send_shmem (vcm->vl_input_queue, (u8 *) & mp);
+  vl_msg_api_send_shmem (wrk->vl_input_queue, (u8 *) & mp);
 }
 
 void
@@ -448,7 +451,7 @@ vppcom_send_connect_sock (vcl_session_t * session)
   cmp = vl_msg_api_alloc (sizeof (*cmp));
   memset (cmp, 0, sizeof (*cmp));
   cmp->_vl_msg_id = ntohs (VL_API_CONNECT_SOCK);
-  cmp->client_index = vcm->my_client_index;
+  cmp->client_index = wrk->my_client_index;
   cmp->context = session->session_index;
   cmp->wrk_index = wrk->vpp_wrk_index;
   cmp->is_ip4 = session->transport.is_ip4;
@@ -456,20 +459,21 @@ vppcom_send_connect_sock (vcl_session_t * session)
   cmp->port = session->transport.rmt_port;
   cmp->proto = session->session_type;
   clib_memcpy_fast (cmp->options, session->options, sizeof (cmp->options));
-  vl_msg_api_send_shmem (vcm->vl_input_queue, (u8 *) & cmp);
+  vl_msg_api_send_shmem (wrk->vl_input_queue, (u8 *) & cmp);
 }
 
 void
 vppcom_send_disconnect_session (u64 vpp_handle)
 {
+  vcl_worker_t *wrk = vcl_worker_get_current ();
   vl_api_disconnect_session_t *dmp;
 
   dmp = vl_msg_api_alloc (sizeof (*dmp));
   memset (dmp, 0, sizeof (*dmp));
   dmp->_vl_msg_id = ntohs (VL_API_DISCONNECT_SESSION);
-  dmp->client_index = vcm->my_client_index;
+  dmp->client_index = wrk->my_client_index;
   dmp->handle = vpp_handle;
-  vl_msg_api_send_shmem (vcm->vl_input_queue, (u8 *) & dmp);
+  vl_msg_api_send_shmem (wrk->vl_input_queue, (u8 *) & dmp);
 }
 
 /* VPP combines bind and listen as one operation. VCL manages the separation
@@ -486,7 +490,7 @@ vppcom_send_bind_sock (vcl_session_t * session)
   memset (bmp, 0, sizeof (*bmp));
 
   bmp->_vl_msg_id = ntohs (VL_API_BIND_SOCK);
-  bmp->client_index = vcm->my_client_index;
+  bmp->client_index = wrk->my_client_index;
   bmp->context = session->session_index;
   bmp->wrk_index = wrk->vpp_wrk_index;
   bmp->is_ip4 = session->transport.is_ip4;
@@ -494,7 +498,7 @@ vppcom_send_bind_sock (vcl_session_t * session)
   bmp->port = session->transport.lcl_port;
   bmp->proto = session->session_type;
   clib_memcpy_fast (bmp->options, session->options, sizeof (bmp->options));
-  vl_msg_api_send_shmem (vcm->vl_input_queue, (u8 *) & bmp);
+  vl_msg_api_send_shmem (wrk->vl_input_queue, (u8 *) & bmp);
 }
 
 void
@@ -507,15 +511,16 @@ vppcom_send_unbind_sock (u64 vpp_handle)
   memset (ump, 0, sizeof (*ump));
 
   ump->_vl_msg_id = ntohs (VL_API_UNBIND_SOCK);
-  ump->client_index = vcm->my_client_index;
+  ump->client_index = wrk->my_client_index;
   ump->wrk_index = wrk->vpp_wrk_index;
   ump->handle = vpp_handle;
-  vl_msg_api_send_shmem (vcm->vl_input_queue, (u8 *) & ump);
+  vl_msg_api_send_shmem (wrk->vl_input_queue, (u8 *) & ump);
 }
 
 void
 vppcom_send_accept_session_reply (u64 handle, u32 context, int retval)
 {
+  vcl_worker_t *wrk = vcl_worker_get_current ();
   vl_api_accept_session_reply_t *rmp;
 
   rmp = vl_msg_api_alloc (sizeof (*rmp));
@@ -524,7 +529,7 @@ vppcom_send_accept_session_reply (u64 handle, u32 context, int retval)
   rmp->retval = htonl (retval);
   rmp->context = context;
   rmp->handle = handle;
-  vl_msg_api_send_shmem (vcm->vl_input_queue, (u8 *) & rmp);
+  vl_msg_api_send_shmem (wrk->vl_input_queue, (u8 *) & rmp);
 }
 
 u32
@@ -549,6 +554,7 @@ vppcom_init_error_string_table (void)
 int
 vppcom_connect_to_vpp (char *app_name)
 {
+  vcl_worker_t *wrk = vcl_worker_get_current ();
   api_main_t *am = &api_main;
   vppcom_cfg_t *vcl_cfg = &vcm->cfg;
 
@@ -584,9 +590,9 @@ vppcom_connect_to_vpp (char *app_name)
 
     }
 
-  vcm->vl_input_queue = am->shmem_hdr->vl_input_queue;
-  vcm->my_client_index = (u32) am->my_client_index;
-  vcm->app_state = STATE_APP_CONN_VPP;
+  wrk->vl_input_queue = am->shmem_hdr->vl_input_queue;
+  wrk->my_client_index = (u32) am->my_client_index;
+  wrk->wrk_state = STATE_APP_CONN_VPP;
 
   VDBG (0, "app (%s) is connected to VPP!", app_name);
   vcl_evt (VCL_EVT_INIT, vcm);
index 402bb91..8baae82 100644 (file)
@@ -23,7 +23,6 @@ static vppcom_main_t _vppcom_main = {
   .debug = VPPCOM_DEBUG_INIT,
   .is_init = 0,
   .app_index = ~0,
-  .my_client_index = ~0
 };
 
 vppcom_main_t *vcm = &_vppcom_main;
index 48ff21a..191d400 100644 (file)
 
 #define VDBG(_lvl, _fmt, _args...)                                     \
   if (VCL_DBG_ON && vcm->debug > _lvl)                                 \
-    clib_warning ("vcl<%d:%d>: " _fmt, vcm->current_pid,               \
+    clib_warning ("vcl<%d:%d>: " _fmt,                                         \
+                 vcm->workers[__vcl_worker_index].current_pid,         \
                  __vcl_worker_index, ##_args)
 
 #define VWRN(_fmt, _args...)                                           \
-clib_warning ("vcl<%d:%d>: " _fmt, vcm->current_pid,                   \
-               __vcl_worker_index, ##_args)
+  clib_warning ("vcl<%d:%d>: " _fmt,                                   \
+                vcm->workers[__vcl_worker_index].current_pid,          \
+                __vcl_worker_index, ##_args)
 
 #define VERR(_fmt, _args...)                                           \
-  clib_warning ("vcl<%d:%d>: ERROR " _fmt, vcm->current_pid,           \
+  clib_warning ("vcl<%d:%d>: ERROR " _fmt,                             \
+                vcm->workers[__vcl_worker_index].current_pid,          \
                __vcl_worker_index, ##_args)
 
 #define foreach_vcl_dbg_evt                                            \
index d159a49..86dccfe 100644 (file)
@@ -220,12 +220,12 @@ vcl_worker_free (vcl_worker_t * wrk)
   pool_put (vcm->workers, wrk);
 }
 
-static void
-vcl_worker_cleanup (void *arg)
+void
+vcl_worker_cleanup (void)
 {
   vcl_worker_t *wrk = vcl_worker_get_current ();
 
-  VDBG (0, "cleaning up worker %u", wrk->wrk_index);
+  clib_spinlock_lock (&vcm->workers_lock);
   vcl_send_app_worker_add_del (0 /* is_add */ );
   close (wrk->mqs_epfd);
   hash_free (wrk->session_index_by_vpp_handles);
@@ -235,6 +235,14 @@ vcl_worker_cleanup (void *arg)
   vec_free (wrk->mq_msg_vector);
   vcl_set_worker_index (~0);
   vcl_worker_free (wrk);
+  clib_spinlock_unlock (&vcm->workers_lock);
+  VDBG (0, "cleaned up worker %u", wrk->wrk_index);
+}
+
+static void
+vcl_worker_cleanup_cb (void *arg)
+{
+  vcl_worker_cleanup ();
 }
 
 vcl_worker_t *
@@ -255,6 +263,8 @@ vcl_worker_alloc_and_init ()
   clib_spinlock_lock (&vcm->workers_lock);
   wrk = vcl_worker_alloc ();
   vcl_set_worker_index (wrk->wrk_index);
+  wrk->thread_id = pthread_self ();
+  wrk->current_pid = getpid ();
 
   wrk->mqs_epfd = -1;
   if (vcm->cfg.use_mq_eventfd)
@@ -263,7 +273,7 @@ vcl_worker_alloc_and_init ()
       if (wrk->mqs_epfd < 0)
        {
          clib_unix_warning ("epoll_create() returned");
-         return 0;
+         goto done;
        }
     }
 
@@ -276,32 +286,160 @@ vcl_worker_alloc_and_init ()
   vec_reset_length (wrk->mq_msg_vector);
   vec_validate (wrk->unhandled_evts_vector, 128);
   vec_reset_length (wrk->unhandled_evts_vector);
+  clib_spinlock_unlock (&vcm->workers_lock);
 
-  if (wrk->wrk_index == 0)
-    {
-      clib_spinlock_unlock (&vcm->workers_lock);
-      return wrk;
-    }
+done:
+  return wrk;
+}
+
+int
+vcl_worker_register_with_vpp (void)
+{
+  vcl_worker_t *wrk = vcl_worker_get_current ();
+
+  clib_spinlock_lock (&vcm->workers_lock);
 
   vcm->app_state = STATE_APP_ADDING_WORKER;
   vcl_send_app_worker_add_del (1 /* is_add */ );
   if (vcl_wait_for_app_state_change (STATE_APP_READY))
     {
       clib_warning ("failed to add worker to vpp");
-      return 0;
+      return -1;
     }
 
-  if (pthread_key_create (&vcl_worker_stop_key, vcl_worker_cleanup))
+  if (pthread_key_create (&vcl_worker_stop_key, vcl_worker_cleanup_cb))
     clib_warning ("failed to add pthread cleanup function");
   if (pthread_setspecific (vcl_worker_stop_key, &wrk->thread_id))
     clib_warning ("failed to setup key value");
-  wrk->thread_id = pthread_self ();
 
   clib_spinlock_unlock (&vcm->workers_lock);
 
   VDBG (0, "added worker %u", wrk->wrk_index);
+  return 0;
+}
 
-  return wrk;
+int
+vcl_worker_set_bapi (void)
+{
+  vcl_worker_t *wrk = vcl_worker_get_current ();
+  int i;
+
+  /* Find the first worker with the same pid */
+  for (i = 0; i < vec_len (vcm->workers); i++)
+    {
+      if (i == wrk->wrk_index)
+       continue;
+      if (vcm->workers[i].current_pid == wrk->current_pid)
+       {
+         wrk->vl_input_queue = vcm->workers[i].vl_input_queue;
+         wrk->my_client_index = vcm->workers[i].my_client_index;
+         return 0;
+       }
+    }
+  return -1;
+}
+
+vcl_shared_session_t *
+vcl_shared_session_alloc (void)
+{
+  vcl_shared_session_t *ss;
+  pool_get (vcm->shared_sessions, ss);
+  memset (ss, 0, sizeof (*ss));
+  ss->ss_index = ss - vcm->shared_sessions;
+  return ss;
+}
+
+vcl_shared_session_t *
+vcl_shared_session_get (u32 ss_index)
+{
+  if (pool_is_free_index (vcm->shared_sessions, ss_index))
+    return 0;
+  return pool_elt_at_index (vcm->shared_sessions, ss_index);
+}
+
+void
+vcl_shared_session_free (vcl_shared_session_t * ss)
+{
+  pool_put (vcm->shared_sessions, ss);
+}
+
+void
+vcl_worker_share_session (vcl_worker_t * parent, vcl_worker_t * wrk,
+                         vcl_session_t * new_s)
+{
+  vcl_shared_session_t *ss;
+  vcl_session_t *s;
+
+  s = vcl_session_get (parent, new_s->session_index);
+  if (s->shared_index == ~0)
+    {
+      ss = vcl_shared_session_alloc ();
+      vec_add1 (ss->workers, parent->wrk_index);
+      s->shared_index = ss->ss_index;
+    }
+  else
+    {
+      ss = vcl_shared_session_get (s->shared_index);
+    }
+  new_s->shared_index = ss->ss_index;
+  vec_add1 (ss->workers, wrk->wrk_index);
+}
+
+int
+vcl_worker_unshare_session (vcl_worker_t * wrk, vcl_session_t * s)
+{
+  vcl_shared_session_t *ss;
+  int i;
+
+  ss = vcl_shared_session_get (s->shared_index);
+  for (i = 0; i < vec_len (ss->workers); i++)
+    {
+      if (ss->workers[i] == wrk->wrk_index)
+       {
+         vec_del1 (ss->workers, i);
+         break;
+       }
+    }
+
+  if (vec_len (ss->workers) == 0)
+    {
+      vcl_shared_session_free (ss);
+      return 1;
+    }
+
+  return 0;
+}
+
+void
+vcl_worker_share_sessions (u32 parent_wrk_index)
+{
+  vcl_worker_t *parent_wrk, *wrk;
+  vcl_session_t *new_s;
+
+  parent_wrk = vcl_worker_get (parent_wrk_index);
+  if (!parent_wrk->sessions)
+    return;
+
+  wrk = vcl_worker_get_current ();
+  wrk->sessions = pool_dup (parent_wrk->sessions);
+  wrk->session_index_by_vpp_handles =
+    hash_dup (parent_wrk->session_index_by_vpp_handles);
+
+  /* *INDENT-OFF* */
+  pool_foreach (new_s, wrk->sessions, ({
+    vcl_worker_share_session (parent_wrk, wrk, new_s);
+  }));
+  /* *INDENT-ON* */
+}
+
+int
+vcl_session_get_refcnt (vcl_session_t * s)
+{
+  vcl_shared_session_t *ss;
+  ss = vcl_shared_session_get (s->shared_index);
+  if (ss)
+    return vec_len (ss->workers);
+  return 0;
 }
 
 /*
index 0204bd5..0420322 100644 (file)
@@ -136,6 +136,12 @@ do {                                            \
 #define VCL_SESS_ATTR_TEST(ATTR, VAL)           \
   ((ATTR) & (1 << (VAL)) ? 1 : 0)
 
+typedef struct vcl_shared_session_
+{
+  u32 ss_index;
+  u32 *workers;
+} vcl_shared_session_t;
+
 typedef struct
 {
   CLIB_CACHE_LINE_ALIGN_MARK (cacheline0);
@@ -161,6 +167,7 @@ typedef struct
   svm_msg_q_t *our_evt_q;
   u64 options[16];
   vcl_session_msg_t *accept_evts_fifo;
+  u32 shared_index;
 #if VCL_ELOG
   elog_track_t elog_track;
 #endif
@@ -225,6 +232,15 @@ typedef struct vcl_worker_
   /** Worker index in vpp*/
   u32 vpp_wrk_index;
 
+  /** API client handle */
+  u32 my_client_index;
+
+  /** State of the connection, shared between msg RX thread and main thread */
+  volatile app_state_t wrk_state;
+
+  /** VPP binary api input queue */
+  svm_queue_t *vl_input_queue;
+
   /** Message queues epoll fd. Initialized only if using mqs with eventfds */
   int mqs_epfd;
 
@@ -268,6 +284,9 @@ typedef struct vcl_worker_
 
   /** Used also as a thread stop key buffer */
   pthread_t thread_id;
+
+  /** Current pid, may be different from main_pid if forked child */
+  pid_t current_pid;
 } vcl_worker_t;
 
 typedef struct vppcom_main_t_
@@ -279,18 +298,9 @@ typedef struct vppcom_main_t_
   /** Main process pid */
   pid_t main_pid;
 
-  /** Current pid, may be different from main_pid if forked child */
-  pid_t current_pid;
-
   /** App's index in vpp. It's used by vpp to identify the app */
   u32 app_index;
 
-  /** API client handle */
-  u32 my_client_index;
-
-  /** VPP binary api input queue */
-  svm_queue_t *vl_input_queue;
-
   /** State of the connection, shared between msg RX thread and main thread */
   volatile app_state_t app_state;
 
@@ -302,12 +312,15 @@ typedef struct vppcom_main_t_
   /** Flag indicating that a new segment is being mounted */
   volatile u32 mounting_segment;
 
+  volatile u32 forking;
+
   /** Workers */
   vcl_worker_t *workers;
 
   /** Lock to protect worker registrations */
   clib_spinlock_t workers_lock;
 
+  vcl_shared_session_t *shared_sessions;
 #ifdef VCL_ELOG
   /* VPP Event-logger */
   elog_main_t elog_main;
@@ -330,6 +343,7 @@ vcl_session_alloc (vcl_worker_t * wrk)
   pool_get (wrk->sessions, s);
   memset (s, 0, sizeof (*s));
   s->session_index = s - wrk->sessions;
+  s->shared_index = ~0;
   return s;
 }
 
@@ -481,6 +495,12 @@ int vcl_mq_epoll_add_evfd (vcl_worker_t * wrk, svm_msg_q_t * mq);
 int vcl_mq_epoll_del_evfd (vcl_worker_t * wrk, u32 mqc_index);
 
 vcl_worker_t *vcl_worker_alloc_and_init (void);
+void vcl_worker_cleanup (void);
+int vcl_worker_register_with_vpp (void);
+int vcl_worker_set_bapi (void);
+void vcl_worker_share_sessions (u32 parent_wrk_index);
+int vcl_worker_unshare_session (vcl_worker_t * wrk, vcl_session_t * s);
+int vcl_session_get_refcnt (vcl_session_t * s);
 
 static inline vcl_worker_t *
 vcl_worker_get (u32 wrk_index)
index 589d57c..195e6cb 100644 (file)
@@ -423,7 +423,7 @@ vcl_session_reset_handler (vcl_worker_t * wrk,
   session->session_state = STATE_CLOSE_ON_EMPTY;
   VDBG (0, "reset handle 0x%llx, sid %u ", reset_msg->handle, sid);
   vcl_send_session_reset_reply (vcl_session_vpp_evt_q (wrk, session),
-                               vcm->my_client_index, reset_msg->handle, 0);
+                               wrk->my_client_index, reset_msg->handle, 0);
   return sid;
 }
 
@@ -673,7 +673,7 @@ vppcom_session_disconnect (u32 session_handle)
   if (state & STATE_CLOSE_ON_EMPTY)
     {
       vpp_evt_q = vcl_session_vpp_evt_q (wrk, session);
-      vcl_send_session_disconnected_reply (vpp_evt_q, vcm->my_client_index,
+      vcl_send_session_disconnected_reply (vpp_evt_q, wrk->my_client_index,
                                           vpp_handle, 0);
       VDBG (1, "VCL<%d>: vpp handle 0x%llx, sid %u: sending disconnect "
            "REPLY...", getpid (), vpp_handle, session_handle);
@@ -691,12 +691,14 @@ vppcom_session_disconnect (u32 session_handle)
 static void
 vcl_cleanup_bapi (void)
 {
+  socket_client_main_t *scm = &socket_client_main;
   api_main_t *am = &api_main;
 
   am->my_client_index = ~0;
   am->my_registration = 0;
   am->vl_input_queue = 0;
   am->msg_index_by_name_and_crc = 0;
+  scm->socket_fd = 0;
 
   vl_client_api_unmap ();
 }
@@ -705,14 +707,22 @@ void
 vcl_app_fork_child_handler (void)
 {
   u8 *child_name;
-  int rv;
-
-  vcm->current_pid = getpid ();
-  vcl_set_worker_index (0);
+  int rv, parent_wrk;
 
   VDBG (0, "initializing forked child");
-  child_name = format (0, "%v-child-%u%c", vcm->app_name, getpid (), 0);
 
+  /*
+   * Allocate worker
+   */
+  parent_wrk = vcl_get_worker_index ();
+  vcl_set_worker_index (~0);
+  if (!vcl_worker_alloc_and_init ())
+    VERR ("couldn't allocate new worker");
+
+  /*
+   * Attach to binary api
+   */
+  child_name = format (0, "%v-child-%u%c", vcm->app_name, getpid (), 0);
   vcl_cleanup_bapi ();
   vppcom_api_hookup ();
   vcm->app_state = STATE_APP_START;
@@ -724,14 +734,23 @@ vcl_app_fork_child_handler (void)
       return;
     }
 
-  vcm->app_state = STATE_APP_ADDING_WORKER;
-  vcl_send_app_worker_add_del (1 /* is_add */ );
-  if (vcl_wait_for_app_state_change (STATE_APP_READY))
-    {
-      VERR ("failed to add worker to vpp");
-      return;
-    }
+  /*
+   * Register worker with vpp and share sessions
+   */
+  vcl_worker_register_with_vpp ();
+  vcl_worker_share_sessions (parent_wrk);
+
   VDBG (0, "forked child main worker initialized");
+  vcm->forking = 0;
+}
+
+void
+vcl_app_fork_parent_handler (void)
+{
+  vcm->forking = 1;
+
+  while (vcm->forking)
+    ;
 }
 
 /*
@@ -743,55 +762,58 @@ vppcom_app_create (char *app_name)
   vppcom_cfg_t *vcl_cfg = &vcm->cfg;
   int rv;
 
-  if (!vcm->is_init)
+  if (vcm->is_init)
     {
-      vcm->is_init = 1;
-      vppcom_cfg (&vcm->cfg);
-      vcl_cfg = &vcm->cfg;
-
-      vcm->main_cpu = pthread_self ();
-      vcm->main_pid = vcm->current_pid = getpid ();
-      vcm->app_name = format (0, "%s", app_name);
-      vppcom_init_error_string_table ();
-      svm_fifo_segment_main_init (vcl_cfg->segment_baseva,
-                                 20 /* timeout in secs */ );
-      pool_init_fixed (vcm->workers, vcl_cfg->max_workers);
-      clib_spinlock_init (&vcm->workers_lock);
-      vcl_worker_alloc_and_init ();
-      pthread_atfork (NULL, NULL, vcl_app_fork_child_handler);
+      clib_warning ("already initialized");
+      return -1;
     }
 
-  if (vcm->my_client_index == ~0)
-    {
-      /* API hookup and connect to VPP */
-      vppcom_api_hookup ();
-      vcl_elog_init (vcm);
-      vcm->app_state = STATE_APP_START;
-      rv = vppcom_connect_to_vpp (app_name);
-      if (rv)
-       {
-         VERR ("couldn't connect to VPP!");
-         return rv;
-       }
-      VDBG (0, "sending session enable");
-      rv = vppcom_app_session_enable ();
-      if (rv)
-       {
-         VERR ("vppcom_app_session_enable() failed!");
-         return rv;
-       }
+  vcm->is_init = 1;
+  vppcom_cfg (&vcm->cfg);
+  vcl_cfg = &vcm->cfg;
 
-      VDBG (0, "sending app attach");
-      rv = vppcom_app_attach ();
-      if (rv)
-       {
-         VERR ("vppcom_app_attach() failed!");
-         return rv;
-       }
+  vcm->main_cpu = pthread_self ();
+  vcm->main_pid = getpid ();
+  vcm->app_name = format (0, "%s", app_name);
+  vppcom_init_error_string_table ();
+  svm_fifo_segment_main_init (vcl_cfg->segment_baseva,
+                             20 /* timeout in secs */ );
+  pool_alloc (vcm->workers, vcl_cfg->max_workers);
+  clib_spinlock_init (&vcm->workers_lock);
+  pthread_atfork (NULL, vcl_app_fork_parent_handler,
+                 vcl_app_fork_child_handler);
 
-      VDBG (0, "app_name '%s', my_client_index %d (0x%x)",
-           app_name, vcm->my_client_index, vcm->my_client_index);
+  /* Allocate default worker */
+  vcl_worker_alloc_and_init ();
+
+  /* API hookup and connect to VPP */
+  vppcom_api_hookup ();
+  vcl_elog_init (vcm);
+  vcm->app_state = STATE_APP_START;
+  rv = vppcom_connect_to_vpp (app_name);
+  if (rv)
+    {
+      VERR ("couldn't connect to VPP!");
+      return rv;
     }
+  VDBG (0, "sending session enable");
+  rv = vppcom_app_session_enable ();
+  if (rv)
+    {
+      VERR ("vppcom_app_session_enable() failed!");
+      return rv;
+    }
+
+  VDBG (0, "sending app attach");
+  rv = vppcom_app_attach ();
+  if (rv)
+    {
+      VERR ("vppcom_app_attach() failed!");
+      return rv;
+    }
+
+  VDBG (0, "app_name '%s', my_client_index %d (0x%x)", app_name,
+       vcm->workers[0].my_client_index, vcm->workers[0].my_client_index);
 
   return VPPCOM_OK;
 }
@@ -802,27 +824,27 @@ vppcom_app_destroy (void)
   int rv;
   f64 orig_app_timeout;
 
-  if (vcm->my_client_index == ~0)
-    return;
-
-  VDBG (0, "detaching from VPP, my_client_index %d (0x%x)",
-       vcm->my_client_index, vcm->my_client_index);
   vcl_evt (VCL_EVT_DETACH, vcm);
 
-  vppcom_app_send_detach ();
-  orig_app_timeout = vcm->cfg.app_timeout;
-  vcm->cfg.app_timeout = 2.0;
-  rv = vcl_wait_for_app_state_change (STATE_APP_ENABLED);
-  vcm->cfg.app_timeout = orig_app_timeout;
-  if (PREDICT_FALSE (rv))
-    VDBG (0, "application detach timed out! returning %d (%s)",
-         rv, vppcom_retval_str (rv));
+  if (vec_len (vcm->workers) == 1)
+    {
+      vppcom_app_send_detach ();
+      orig_app_timeout = vcm->cfg.app_timeout;
+      vcm->cfg.app_timeout = 2.0;
+      rv = vcl_wait_for_app_state_change (STATE_APP_ENABLED);
+      vcm->cfg.app_timeout = orig_app_timeout;
+      if (PREDICT_FALSE (rv))
+       VDBG (0, "application detach timed out! returning %d (%s)", rv,
+             vppcom_retval_str (rv));
+    }
+  else
+    {
+      vcl_worker_cleanup ();
+    }
 
   vcl_elog_stop (vcm);
   vl_client_disconnect_from_vlib ();
   vec_free (vcm->app_name);
-  vcm->my_client_index = ~0;
-  vcm->app_state = STATE_APP_START;
 }
 
 int
@@ -853,8 +875,8 @@ int
 vppcom_session_close (uint32_t session_handle)
 {
   vcl_worker_t *wrk = vcl_worker_get_current ();
+  u8 is_vep, do_disconnect = 1;
   vcl_session_t *session = 0;
-  u8 is_vep, is_vep_session;
   session_state_t state;
   u32 next_sh, vep_sh;
   int rv = VPPCOM_OK;
@@ -864,24 +886,17 @@ vppcom_session_close (uint32_t session_handle)
   if (!session)
     return VPPCOM_EBADFD;
 
+  if (session->shared_index != ~0)
+    do_disconnect = vcl_worker_unshare_session (wrk, session);
+
   is_vep = session->is_vep;
-  is_vep_session = session->is_vep_session;
   next_sh = session->vep.next_sh;
   vep_sh = session->vep.vep_sh;
   state = session->session_state;
   vpp_handle = session->vpp_handle;
 
-  if (VPPCOM_DEBUG > 0)
-    {
-      if (is_vep)
-       clib_warning ("VCL<%d>: vep_idx %u / sid %u: "
-                     "closing epoll session...",
-                     getpid (), session_handle, session_handle);
-      else
-       clib_warning ("VCL<%d>: vpp handle 0x%llx, sid %d: "
-                     "closing session...",
-                     getpid (), vpp_handle, session_handle);
-    }
+  VDBG (0, "Closing session handle %u vpp handle %u", session_handle,
+       vpp_handle);
 
   if (is_vep)
     {
@@ -889,34 +904,34 @@ vppcom_session_close (uint32_t session_handle)
        {
          rv = vppcom_epoll_ctl (session_handle, EPOLL_CTL_DEL, next_sh, 0);
          if (PREDICT_FALSE (rv < 0))
-           VDBG (0, "VCL<%d>: vpp handle 0x%llx, sid %u: EPOLL_CTL_DEL "
-                 "vep_idx %u failed! rv %d (%s)",
-                 getpid (), vpp_handle, next_sh, vep_sh,
-                 rv, vppcom_retval_str (rv));
+           VDBG (0, "vpp handle 0x%llx, sid %u: EPOLL_CTL_DEL vep_idx %u"
+                 " failed! rv %d (%s)", vpp_handle, next_sh, vep_sh, rv,
+                 vppcom_retval_str (rv));
 
          next_sh = session->vep.next_sh;
        }
     }
   else
     {
-      if (is_vep_session)
+      if (session->is_vep_session)
        {
          rv = vppcom_epoll_ctl (vep_sh, EPOLL_CTL_DEL, session_handle, 0);
          if (rv < 0)
-           VDBG (0, "VCL<%d>: vpp handle 0x%llx, sid %u: EPOLL_CTL_DEL "
-                 "vep_idx %u failed! rv %d (%s)",
-                 getpid (), vpp_handle, session_handle,
-                 vep_sh, rv, vppcom_retval_str (rv));
+           VDBG (0, "vpp handle 0x%llx, sid %u: EPOLL_CTL_DEL vep_idx %u "
+                 "failed! rv %d (%s)", vpp_handle, session_handle, vep_sh,
+                 rv, vppcom_retval_str (rv));
        }
 
+      if (!do_disconnect)
+       goto cleanup;
+
       if (state & STATE_LISTEN)
        {
          rv = vppcom_session_unbind (session_handle);
          if (PREDICT_FALSE (rv < 0))
-           VDBG (0, "VCL<%d>: vpp handle 0x%llx, sid %u: listener unbind "
-                 "failed! rv %d (%s)",
-                 getpid (), vpp_handle, session_handle,
-                 rv, vppcom_retval_str (rv));
+           VDBG (0, "vpp handle 0x%llx, sid %u: listener unbind failed! "
+                 "rv %d (%s)", vpp_handle, session_handle, rv,
+                 vppcom_retval_str (rv));
        }
       else if (state & STATE_OPEN)
        {
@@ -929,6 +944,8 @@ vppcom_session_close (uint32_t session_handle)
        }
     }
 
+cleanup:
+
   if (vcl_session_is_ct (session))
     {
       vcl_cut_through_registration_t *ctr;
@@ -952,15 +969,8 @@ vppcom_session_close (uint32_t session_handle)
     }
   vcl_session_free (wrk, session);
 
-  if (VPPCOM_DEBUG > 0)
-    {
-      if (is_vep)
-       clib_warning ("VCL<%d>: vep_idx %u / sid %u: epoll session removed.",
-                     getpid (), session_handle, session_handle);
-      else
-       clib_warning ("VCL<%d>: vpp handle 0x%llx, sid %u: session removed.",
-                     getpid (), vpp_handle, session_handle);
-    }
+  VDBG (0, "session handle %u vpp handle %u removed", session_handle,
+       vpp_handle);
 
   vcl_evt (VCL_EVT_CLOSE, session, rv);
 
@@ -1986,9 +1996,9 @@ vppcom_select (unsigned long n_bits, unsigned long *read_map,
   clib_bitmap_foreach (sid, wrk->wr_bitmap, ({
     if (!(session = vcl_session_get (wrk, sid)))
       {
-        VDBG (0, "VCL<%d>: session %d specified in write_map is closed.",
-              getpid (), sid);
-        return VPPCOM_EBADFD;
+        if (except_map && sid < minbits)
+          clib_bitmap_set_no_check (except_map, sid, 1);
+        continue;
       }
 
     rv = svm_fifo_is_full (session->tx_fifo);
@@ -2006,9 +2016,9 @@ check_rd:
   clib_bitmap_foreach (sid, wrk->rd_bitmap, ({
     if (!(session = vcl_session_get (wrk, sid)))
       {
-        VDBG (0, "VCL<%d>: session %d specified in write_map is closed.",
-              getpid (), sid);
-        return VPPCOM_EBADFD;
+        if (except_map && sid < minbits)
+          clib_bitmap_set_no_check (except_map, sid, 1);
+        continue;
       }
 
     rv = vppcom_session_read_ready (session);
@@ -3150,6 +3160,10 @@ vppcom_session_attr (uint32_t session_handle, uint32_t op,
        rv = VPPCOM_EINVAL;
       break;
 
+    case VPPCOM_ATTR_GET_REFCNT:
+      rv = vcl_session_get_refcnt (session);
+      break;
+
     default:
       rv = VPPCOM_EINVAL;
       break;
@@ -3355,15 +3369,22 @@ vppcom_session_index (uint32_t session_handle)
 int
 vppcom_session_handle (uint32_t session_index)
 {
-  return vcl_get_worker_index () << 24 | session_index;
+  return (vcl_get_worker_index () << 24) | session_index;
 }
 
 int
 vppcom_worker_register (void)
 {
-  if (vcl_worker_alloc_and_init ())
-    return VPPCOM_OK;
-  return VPPCOM_EEXIST;
+  if (!vcl_worker_alloc_and_init ())
+    return VPPCOM_EEXIST;
+
+  if (vcl_worker_set_bapi ())
+    return VPPCOM_EEXIST;
+
+  if (vcl_worker_register_with_vpp ())
+    return VPPCOM_EEXIST;
+
+  return VPPCOM_OK;
 }
 
 int
index 0c0b7ce..30ab7c4 100644 (file)
@@ -137,6 +137,7 @@ typedef enum
   VPPCOM_ATTR_SET_TCP_KEEPINTVL,
   VPPCOM_ATTR_GET_TCP_USER_MSS,
   VPPCOM_ATTR_SET_TCP_USER_MSS,
+  VPPCOM_ATTR_GET_REFCNT,
 } vppcom_attr_op_t;
 
 typedef struct _vcl_poll
index 9b77af9..380c960 100644 (file)
@@ -2379,7 +2379,7 @@ show_app_command_fn (vlib_main_t * vm, unformat_input_t * input,
 
   if (app_index != ~0)
     {
-      app = application_get (app_index);
+      app = application_get_if_valid (app_index);
       if (!app)
        return clib_error_return (0, "No app with index %u", app_index);
 
index 47bae07..10262e9 100644 (file)
@@ -373,7 +373,7 @@ do {                                                                        \
  * @param P pool to copy
  * @return copy of pool
  */
-#define pool_dup(P) pool_dup_aligned(P, 0)
+#define pool_dup(P) pool_dup_aligned(P,0)
 
 /** Low-level free pool operator (do not call directly). */
 always_inline void *