+ /* Add app to lookup by api_client_index table */
+ if (!application_is_builtin (app))
+ application_api_table_add (app->app_index, a->api_client_index);
+ else
+ application_name_table_add (app);
+
+ a->app_index = app->app_index;
+
+ APP_DBG ("New app name: %v api index: %u index %u", app->name,
+ app->api_client_index, app->app_index);
+
+ return 0;
+}
+
+void
+application_free (application_t * app)
+{
+ app_worker_map_t *wrk_map;
+ app_worker_t *app_wrk;
+ u32 table_index;
+ local_session_t *ll;
+ session_endpoint_t sep;
+
+ /*
+ * The app event queue allocated in first segment is cleared with
+ * the segment manager. No need to explicitly free it.
+ */
+ APP_DBG ("Delete app name %v api index: %d index: %d", app->name,
+ app->api_client_index, app->app_index);
+
+ if (application_is_proxy (app))
+ application_remove_proxy (app);
+
+ /*
+ * Free workers
+ */
+
+ /* *INDENT-OFF* */
+ pool_flush (wrk_map, app->worker_maps, ({
+ app_wrk = app_worker_get (wrk_map->wrk_index);
+ app_worker_free (app_wrk);
+ }));
+ /* *INDENT-ON* */
+ pool_free (app->worker_maps);
+
+ /*
+ * Free local listeners. Global table unbinds stop local listeners
+ * as well, but if we have only local binds, these won't be cleaned up.
+ * Don't bother with local accepted sessions, we clean them when
+ * cleaning up the worker.
+ */
+ table_index = application_local_session_table (app);
+ /* *INDENT-OFF* */
+ pool_foreach (ll, app->local_listen_sessions, ({
+ application_local_listener_session_endpoint (ll, &sep);
+ session_lookup_del_session_endpoint (table_index, &sep);
+ }));
+ /* *INDENT-ON* */
+ pool_free (app->local_listen_sessions);
+
+ /*
+ * Cleanup remaining state
+ */
+ if (application_is_builtin (app))
+ application_name_table_del (app);
+ vec_free (app->name);
+ vec_free (app->tls_cert);
+ vec_free (app->tls_key);
+ 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_client_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_client_index = api_client_index;
+ vec_foreach (wrk_index, wrks)
+ {
+ app_wrk = app_worker_get (wrk_index[0]);
+ args->wrk_map_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)
+{
+ app_worker_map_t *map;
+ map = app_worker_map_get (app, wrk_map_index);
+ if (!map)
+ return 0;
+ return app_worker_get (map->wrk_index);
+}
+
+app_worker_t *
+application_get_default_worker (application_t * app)
+{
+ 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)
+{
+ app_listener_t *app_listener;
+ application_t *app;
+ u32 wrk_index;
+
+ app = application_get (ls->app_index);
+ if (!is_local)
+ app_listener = app_listener_get (app, ls->listener_db_index);
+ else
+ app_listener = app_local_listener_get (app, ls->listener_db_index);
+
+ wrk_index = clib_bitmap_next_set (app_listener->workers,
+ app_listener->accept_rotor + 1);
+ if (wrk_index == ~0)
+ wrk_index = clib_bitmap_first_set (app_listener->workers);
+
+ ASSERT (wrk_index != ~0);
+ app_listener->accept_rotor = wrk_index;
+ return application_get_worker (app, wrk_index);
+}
+
+app_worker_t *
+app_worker_alloc (application_t * app)
+{
+ app_worker_t *app_wrk;
+ pool_get (app_main.workers, app_wrk);
+ clib_memset (app_wrk, 0, sizeof (*app_wrk));
+ app_wrk->wrk_index = app_wrk - app_main.workers;
+ app_wrk->app_index = app->app_index;
+ app_wrk->wrk_map_index = ~0;
+ app_wrk->connects_seg_manager = APP_INVALID_SEGMENT_MANAGER_INDEX;
+ app_wrk->first_segment_manager = APP_INVALID_SEGMENT_MANAGER_INDEX;
+ app_wrk->local_segment_manager = APP_INVALID_SEGMENT_MANAGER_INDEX;
+ APP_DBG ("New app %v worker %u", app_get_name (app), app_wrk->wrk_index);
+ return app_wrk;
+}
+
+app_worker_t *
+app_worker_get (u32 wrk_index)
+{
+ return pool_elt_at_index (app_main.workers, wrk_index);
+}
+
+app_worker_t *
+app_worker_get_if_valid (u32 wrk_index)
+{
+ if (pool_is_free_index (app_main.workers, wrk_index))
+ return 0;
+ return pool_elt_at_index (app_main.workers, wrk_index);
+}
+
+void
+app_worker_free (app_worker_t * app_wrk)
+{
+ application_t *app = application_get (app_wrk->app_index);
+ vnet_unbind_args_t _a, *a = &_a;
+ u64 handle, *handles = 0;
+ segment_manager_t *sm;
+ u32 sm_index;
+ int i;
+
+ /*
+ * Listener cleanup
+ */
+
+ /* *INDENT-OFF* */
+ hash_foreach (handle, sm_index, app_wrk->listeners_table,
+ ({
+ vec_add1 (handles, handle);
+ sm = segment_manager_get (sm_index);
+ sm->app_wrk_index = SEGMENT_MANAGER_INVALID_APP_INDEX;
+ }));
+ /* *INDENT-ON* */
+
+ for (i = 0; i < vec_len (handles); i++)
+ {
+ a->app_index = app->app_index;
+ a->wrk_map_index = app_wrk->wrk_map_index;
+ a->handle = handles[i];
+ /* seg manager is removed when unbind completes */
+ vnet_unbind (a);
+ }
+
+ /*
+ * Connects segment manager cleanup
+ */
+
+ if (app_wrk->connects_seg_manager != APP_INVALID_SEGMENT_MANAGER_INDEX)
+ {
+ sm = segment_manager_get (app_wrk->connects_seg_manager);
+ sm->app_wrk_index = SEGMENT_MANAGER_INVALID_APP_INDEX;
+ segment_manager_init_del (sm);
+ }
+
+ /* If first segment manager is used by a listener */
+ if (app_wrk->first_segment_manager != APP_INVALID_SEGMENT_MANAGER_INDEX
+ && app_wrk->first_segment_manager != app_wrk->connects_seg_manager)
+ {
+ sm = segment_manager_get (app_wrk->first_segment_manager);
+ sm->first_is_protected = 0;
+ sm->app_wrk_index = SEGMENT_MANAGER_INVALID_APP_INDEX;
+ /* .. and has no fifos, e.g. it might be used for redirected sessions,
+ * remove it */
+ if (!segment_manager_has_fifos (sm))
+ segment_manager_del (sm);
+ }
+
+ /*
+ * Local sessions
+ */
+ app_worker_local_sessions_free (app_wrk);
+
+ pool_put (app_main.workers, app_wrk);
+ if (CLIB_DEBUG)
+ clib_memset (app_wrk, 0xfe, sizeof (*app_wrk));
+}