vcl/session: apps with process workers

Allow apps to register child processes as app workers. In particular,
on fork vcl now registers the child process with vpp as a new worker.

Change-Id: I52a65fbc3292962b1f6e1fe0f6153f739e6e0d4a
Signed-off-by: Florin Coras <fcoras@cisco.com>
diff --git a/src/vnet/session/application.c b/src/vnet/session/application.c
index 1d7b3ad..72c5768 100644
--- a/src/vnet/session/application.c
+++ b/src/vnet/session/application.c
@@ -112,11 +112,11 @@
   return app_name;
 }
 
-static u8 *
+static const u8 *
 app_get_name (application_t * app)
 {
   if (!app->name)
-    return app_get_name_from_reg_index (app);
+    app->name = app_get_name_from_reg_index (app);
   return app->name;
 }
 
@@ -174,18 +174,27 @@
 }
 
 /**
- * Returns app name
- *
- * Since the name is not stored per app, we generate it on the fly. It is
- * the caller's responsibility to free the vector
+ * Returns app name for app-index
  */
-u8 *
+const u8 *
 application_name_from_index (u32 app_index)
 {
   application_t *app = application_get (app_index);
   if (!app)
     return 0;
-  return app_get_name_from_reg_index (app);
+  return app_get_name (app);
+}
+
+static void
+application_api_table_add (u32 app_index, u32 api_client_index)
+{
+  hash_set (app_main.app_by_api_client_index, api_client_index, app_index);
+}
+
+static void
+application_api_table_del (u32 api_client_index)
+{
+  hash_unset (app_main.app_by_api_client_index, api_client_index);
 }
 
 static void
@@ -213,7 +222,7 @@
   uword *p;
   p = hash_get (app_main.app_by_api_client_index, api_client_index);
   if (p)
-    return application_get (p[0]);
+    return application_get_if_valid (p[0]);
 
   return 0;
 }
@@ -440,6 +449,50 @@
   pool_put (app_main.app_pool, app);
 }
 
+void
+application_detach_process (application_t * app, u32 api_client_index)
+{
+  vnet_app_worker_add_del_args_t _args = { 0 }, *args = &_args;
+  app_worker_map_t *wrk_map;
+  u32 *wrks = 0, *wrk_index;
+  app_worker_t *app_wrk;
+
+  if (api_client_index == ~0)
+    {
+      application_free (app);
+      return;
+    }
+
+  APP_DBG ("Detaching for app %v index %u api client index %u", app->name,
+	   app->app_index, app->api_client_index);
+
+  /* *INDENT-OFF* */
+  pool_foreach (wrk_map, app->worker_maps, ({
+    app_wrk = app_worker_get (wrk_map->wrk_index);
+    if (app_wrk->api_index == api_client_index)
+      vec_add1 (wrks, app_wrk->wrk_index);
+  }));
+  /* *INDENT-ON* */
+
+  if (!vec_len (wrks))
+    {
+      clib_warning ("no workers for app %u api_index %u", app->app_index,
+		    api_client_index);
+      return;
+    }
+
+  args->app_index = app->app_index;
+  args->api_index = api_client_index;
+  vec_foreach (wrk_index, wrks)
+  {
+    app_wrk = app_worker_get (wrk_index[0]);
+    args->wrk_index = app_wrk->wrk_map_index;
+    args->is_add = 0;
+    vnet_app_worker_add_del (args);
+  }
+  vec_free (wrks);
+}
+
 app_worker_t *
 application_get_worker (application_t * app, u32 wrk_map_index)
 {
@@ -456,6 +509,12 @@
   return application_get_worker (app, 0);
 }
 
+u32
+application_n_workers (application_t * app)
+{
+  return pool_elts (app->worker_maps);
+}
+
 app_worker_t *
 application_listener_select_worker (stream_session_t * ls, u8 is_local)
 {
@@ -609,6 +668,7 @@
   app_wrk->listeners_table = hash_create (0, sizeof (u64));
   app_wrk->event_queue = segment_manager_event_queue (sm);
   app_wrk->app_is_builtin = application_is_builtin (app);
+  app_wrk->api_index = app->api_client_index;
 
   /*
    * Segment manager for local sessions
@@ -815,7 +875,8 @@
   app_listener = app_listener_get (app, listener->listener_db_index);
   if (!clib_bitmap_get (app_listener->workers, app_wrk_index))
     {
-      clib_warning ("worker not listening on handle %lu", handle);
+      clib_warning ("worker %u not listening on handle %lu", app_wrk_index,
+		    handle);
       return 0;
     }
 
@@ -899,6 +960,15 @@
     {
       if ((rv = app_worker_alloc_and_init (app, &app_wrk)))
 	return clib_error_return_code (0, rv, 0, "app wrk init: %d", rv);
+
+      /* Map worker api index to the app */
+      if (a->api_index != app->api_client_index
+	  && app->api_client_index != APP_INVALID_INDEX)
+	{
+	  app_wrk->api_index = a->api_index;
+	  application_api_table_add (app->app_index, a->api_index);
+	}
+
       sm = segment_manager_get (app_wrk->first_segment_manager);
       fs = segment_manager_get_segment_w_lock (sm, 0);
       a->segment = &fs->ssvm;
@@ -914,11 +984,15 @@
 				       "App %u does not have worker %u",
 				       app->app_index, a->wrk_index);
       app_wrk = app_worker_get (wrk_map->wrk_index);
