From d086f507d02078aed618ab291a0bc4a634958fa3 Mon Sep 17 00:00:00 2001 From: Julien Desfossez Date: Fri, 15 Dec 2017 16:49:42 -0500 Subject: [PATCH] Sessiond timer thread MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit 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 Signed-off-by: Jérémie Galarneau --- src/bin/lttng-sessiond/Makefile.am | 3 +- src/bin/lttng-sessiond/health-sessiond.h | 1 + src/bin/lttng-sessiond/main.c | 97 ++++++++- src/bin/lttng-sessiond/rotation-thread.c | 17 +- src/bin/lttng-sessiond/rotation-thread.h | 15 +- src/bin/lttng-sessiond/sessiond-timer.c | 258 +++++++++++++++++++++++ src/bin/lttng-sessiond/sessiond-timer.h | 57 +++++ src/lib/lttng-ctl/lttng-ctl-health.c | 1 + 8 files changed, 442 insertions(+), 7 deletions(-) create mode 100644 src/bin/lttng-sessiond/sessiond-timer.c create mode 100644 src/bin/lttng-sessiond/sessiond-timer.h diff --git a/src/bin/lttng-sessiond/Makefile.am b/src/bin/lttng-sessiond/Makefile.am index ea6e9e9bb..4da4e6bab 100644 --- a/src/bin/lttng-sessiond/Makefile.am +++ b/src/bin/lttng-sessiond/Makefile.am @@ -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 \ diff --git a/src/bin/lttng-sessiond/health-sessiond.h b/src/bin/lttng-sessiond/health-sessiond.h index abeb4f0cf..b9c3161fe 100644 --- a/src/bin/lttng-sessiond/health-sessiond.h +++ b/src/bin/lttng-sessiond/health-sessiond.h @@ -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, }; diff --git a/src/bin/lttng-sessiond/main.c b/src/bin/lttng-sessiond/main.c index b1229e721..489cc9d76 100644 --- a/src/bin/lttng-sessiond/main.c +++ b/src/bin/lttng-sessiond/main.c @@ -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(); diff --git a/src/bin/lttng-sessiond/rotation-thread.c b/src/bin/lttng-sessiond/rotation-thread.c index b245f75ad..1704777a0 100644 --- a/src/bin/lttng-sessiond/rotation-thread.c +++ b/src/bin/lttng-sessiond/rotation-thread.c @@ -40,6 +40,7 @@ #include "rotate.h" #include "cmd.h" #include "session.h" +#include "sessiond-timer.h" #include #include @@ -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) { diff --git a/src/bin/lttng-sessiond/rotation-thread.h b/src/bin/lttng-sessiond/rotation-thread.h index 64e1ad98d..c7bca0c4b 100644 --- a/src/bin/lttng-sessiond/rotation-thread.h +++ b/src/bin/lttng-sessiond/rotation-thread.h @@ -28,6 +28,17 @@ #include #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 index 000000000..d7aaca0f1 --- /dev/null +++ b/src/bin/lttng-sessiond/sessiond-timer.c @@ -0,0 +1,258 @@ +/* + * Copyright (C) 2017 - Julien Desfossez + * + * 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 +#include +#include + +#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 index 000000000..e1b8a7ead --- /dev/null +++ b/src/bin/lttng-sessiond/sessiond-timer.h @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2017 - Julien Desfossez + * + * 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 + +#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 */ diff --git a/src/lib/lttng-ctl/lttng-ctl-health.c b/src/lib/lttng-ctl/lttng-ctl-health.c index 1f1f16056..811c6d935 100644 --- a/src/lib/lttng-ctl/lttng-ctl-health.c +++ b/src/lib/lttng-ctl/lttng-ctl-health.c @@ -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 -- 2.34.1