+ /*
+ * Metadata closed. Even on error this means that the consumer is not
+ * responding or not found so either way a second close should NOT be emit
+ * for this registry.
+ */
+ registry->metadata_closed = 1;
+end:
+ rcu_read_unlock();
+ return ret;
+}
+
+/*
+ * We need to execute ht_destroy outside of RCU read-side critical
+ * section and outside of call_rcu thread, so we postpone its execution
+ * using ht_cleanup_push. It is simpler than to change the semantic of
+ * the many callers of delete_ust_app_session().
+ */
+static
+void delete_ust_app_session_rcu(struct rcu_head *head)
+{
+ struct ust_app_session *ua_sess =
+ caa_container_of(head, struct ust_app_session, rcu_head);
+
+ ht_cleanup_push(ua_sess->channels);
+ free(ua_sess);
+}
+
+/*
+ * Delete ust app session safely. RCU read lock must be held before calling
+ * this function.
+ */
+static
+void delete_ust_app_session(int sock, struct ust_app_session *ua_sess,
+ struct ust_app *app)
+{
+ int ret;
+ struct lttng_ht_iter iter;
+ struct ust_app_channel *ua_chan;
+ struct ust_registry_session *registry;
+
+ assert(ua_sess);
+
+ pthread_mutex_lock(&ua_sess->lock);
+
+ registry = get_session_registry(ua_sess);
+ if (registry && !registry->metadata_closed) {
+ /* Push metadata for application before freeing the application. */
+ (void) push_metadata(registry, ua_sess->consumer);
+
+ /*
+ * Don't ask to close metadata for global per UID buffers. Close
+ * metadata only on destroy trace session in this case. Also, the
+ * previous push metadata could have flag the metadata registry to
+ * close so don't send a close command if closed.
+ */
+ if (ua_sess->buffer_type != LTTNG_BUFFER_PER_UID &&
+ !registry->metadata_closed) {
+ /* And ask to close it for this session registry. */
+ (void) close_metadata(registry, ua_sess->consumer);
+ }
+ }
+
+ cds_lfht_for_each_entry(ua_sess->channels->ht, &iter.iter, ua_chan,
+ node.node) {
+ ret = lttng_ht_del(ua_sess->channels, &iter);
+ assert(!ret);
+ delete_ust_app_channel(sock, ua_chan, app);
+ }
+
+ /* In case of per PID, the registry is kept in the session. */
+ if (ua_sess->buffer_type == LTTNG_BUFFER_PER_PID) {
+ struct buffer_reg_pid *reg_pid = buffer_reg_pid_find(ua_sess->id);
+ if (reg_pid) {
+ buffer_reg_pid_remove(reg_pid);
+ buffer_reg_pid_destroy(reg_pid);
+ }
+ }
+
+ if (ua_sess->handle != -1) {
+ ret = ustctl_release_handle(sock, ua_sess->handle);
+ if (ret < 0 && ret != -EPIPE && ret != -LTTNG_UST_ERR_EXITING) {
+ ERR("UST app sock %d release session handle failed with ret %d",
+ sock, ret);
+ }
+ }
+ pthread_mutex_unlock(&ua_sess->lock);
+
+ call_rcu(&ua_sess->rcu_head, delete_ust_app_session_rcu);
+}
+
+/*
+ * Delete a traceable application structure from the global list. Never call
+ * this function outside of a call_rcu call.
+ *
+ * RCU read side lock should _NOT_ be held when calling this function.
+ */
+static
+void delete_ust_app(struct ust_app *app)
+{
+ int ret, sock;
+ struct ust_app_session *ua_sess, *tmp_ua_sess;
+
+ /* Delete ust app sessions info */
+ sock = app->sock;
+ app->sock = -1;
+
+ /* Wipe sessions */
+ cds_list_for_each_entry_safe(ua_sess, tmp_ua_sess, &app->teardown_head,
+ teardown_node) {
+ /* Free every object in the session and the session. */
+ rcu_read_lock();
+ delete_ust_app_session(sock, ua_sess, app);
+ rcu_read_unlock();
+ }
+
+ ht_cleanup_push(app->sessions);
+ ht_cleanup_push(app->ust_objd);
+
+ /*
+ * Wait until we have deleted the application from the sock hash table
+ * before closing this socket, otherwise an application could re-use the
+ * socket ID and race with the teardown, using the same hash table entry.
+ *
+ * It's OK to leave the close in call_rcu. We want it to stay unique for
+ * all RCU readers that could run concurrently with unregister app,
+ * therefore we _need_ to only close that socket after a grace period. So
+ * it should stay in this RCU callback.
+ *
+ * This close() is a very important step of the synchronization model so
+ * every modification to this function must be carefully reviewed.
+ */
+ ret = close(sock);
+ if (ret) {
+ PERROR("close");
+ }
+ lttng_fd_put(LTTNG_FD_APPS, 1);
+
+ DBG2("UST app pid %d deleted", app->pid);
+ free(app);
+}
+
+/*
+ * URCU intermediate call to delete an UST app.
+ */
+static
+void delete_ust_app_rcu(struct rcu_head *head)
+{
+ struct lttng_ht_node_ulong *node =
+ caa_container_of(head, struct lttng_ht_node_ulong, head);
+ struct ust_app *app =
+ caa_container_of(node, struct ust_app, pid_n);
+
+ DBG3("Call RCU deleting app PID %d", app->pid);
+ delete_ust_app(app);
+}
+
+/*
+ * Delete the session from the application ht and delete the data structure by
+ * freeing every object inside and releasing them.
+ */
+static void destroy_app_session(struct ust_app *app,
+ struct ust_app_session *ua_sess)
+{
+ int ret;
+ struct lttng_ht_iter iter;
+
+ assert(app);
+ assert(ua_sess);
+
+ iter.iter.node = &ua_sess->node.node;
+ ret = lttng_ht_del(app->sessions, &iter);
+ if (ret) {
+ /* Already scheduled for teardown. */
+ goto end;
+ }
+
+ /* Once deleted, free the data structure. */
+ delete_ust_app_session(app->sock, ua_sess, app);
+
+end:
+ return;
+}
+
+/*
+ * Alloc new UST app session.
+ */
+static
+struct ust_app_session *alloc_ust_app_session(struct ust_app *app)
+{
+ struct ust_app_session *ua_sess;
+
+ /* Init most of the default value by allocating and zeroing */
+ ua_sess = zmalloc(sizeof(struct ust_app_session));
+ if (ua_sess == NULL) {
+ PERROR("malloc");
+ goto error_free;
+ }
+
+ ua_sess->handle = -1;
+ ua_sess->channels = lttng_ht_new(0, LTTNG_HT_TYPE_STRING);
+ ua_sess->metadata_attr.type = LTTNG_UST_CHAN_METADATA;
+ pthread_mutex_init(&ua_sess->lock, NULL);
+
+ return ua_sess;
+
+error_free:
+ return NULL;