Fix: if relayd is unreachable, disable consumer for the session
[lttng-tools.git] / src / bin / lttng-sessiond / ust-app.c
index 4fb23b627921ed2266ab9d885dd94993ffa9133e..0f553a671603845a6bdd40b9819505963d27c45b 100644 (file)
@@ -38,6 +38,7 @@
 #include "ust-app.h"
 #include "ust-consumer.h"
 #include "ust-ctl.h"
+#include "utils.h"
 
 /* Next available channel key. */
 static unsigned long next_channel_key;
@@ -307,9 +308,9 @@ void delete_ust_app_stream(int sock, struct ust_app_stream *stream)
 
 /*
  * We need to execute ht_destroy outside of RCU read-side critical
- * section, so we postpone its execution using call_rcu. It is simpler
- * than to change the semantic of the many callers of
- * delete_ust_app_channel().
+ * 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_channel_rcu(struct rcu_head *head)
@@ -317,8 +318,8 @@ void delete_ust_app_channel_rcu(struct rcu_head *head)
        struct ust_app_channel *ua_chan =
                caa_container_of(head, struct ust_app_channel, rcu_head);
 
-       lttng_ht_destroy(ua_chan->ctx);
-       lttng_ht_destroy(ua_chan->events);
+       ht_cleanup_push(ua_chan->ctx);
+       ht_cleanup_push(ua_chan->events);
        free(ua_chan);
 }
 
@@ -384,7 +385,10 @@ void delete_ust_app_channel(int sock, struct ust_app_channel *ua_chan,
 }
 
 /*
- * Push metadata to consumer socket. The socket lock MUST be acquired.
+ * Push metadata to consumer socket.
+ *
+ * The socket lock MUST be acquired.
+ * The ust app session lock MUST be acquired.
  *
  * On success, return the len of metadata pushed or else a negative value.
  */
