Sessiond timer thread
authorJulien Desfossez <jdesfossez@efficios.com>
Fri, 15 Dec 2017 21:49:42 +0000 (16:49 -0500)
committerJérémie Galarneau <jeremie.galarneau@efficios.com>
Fri, 16 Mar 2018 16:15:59 +0000 (12:15 -0400)
This new thread allows to handle timers in the session daemon. The
structure is the same as the consumer-timer thread. For now it does not
perform any action, but it will be used by the session rotation feature.

Signed-off-by: Julien Desfossez <jdesfossez@efficios.com>
Signed-off-by: Jérémie Galarneau <jeremie.galarneau@efficios.com>
src/bin/lttng-sessiond/Makefile.am
src/bin/lttng-sessiond/health-sessiond.h
src/bin/lttng-sessiond/main.c
src/bin/lttng-sessiond/rotation-thread.c
src/bin/lttng-sessiond/rotation-thread.h
src/bin/lttng-sessiond/sessiond-timer.c [new file with mode: 0644]
src/bin/lttng-sessiond/sessiond-timer.h [new file with mode: 0644]
src/lib/lttng-ctl/lttng-ctl-health.c

index ea6e9e9bb9770122b8962248d2d055ae74288737..4da4e6bab15cff2ef5cb467a4ad2a50f9fa5825b 100644 (file)
@@ -37,7 +37,8 @@ lttng_sessiond_SOURCES = utils.c utils.h \
                        notification-thread-events.h notification-thread-events.c \
                        sessiond-config.h sessiond-config.c \
                        rotate.h rotate.c \
-                       rotation-thread.h rotation-thread.c
+                       rotation-thread.h rotation-thread.c \
+                       sessiond-timer.c sessiond-timer.h
 
 if HAVE_LIBLTTNG_UST_CTL
 lttng_sessiond_SOURCES += trace-ust.c ust-registry.c ust-app.c \
index abeb4f0cf13e67b8d2593f0d52bc3deb0f0e189d..b9c3161fe3a001dba8ef16efbd0d424ce7f5172b 100644 (file)
@@ -31,6 +31,7 @@ enum health_type_sessiond {
        HEALTH_SESSIOND_TYPE_APP_REG_DISPATCH   = 7,
        HEALTH_SESSIOND_TYPE_NOTIFICATION       = 8,
        HEALTH_SESSIOND_TYPE_ROTATION           = 9,
+       HEALTH_SESSIOND_TYPE_TIMER              = 10,
 
        NR_HEALTH_SESSIOND_TYPES,
 };
index b1229e7214828af2fcb260c3983d0e4d7e3cd1be..489cc9d7646e5643634e04d27e71057beceee930 100644 (file)
@@ -78,6 +78,7 @@
 #include "agent.h"
 #include "ht-cleanup.h"
 #include "sessiond-config.h"
+#include "sessiond-timer.h"
 
 static const char *help_msg =
 #ifdef LTTNG_EMBED_HELP
@@ -210,6 +211,7 @@ static pthread_t agent_reg_thread;
 static pthread_t load_session_thread;
 static pthread_t notification_thread;
 static pthread_t rotation_thread;
+static pthread_t timer_thread;
 
 /*
  * UST registration command queue. This queue is tied with a futex and uses a N
@@ -5469,6 +5471,48 @@ end:
        return ret;
 }
 
+static
+struct rotation_thread_timer_queue *create_rotate_timer_queue(void)
+{
+       struct rotation_thread_timer_queue *queue = NULL;
+
+       queue = zmalloc(sizeof(struct rotation_thread_timer_queue));
+       if (!queue) {
+               PERROR("Failed to allocate timer rotate queue");
+               goto end;
+       }
+
+       queue->event_pipe = lttng_pipe_open(FD_CLOEXEC | O_NONBLOCK);
+       CDS_INIT_LIST_HEAD(&queue->list);
+       pthread_mutex_init(&queue->lock, NULL);
+
+end:
+       return queue;
+}
+
+static
+void destroy_rotate_timer_queue(struct rotation_thread_timer_queue *queue)
+{
+       struct sessiond_rotation_timer *node, *tmp_node;
+
+       if (!queue) {
+               return;
+       }
+
+       lttng_pipe_destroy(queue->event_pipe);
+
+       pthread_mutex_lock(&queue->lock);
+       /* Empty wait queue. */
+       cds_list_for_each_entry_safe(node, tmp_node, &queue->list, head) {
+               cds_list_del(&node->head);
+               free(node);
+       }
+       pthread_mutex_unlock(&queue->lock);
+
+       pthread_mutex_destroy(&queue->lock);
+       free(queue);
+}
+
 /*
  * main
  */