-      app_worker_map_free (app, wrk_map);
       if (!app_wrk)
 	return clib_error_return_code (0, VNET_API_ERROR_INVALID_VALUE, 0,
 				       "No worker %u", a->wrk_index);
+      if (app->api_client_index != app_wrk->api_index)
+	application_api_table_del (app_wrk->api_index);
       app_worker_free (app_wrk);
+      app_worker_map_free (app, wrk_map);
+      if (application_n_workers (app) == 0)
+	application_free (app);
     }
   return 0;
 }
@@ -1916,7 +1990,8 @@
   int verbose = va_arg (*args, int);
   stream_session_t *listener;
   application_t *app;
-  u8 *app_name, *str;
+  const u8 *app_name;
+  u8 *str;
 
   if (!app_wrk)
     {
@@ -1930,7 +2005,7 @@
     }
 
   app = application_get (app_wrk->app_index);
-  app_name = app_get_name_from_reg_index (app);
+  app_name = app_get_name (app);
   listener = listen_session_get_from_handle (handle);
   str = format (0, "%U", format_stream_session, listener, verbose);
 
@@ -1944,7 +2019,6 @@
   else
     s = format (s, "%-40s%-25s%=10u", str, app_name, app_wrk->wrk_map_index);
 
-  vec_free (app_name);
   return s;
 }
 
@@ -1983,8 +2057,9 @@
   svm_fifo_segment_private_t *fifo_segment;
   vlib_main_t *vm = vlib_get_main ();
   segment_manager_t *sm;
-  u8 *app_name, *s = 0;
+  const u8 *app_name;
   application_t *app;
+  u8 *s = 0;
 
   /* Header */
   if (!app_wrk)
@@ -2001,7 +2076,7 @@
   if (app_wrk->connects_seg_manager == (u32) ~ 0)
     return;
 
-  app_name = app_get_name_from_reg_index (app);
+  app_name = app_get_name (app);
 
   /* Across all fifo segments */
   sm = segment_manager_get (app_wrk->connects_seg_manager);
@@ -2039,7 +2114,6 @@
   }));
   /* *INDENT-ON* */
 
-  vec_free (app_name);
 }
 
 static void
@@ -2200,8 +2274,7 @@
   application_t *app = va_arg (*args, application_t *);
   CLIB_UNUSED (int verbose) = va_arg (*args, int);
   segment_manager_properties_t *props;
-  const u8 *app_ns_name;
-  u8 *app_name;
+  const u8 *app_ns_name, *app_name;
 
   if (app == 0)
     {
diff --git a/src/vnet/session/application.h b/src/vnet/session/application.h
index 3888cf7..9c68433 100644
--- a/src/vnet/session/application.h
+++ b/src/vnet/session/application.h
@@ -101,6 +101,9 @@
   /** Hash table of the app's local connects */
   uword *local_connects;
 
+  /** API index for the worker. Needed for multi-process apps */
+  u32 api_index;
+
   u8 app_is_builtin;
 } app_worker_t;
 
@@ -121,7 +124,8 @@
   /** App index in app pool */
   u32 app_index;
 
-  /** Binary API connection index, ~0 if internal */
+  /** Binary API connection index of app process that created the app,
+   *  ~0 if internal */
   u32 api_client_index;
 
   /** Flags */