@@ -398,8 +402,19 @@ ssize_t ust_app_push_metadata(struct ust_registry_session *registry,
 
        assert(registry);
        assert(socket);
-       /* Should never be 0 which is the initial state. */
-       assert(registry->metadata_key);
+
+       /*
+        * On a push metadata error either the consumer is dead or the metadata
+        * channel has been destroyed because its endpoint might have died (e.g:
+        * relayd). If so, the metadata closed flag is set to 1 so we deny pushing
+        * metadata again which is not valid anymore on the consumer side.
+        *
+        * The ust app session mutex locked allows us to make this check without
+        * the registry lock.
+        */
+       if (registry->metadata_closed) {
+               return -EPIPE;
+       }
 
        pthread_mutex_lock(&registry->lock);
 
@@ -474,7 +489,7 @@ static int push_metadata(struct ust_registry_session *registry,
         */
        if (!registry->metadata_key) {
                ret_val = 0;
-               goto error_rcu_unlock;
+               goto end_rcu_unlock;
        }
 
        /* Get consumer socket to use to push the metadata.*/
@@ -507,6 +522,13 @@ static int push_metadata(struct ust_registry_session *registry,
        return 0;
 
 error_rcu_unlock:
+       /*
+        * On error, flag the registry that the metadata is closed. We were unable
+        * to push anything and this means that either the consumer is not
+        * responding or the metadata cache has been destroyed on the consumer.
+        */
+       registry->metadata_closed = 1;
+end_rcu_unlock:
        rcu_read_unlock();
        return ret_val;
 }
@@ -532,7 +554,7 @@ static int close_metadata(struct ust_registry_session *registry,
 
        if (!registry->metadata_key || registry->metadata_closed) {
                ret = 0;
-               goto error;
+               goto end;
        }
 
        /* Get consumer socket to use to push the metadata.*/
@@ -548,19 +570,23 @@ static int close_metadata(struct ust_registry_session *registry,
                goto error;
        }
 
-       /* Metadata successfully closed. Flag the registry. */
-       registry->metadata_closed = 1;
-
 error:
+       /*
+        * 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, so we postpone its execution using call_rcu. It is simpler
- * than to change the semantic of the many callers of
- * delete_ust_app_session().
+ * 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)
@@ -568,7 +594,7 @@ 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);
 
-       lttng_ht_destroy(ua_sess->channels);
+       ht_cleanup_push(ua_sess->channels);
        free(ua_sess);
 }
 
@@ -587,16 +613,21 @@ void delete_ust_app_session(int sock, struct ust_app_session *ua_sess,
 
        assert(ua_sess);
 
+       pthread_mutex_lock(&ua_sess->lock);
+
        registry = get_session_registry(ua_sess);
-       if (registry) {
+       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.
+                * 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) {
+               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);
                }
@@ -625,6 +656,8 @@ void delete_ust_app_session(int sock, struct ust_app_session *ua_sess,
                                        sock, ret);
                }
        }
+       pthread_mutex_unlock(&ua_sess->lock);
+
        call_rcu(&ua_sess->rcu_head, delete_ust_app_session_rcu);
 }
 
@@ -653,8 +686,8 @@ void delete_ust_app(struct ust_app *app)
                rcu_read_unlock();
        }
 
-       lttng_ht_destroy(app->sessions);
-       lttng_ht_destroy(app->ust_objd);
+       ht_cleanup_push(app->sessions);
+       ht_cleanup_push(app->ust_objd);
 
        /*
         * Wait until we have deleted the application from the sock hash table
@@ -1913,9 +1946,11 @@ static int do_consumer_create_channel(struct ltt_ust_session *usess,
         * Now get the channel from the consumer. This call wil populate the stream
         * list of that channel and set the ust objects.
         */
-       ret = ust_consumer_get_channel(socket, ua_chan);
-       if (ret < 0) {
-               goto error_destroy;
+       if (usess->consumer->enabled) {
+               ret = ust_consumer_get_channel(socket, ua_chan);
+               if (ret < 0) {
+                       goto error_destroy;
+               }
        }
 
        rcu_read_unlock();
@@ -2517,8 +2552,8 @@ static int create_ust_app_metadata(struct ust_app_session *ua_sess,
        registry = get_session_registry(ua_sess);
        assert(registry);
 
-       /* Metadata already exists for this registry. */
-       if (registry->metadata_key) {
+       /* Metadata already exists for this registry or it was closed previously */
+       if (registry->metadata_key || registry->metadata_closed) {
                ret = 0;
                goto error;
        }
@@ -2577,10 +2612,8 @@ static int create_ust_app_metadata(struct ust_app_session *ua_sess,
        ret = ust_consumer_ask_channel(ua_sess, metadata, consumer, socket,
                        registry);
        if (ret < 0) {
-               /*
-                * Safe because the metadata obj pointer is not set so the delete below
-                * will not put a FD back again.
-                */
+               /* Nullify the metadata key so we don't try to close it later on. */
+               registry->metadata_key = 0;
                goto error_consumer;
        }
 
@@ -2592,10 +2625,8 @@ static int create_ust_app_metadata(struct ust_app_session *ua_sess,
         */
        ret = consumer_setup_metadata(socket, metadata->key);
        if (ret < 0) {
-               /*
-                * Safe because the metadata obj pointer is not set so the delete below
-                * will not put a FD back again.
-                */
+               /* Nullify the metadata key so we don't try to close it later on. */
+               registry->metadata_key = 0;
                goto error_consumer;
        }
 
@@ -2851,15 +2882,18 @@ void ust_app_unregister(int sock)
                 * session so the delete session will NOT push/close a second time.
                 */
                registry = get_session_registry(ua_sess);
-               if (registry) {
+               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.
+                        * 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) {
+                       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);
                        }
@@ -3120,9 +3154,9 @@ void ust_app_clean_list(void)
        rcu_read_unlock();
 
        /* Destroy is done only when the ht is empty */
-       lttng_ht_destroy(ust_app_ht);
-       lttng_ht_destroy(ust_app_ht_by_sock);
-       lttng_ht_destroy(ust_app_ht_by_notify_sock);
+       ht_cleanup_push(ust_app_ht);
+       ht_cleanup_push(ust_app_ht_by_sock);
+       ht_cleanup_push(ust_app_ht_by_notify_sock);
 }
 
 /*
@@ -3742,8 +3776,11 @@ int ust_app_stop_trace(struct ltt_ust_session *usess, struct ust_app *app)
 
        registry = get_session_registry(ua_sess);
        assert(registry);
-       /* Push metadata for application before freeing the application. */
-       (void) push_metadata(registry, ua_sess->consumer);
+
+       if (!registry->metadata_closed) {
+               /* Push metadata for application before freeing the application. */
+               (void) push_metadata(registry, ua_sess->consumer);
+       }
 
        pthread_mutex_unlock(&ua_sess->lock);
 end_no_session:
@@ -4434,12 +4471,19 @@ static int reply_ust_register_channel(int sock, int sobjd, int cobjd,
                DBG("Application socket %d is being teardown. Abort event notify",
                                sock);
                ret = 0;
+               free(fields);
                goto error_rcu_unlock;
        }
 
-       /* Lookup channel by UST object descriptor. Should always be found. */
+       /* Lookup channel by UST object descriptor. */
        ua_chan = find_channel_by_objd(app, cobjd);
-       assert(ua_chan);
+       if (!ua_chan) {
+               DBG("Application channel is being teardown. Abort event notify");
+               ret = 0;
+               free(fields);
+               goto error_rcu_unlock;
+       }
+
        assert(ua_chan->session);
        ua_sess = ua_chan->session;
 
@@ -4473,6 +4517,9 @@ static int reply_ust_register_channel(int sock, int sobjd, int cobjd,
        } else {
                /* Get current already assigned values. */
                type = chan_reg->header_type;
+               free(fields);
+               /* Set to NULL so the error path does not do a double free. */
+               fields = NULL;
        }
        /* Channel id is set during the object creation. */
        chan_id = chan_reg->chan_id;
@@ -4508,6 +4555,9 @@ error:
        pthread_mutex_unlock(&registry->lock);
 error_rcu_unlock:
        rcu_read_unlock();
+       if (ret) {
+               free(fields);
+       }
        return ret;
 }
 
@@ -4540,12 +4590,23 @@ static int add_event_ust_registry(int sock, int sobjd, int cobjd, char *name,
                DBG("Application socket %d is being teardown. Abort event notify",
                                sock);
                ret = 0;
+               free(sig);
+               free(fields);
+               free(model_emf_uri);
                goto error_rcu_unlock;
        }
 
-       /* Lookup channel by UST object descriptor. Should always be found. */
+       /* Lookup channel by UST object descriptor. */
        ua_chan = find_channel_by_objd(app, cobjd);
-       assert(ua_chan);
+       if (!ua_chan) {
+               DBG("Application channel is being teardown. Abort event notify");
+               ret = 0;
+               free(sig);
+               free(fields);
+               free(model_emf_uri);
+               goto error_rcu_unlock;
+       }
+
        assert(ua_chan->session);
        ua_sess = ua_chan->session;
 
@@ -4560,6 +4621,11 @@ static int add_event_ust_registry(int sock, int sobjd, int cobjd, char *name,
 
        pthread_mutex_lock(&registry->lock);
 
+       /*
+        * From this point on, this call acquires the ownership of the sig, fields
+        * and model_emf_uri meaning any free are done inside it if needed. These
+        * three variables MUST NOT be read/write after this.
+        */
        ret_code = ust_registry_create_event(registry, chan_reg_key,
                        sobjd, cobjd, name, sig, nr_fields, fields, loglevel,
                        model_emf_uri, ua_sess->buffer_type, &event_id);
@@ -4636,7 +4702,12 @@ int ust_app_recv_notify(int sock)
                        goto error;
                }
 
-               /* Add event to the UST registry coming from the notify socket. */
+               /*
+                * Add event to the UST registry coming from the notify socket. This
+                * call will free if needed the sig, fields and model_emf_uri. This
+                * code path loses the ownsership of these variables and transfer them
+                * to the this function.
+                */
                ret = add_event_ust_registry(sock, sobjd, cobjd, name, sig, nr_fields,
                                fields, loglevel, model_emf_uri);
                if (ret < 0) {
@@ -4664,6 +4735,11 @@ int ust_app_recv_notify(int sock)
                        goto error;
                }
 
+               /*
+                * The fields ownership are transfered to this function call meaning
+                * that if needed it will be freed. After this, it's invalid to access
+                * fields or clean it up.
+                */
                ret = reply_ust_register_channel(sock, sobjd, cobjd, nr_fields,
                                fields);
                if (ret < 0) {
@@ -4757,3 +4833,15 @@ close_socket:
                call_rcu(&obj->head, close_notify_sock_rcu);
        }
 }
+
+/*
+ * Destroy a ust app data structure and free its memory.
+ */
+void ust_app_destroy(struct ust_app *app)
+{
+       if (!app) {
+               return;
+       }
+
+       call_rcu(&app->pid_n.head, delete_ust_app_rcu);
+}
This page took 0.035366 seconds and 5 git commands to generate.