@@ -5482,9 +5526,13 @@ int main(int argc, char **argv)
                        *kernel_channel_monitor_pipe = NULL;
        bool notification_thread_running = false;
        bool rotation_thread_running = false;
+       bool timer_thread_running = false;
        struct lttng_pipe *ust32_channel_rotate_pipe = NULL,
                        *ust64_channel_rotate_pipe = NULL,
                        *kernel_channel_rotate_pipe = NULL;
+       struct timer_thread_parameters timer_thread_ctx;
+       /* Queue of rotation jobs populated by the sessiond-timer. */
+       struct rotation_thread_timer_queue *rotation_timer_queue = NULL;
 
        init_kernel_workarounds();
 
@@ -5495,6 +5543,11 @@ int main(int argc, char **argv)
                goto exit_set_signal_handler;
        }
 
+       if (sessiond_timer_signal_init()) {
+               retval = -1;
+               goto exit_set_signal_handler;
+       }
+
        page_size = sysconf(_SC_PAGESIZE);
        if (page_size < 0) {
                PERROR("sysconf _SC_PAGESIZE");
@@ -5682,6 +5735,17 @@ int main(int argc, char **argv)
                goto exit_init_data;
        }
 
+       /*
+        * The rotation_timer_queue structure is shared between the sessiond timer
+        * thread and the rotation thread. The main() keeps the ownership and
+        * destroys it when both threads have quit.
+        */
+       rotation_timer_queue = create_rotate_timer_queue();
+       if (!rotation_timer_queue) {
+               retval = -1;
+               goto exit_init_data;
+       }
+       timer_thread_ctx.rotation_timer_queue = rotation_timer_queue;
 
        ust64_channel_monitor_pipe = lttng_pipe_open(0);
        if (!ust64_channel_monitor_pipe) {
@@ -5900,19 +5964,31 @@ int main(int argc, char **argv)
        }
        notification_thread_running = true;
 
+       /* Create timer thread. */
+       ret = pthread_create(&timer_thread, default_pthread_attr(),
+                       sessiond_timer_thread, &timer_thread_ctx);
+       if (ret) {
+               errno = ret;
+               PERROR("pthread_create timer");
+               retval = -1;
+               stop_threads();
+               goto exit_notification;
+       }
+       timer_thread_running = true;
+
        /* rotation_thread_data acquires the pipes' read side. */
        rotation_thread_handle = rotation_thread_handle_create(
                        ust32_channel_rotate_pipe,
                        ust64_channel_rotate_pipe,
                        kernel_channel_rotate_pipe,
-                       thread_quit_pipe[0]);
+                       thread_quit_pipe[0],
+                       rotation_timer_queue);
        if (!rotation_thread_handle) {
                retval = -1;
                ERR("Failed to create rotation thread shared data");
                stop_threads();
                goto exit_rotation;
        }
-       rotation_thread_running = true;
 
        /* Create rotation thread. */
        ret = pthread_create(&rotation_thread, default_pthread_attr(),
@@ -5924,6 +6000,7 @@ int main(int argc, char **argv)
                stop_threads();
                goto exit_rotation;
        }
+       rotation_thread_running = true;
 
        /* Create thread to manage the client socket */
        ret = pthread_create(&client_thread, default_pthread_attr(),
@@ -6153,6 +6230,22 @@ exit_init_data:
                rotation_thread_handle_destroy(rotation_thread_handle);
        }
 
+       if (timer_thread_running) {
+               kill(getpid(), LTTNG_SESSIOND_SIG_EXIT);
+               ret = pthread_join(timer_thread, &status);
+               if (ret) {
+                       errno = ret;
+                       PERROR("pthread_join timer thread");
+                       retval = -1;
+               }
+       }
+
+       /*
+        * After the rotation and timer thread have quit, we can safely destroy
+        * the rotation_timer_queue.
+        */
+       destroy_rotate_timer_queue(rotation_timer_queue);
+
        rcu_thread_offline();
        rcu_unregister_thread();
 
index b245f75adaa89f372b66bb83dc16769b5a0e7c23..1704777a09ece70aef872745fd0ffc0c451ba939 100644 (file)
@@ -40,6 +40,7 @@
 #include "rotate.h"
 #include "cmd.h"
 #include "session.h"
+#include "sessiond-timer.h"
 
 #include <urcu.h>
 #include <urcu/list.h>