@@ -210,6 +214,7 @@
 {
   u32 app_index;		/**< App for which a new worker is requested */
   u32 wrk_index;		/**< Index to delete or return value if add */
+  u32 api_index;		/**< Binary API client index */
   ssvm_private_t *segment;	/**< First segment in segment manager */
   svm_msg_q_t *evt_q;		/**< Worker message queue */
   u8 is_add;			/**< Flag set if addition */
@@ -251,6 +256,7 @@
 application_t *application_alloc (void);
 int application_alloc_and_init (app_init_args_t * args);
 void application_free (application_t * app);
+void application_detach_process (application_t * app, u32 api_client_index);
 application_t *application_get (u32 index);
 application_t *application_get_if_valid (u32 index);
 application_t *application_lookup (u32 api_client_index);
@@ -268,7 +274,7 @@
 int application_is_builtin_proxy (application_t * app);
 u32 application_session_table (application_t * app, u8 fib_proto);
 u32 application_local_session_table (application_t * app);
-u8 *application_name_from_index (u32 app_or_wrk);
+const u8 *application_name_from_index (u32 app_or_wrk);
 u8 application_has_local_scope (application_t * app);
 u8 application_has_global_scope (application_t * app);
 u8 application_use_mq_for_ctrl (application_t * app);
diff --git a/src/vnet/session/application_interface.c b/src/vnet/session/application_interface.c
index 89f7462..3cd8c2f 100644
--- a/src/vnet/session/application_interface.c
+++ b/src/vnet/session/application_interface.c
@@ -554,7 +554,7 @@
     }
 
   app_interface_check_thread_and_barrier (vnet_application_detach, a);
-  application_free (app);
+  application_detach_process (app, a->api_client_index);
   return 0;
 }
 
diff --git a/src/vnet/session/application_interface.h b/src/vnet/session/application_interface.h
index dc4c469..10edfde 100644
--- a/src/vnet/session/application_interface.h
+++ b/src/vnet/session/application_interface.h
@@ -33,6 +33,7 @@
 typedef struct _vnet_app_detach_args_t
 {
   u32 app_index;
+  u32 api_client_index;
 } vnet_app_detach_args_t;
 
 typedef struct _vnet_bind_args_t
diff --git a/src/vnet/session/session_api.c b/src/vnet/session/session_api.c
index 8315c19..5887701 100755
--- a/src/vnet/session/session_api.c
+++ b/src/vnet/session/session_api.c
@@ -856,6 +856,7 @@
   if (app)
     {
       a->app_index = app->app_index;
+      a->api_client_index = mp->client_index;
       rv = vnet_application_detach (a);
     }
 
@@ -1359,6 +1360,7 @@
   vnet_app_worker_add_del_args_t args = {
     .app_index = app->app_index,
     .wrk_index = clib_net_to_host_u32 (mp->wrk_index),
+    .api_index = mp->client_index,
     .is_add = mp->is_add
   };
   error = vnet_app_worker_add_del (&args);
@@ -1732,6 +1734,7 @@
   if (app)
     {
       a->app_index = app->app_index;
+      a->api_client_index = client_index;
       vnet_application_detach (a);
     }
   return 0;
diff --git a/src/vnet/session/session_cli.c b/src/vnet/session/session_cli.c
index 651abe1..3fe4991 100755
--- a/src/vnet/session/session_cli.c
+++ b/src/vnet/session/session_cli.c
@@ -205,10 +205,12 @@
 			 vlib_cli_command_t * cmd)
 {
   session_manager_main_t *smm = &session_manager_main;
-  u8 *str = 0, one_session = 0, do_listeners = 0, sst, *app_name;
-  int verbose = 0, i;
+  u8 *str = 0, one_session = 0, do_listeners = 0, sst;
   stream_session_t *pool, *s;
   u32 transport_proto = ~0;
+  app_worker_t *app_wrk;
+  int verbose = 0, i;
+  const u8 *app_name;
 
   if (!smm->is_enabled)
     {
@@ -248,10 +250,10 @@
 	if (s->session_state != SESSION_STATE_LISTENING
 	    || s->session_type != sst)
 	  continue;
-	app_name = application_name_from_index (s->app_wrk_index);
+	app_wrk = app_worker_get (s->app_wrk_index);
+	app_name = application_name_from_index (app_wrk->app_index);
 	vlib_cli_output (vm, "%U%-25v%", format_stream_session, s, 1,
 			 app_name);
-	vec_free (app_name);
       }));
       /* *INDENT-ON* */
       return 0;
diff --git a/src/vnet/session/session_lookup.c b/src/vnet/session/session_lookup.c
index 927922e..c410dab 100644
--- a/src/vnet/session/session_lookup.c
+++ b/src/vnet/session/session_lookup.c
@@ -1302,9 +1302,10 @@
   clib_bihash_kv_16_8_t *kvp = va_arg (*args, clib_bihash_kv_16_8_t *);
   u32 is_local = va_arg (*args, u32), app_wrk_index, session_index;
   v4_connection_key_t *key = (v4_connection_key_t *) kvp->key;
-  u8 *app_name, *str = 0;
   stream_session_t *session;
   app_worker_t *app_wrk;
+  const u8 *app_name;
+  u8 *str = 0;
 
   if (!is_local)
     {
@@ -1327,7 +1328,6 @@
 		    clib_net_to_host_u16 (key->src_port));
       s = format (s, "%-30v%-30v", str, app_name);
     }
-  vec_free (app_name);
   return s;
 }