@@ -140,7 +141,8 @@ struct rotation_thread_handle *rotation_thread_handle_create(
                struct lttng_pipe *ust32_channel_rotate_pipe,
                struct lttng_pipe *ust64_channel_rotate_pipe,
                struct lttng_pipe *kernel_channel_rotate_pipe,
-               int thread_quit_pipe)
+               int thread_quit_pipe,
+               struct rotation_thread_timer_queue *rotation_timer_queue)
 {
        struct rotation_thread_handle *handle;
 
@@ -180,6 +182,7 @@ struct rotation_thread_handle *rotation_thread_handle_create(
                handle->kernel_consumer = -1;
        }
        handle->thread_quit_pipe = thread_quit_pipe;
+       handle->rotation_timer_queue = rotation_timer_queue;
 
 end:
        return handle;
@@ -195,13 +198,14 @@ int init_poll_set(struct lttng_poll_event *poll_set,
        int ret;
 
        /*
-        * Create pollset with size 4:
+        * Create pollset with size 5:
         *      - sessiond quit pipe
+        *      - sessiond timer pipe,
         *      - consumerd (32-bit user space) channel rotate pipe,
         *      - consumerd (64-bit user space) channel rotate pipe,
         *      - consumerd (kernel) channel rotate pipe,
         */
-       ret = lttng_poll_create(poll_set, 4, LTTNG_CLOEXEC);
+       ret = lttng_poll_create(poll_set, 5, LTTNG_CLOEXEC);
        if (ret < 0) {
                goto end;
        }
@@ -212,6 +216,13 @@ int init_poll_set(struct lttng_poll_event *poll_set,
                ERR("[rotation-thread] Failed to add thread_quit_pipe fd to pollset");
                goto error;
        }
+       ret = lttng_poll_add(poll_set,
+                       lttng_pipe_get_readfd(handle->rotation_timer_queue->event_pipe),
+                       LPOLLIN | LPOLLERR);
+       if (ret < 0) {
+               ERR("[rotation-thread] Failed to add rotate_pending fd to pollset");
+               goto error;
+       }
        ret = lttng_poll_add(poll_set, handle->ust32_consumer,
                        LPOLLIN | LPOLLERR);
        if (ret < 0) {
index 64e1ad98d8b1e4306447f44eb92e14940e5afb01..c7bca0c4b7937fc6e2d4dc114255d32b3af4fc39 100644 (file)
 #include <pthread.h>
 #include "session.h"
 
+/*
+ * The timer thread enqueues struct sessiond_rotation_timer objects in the list
+ * and wake up the rotation thread. When the rotation thread wakes up, it
+ * empties the queue.
+ */
+struct rotation_thread_timer_queue {
+       struct lttng_pipe *event_pipe;
+       struct cds_list_head list;
+       pthread_mutex_t lock;
+};
+
 struct rotation_thread_handle {
        /*
         * Read side of pipes used to communicate with the rotation thread.
@@ -38,13 +49,15 @@ struct rotation_thread_handle {
        int kernel_consumer;
        /* quit pipe */
        int thread_quit_pipe;
+       struct rotation_thread_timer_queue *rotation_timer_queue;
 };
 
 struct rotation_thread_handle *rotation_thread_handle_create(
                struct lttng_pipe *ust32_channel_rotate_pipe,
                struct lttng_pipe *ust64_channel_rotate_pipe,
                struct lttng_pipe *kernel_channel_rotate_pipe,
-               int thread_quit_pipe);
+               int thread_quit_pipe,
+               struct rotation_thread_timer_queue *rotation_timer_queue);
 
 void rotation_thread_handle_destroy(
                struct rotation_thread_handle *handle);
diff --git a/src/bin/lttng-sessiond/sessiond-timer.c b/src/bin/lttng-sessiond/sessiond-timer.c
new file mode 100644 (file)
index 0000000..d7aaca0
--- /dev/null
@@ -0,0 +1,258 @@
+/*
+ * Copyright (C) 2017 - Julien Desfossez <jdesfossez@efficios.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License, version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 51
+ * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#define _LGPL_SOURCE
+#include <assert.h>
+#include <inttypes.h>
+#include <signal.h>
+
+#include "sessiond-timer.h"
+#include "health-sessiond.h"
+#include "rotation-thread.h"
+
+static
+struct timer_signal_data timer_signal = {
+       .tid = 0,
+       .qs_done = 0,
+       .lock = PTHREAD_MUTEX_INITIALIZER,
+};
+
+/*
+ * Set custom signal mask to current thread.
+ */
+static
+void setmask(sigset_t *mask)
+{
+       int ret;
+
+       ret = sigemptyset(mask);
+       if (ret) {
+               PERROR("sigemptyset");
+       }
+       ret = sigaddset(mask, LTTNG_SESSIOND_SIG_TEARDOWN);
+       if (ret) {
+               PERROR("sigaddset teardown");
+       }
+       ret = sigaddset(mask, LTTNG_SESSIOND_SIG_EXIT);
+       if (ret) {
+               PERROR("sigaddset exit");
+       }
+}
+
+/*
+ * This is the same function as consumer_timer_signal_thread_qs, when it
+ * returns, it means that no timer signr is currently pending or being handled
+ * by the timer thread. This cannot be called from the timer thread.
+ */
+static
+void sessiond_timer_signal_thread_qs(unsigned int signr)
+{
+       sigset_t pending_set;
+       int ret;
+
+       /*
+        * We need to be the only thread interacting with the thread
+        * that manages signals for teardown synchronization.
+        */
+       pthread_mutex_lock(&timer_signal.lock);
+
+       /* Ensure we don't have any signal queued for this session. */
+       for (;;) {
+               ret = sigemptyset(&pending_set);
+               if (ret == -1) {
+                       PERROR("sigemptyset");
+               }
+               ret = sigpending(&pending_set);
+               if (ret == -1) {
+                       PERROR("sigpending");
+               }
+               if (!sigismember(&pending_set, signr)) {
+                       break;
+               }
+               caa_cpu_relax();
+       }
+
+       /*
+        * From this point, no new signal handler will be fired that would try to
+        * access "session". However, we still need to wait for any currently
+        * executing handler to complete.
+        */
+       cmm_smp_mb();
+       CMM_STORE_SHARED(timer_signal.qs_done, 0);
+       cmm_smp_mb();
+
+       /*
+        * Kill with LTTNG_SESSIOND_SIG_TEARDOWN, so signal management thread
+        * wakes up.
+        */
+       kill(getpid(), LTTNG_SESSIOND_SIG_TEARDOWN);
+
+       while (!CMM_LOAD_SHARED(timer_signal.qs_done)) {
+               caa_cpu_relax();
+       }
+       cmm_smp_mb();
+
+       pthread_mutex_unlock(&timer_signal.lock);
+}
+
+/*
+ * Start a timer on a session that will fire at a given interval
+ * (timer_interval_us) and fire a given signal (signal).
+ *
+ * Returns a negative value on error, 0 if a timer was created, and
+ * a positive value if no timer was created (not an error).
+ */
+static
+int session_timer_start(timer_t *timer_id, struct ltt_session *session,
+               unsigned int timer_interval_us, int signal, bool one_shot)
+{
+       int ret = 0, delete_ret;
+       struct sigevent sev;
+       struct itimerspec its;
+
+       assert(session);
+
+       sev.sigev_notify = SIGEV_SIGNAL;
+       sev.sigev_signo = signal;
+       sev.sigev_value.sival_ptr = session;
+       ret = timer_create(CLOCKID, &sev, timer_id);
+       if (ret == -1) {
+               PERROR("timer_create");
+               goto end;
+       }
+
+       its.it_value.tv_sec = timer_interval_us / 1000000;
+       its.it_value.tv_nsec = (timer_interval_us % 1000000) * 1000;
+       if (one_shot) {
+               its.it_interval.tv_sec = 0;
+               its.it_interval.tv_nsec = 0;
+       } else {
+               its.it_interval.tv_sec = its.it_value.tv_sec;
+               its.it_interval.tv_nsec = its.it_value.tv_nsec;
+       }
+
+       ret = timer_settime(*timer_id, 0, &its, NULL);
+       if (ret == -1) {
+               PERROR("timer_settime");
+               goto error_destroy_timer;
+       }
+       goto end;
+
+error_destroy_timer:
+       delete_ret = timer_delete(*timer_id);
+       if (delete_ret == -1) {
+               PERROR("timer_delete");
+       }
+
+end:
+       return ret;
+}
+
+static
+int session_timer_stop(timer_t *timer_id, int signal)
+{
+       int ret = 0;
+
+       ret = timer_delete(*timer_id);
+       if (ret == -1) {
+               PERROR("timer_delete");
+               goto end;
+       }
+
+       sessiond_timer_signal_thread_qs(signal);
+       *timer_id = 0;
+end:
+       return ret;
+}
+
+/*
+ * Block the RT signals for the entire process. It must be called from the
+ * sessiond main before creating the threads
+ */
+int sessiond_timer_signal_init(void)
+{
+       int ret;
+       sigset_t mask;
+
+       /* Block signal for entire process, so only our thread processes it. */
+       setmask(&mask);
+       ret = pthread_sigmask(SIG_BLOCK, &mask, NULL);
+       if (ret) {
+               errno = ret;
+               PERROR("pthread_sigmask");
+               return -1;
+       }
+       return 0;
+}
+
+/*
+ * This thread is the sighandler for the timer signals.
+ */
+void *sessiond_timer_thread(void *data)
+{
+       int signr;
+       sigset_t mask;
+       siginfo_t info;
+       struct timer_thread_parameters *ctx = data;
+
+       rcu_register_thread();
+       rcu_thread_online();
+
+       health_register(health_sessiond, HEALTH_SESSIOND_TYPE_TIMER);
+
+       health_code_update();
+
+       /* Only self thread will receive signal mask. */
+       setmask(&mask);
+       CMM_STORE_SHARED(timer_signal.tid, pthread_self());
+
+       while (1) {
+               health_code_update();
+
+               health_poll_entry();
+               signr = sigwaitinfo(&mask, &info);
+               health_poll_exit();
+
+               /*
+                * NOTE: cascading conditions are used instead of a switch case
+                * since the use of SIGRTMIN in the definition of the signals'
+                * values prevents the reduction to an integer constant.
+                */
+               if (signr == -1) {
+                       if (errno != EINTR) {
+                               PERROR("sigwaitinfo");
+                       }
+                       continue;
+               } else if (signr == LTTNG_SESSIOND_SIG_TEARDOWN) {
+                       cmm_smp_mb();
+                       CMM_STORE_SHARED(timer_signal.qs_done, 1);
+                       cmm_smp_mb();
+                       DBG("Signal timer metadata thread teardown");
+               } else if (signr == LTTNG_SESSIOND_SIG_EXIT) {
+                       goto end;
+               } else {
+                       ERR("Unexpected signal %d\n", info.si_signo);
+               }
+       }
+
+end:
+       DBG("[timer-thread] Exit");
+       health_unregister(health_sessiond);
+       rcu_thread_offline();
+       rcu_unregister_thread();
+       return NULL;
+}
diff --git a/src/bin/lttng-sessiond/sessiond-timer.h b/src/bin/lttng-sessiond/sessiond-timer.h
new file mode 100644 (file)
index 0000000..e1b8a7e
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2017 - Julien Desfossez <jdesfossez@efficios.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef SESSIOND_TIMER_H
+#define SESSIOND_TIMER_H
+
+#include <pthread.h>
+
+#include "session.h"
+
+#define LTTNG_SESSIOND_SIG_TEARDOWN            SIGRTMIN + 10
+#define LTTNG_SESSIOND_SIG_EXIT                        SIGRTMIN + 11
+
+#define CLOCKID CLOCK_MONOTONIC
+
+/*
+ * Handle timer teardown race wrt memory free of private data by sessiond
+ * signals are handled by a single thread, which permits a synchronization
+ * point between handling of each signal. Internal lock ensures mutual
+ * exclusion.
+ */
+struct timer_signal_data {
+       /* Thread managing signals. */
+       pthread_t tid;
+       int qs_done;
+       pthread_mutex_t lock;
+};
+
+struct timer_thread_parameters {
+       struct rotation_thread_timer_queue *rotation_timer_queue;
+};
+
+struct sessiond_rotation_timer {
+       uint64_t session_id;
+       unsigned int signal;
+       /* List member in struct rotation_thread_timer_queue. */
+       struct cds_list_head head;
+};
+
+void *sessiond_timer_thread(void *data);
+int sessiond_timer_signal_init(void);
+
+#endif /* SESSIOND_TIMER_H */
index 1f1f160563c8212d7d0bcda845bd908a7d3fa45a..811c6d935184115af4388921df4507c2c05ae5c0 100644 (file)
@@ -71,6 +71,7 @@ const char *sessiond_thread_name[NR_HEALTH_SESSIOND_TYPES] = {
        [ HEALTH_SESSIOND_TYPE_APP_MANAGE_NOTIFY ] = "Session daemon application notification manager",
        [ HEALTH_SESSIOND_TYPE_APP_REG_DISPATCH ] = "Session daemon application registration dispatcher",
        [ HEALTH_SESSIOND_TYPE_ROTATION ] = "Session daemon rotation manager",
+       [ HEALTH_SESSIOND_TYPE_TIMER ] = "Session daemon timer manager",
 };
 
 static
This page took 0.053509 seconds and 5 git commands to generate.