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 \
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,
};
#include "agent.h"
#include "ht-cleanup.h"
#include "sessiond-config.h"
+#include "sessiond-timer.h"
static const char *help_msg =
#ifdef LTTNG_EMBED_HELP
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
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
*/
*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();
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");
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) {
}
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(),
stop_threads();
goto exit_rotation;
}
+ rotation_thread_running = true;
/* Create thread to manage the client socket */
ret = pthread_create(&client_thread, default_pthread_attr(),
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();
#include "rotate.h"
#include "cmd.h"
#include "session.h"
+#include "sessiond-timer.h"
#include <urcu.h>
#include <urcu/list.h>
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;
handle->kernel_consumer = -1;
}
handle->thread_quit_pipe = thread_quit_pipe;
+ handle->rotation_timer_queue = rotation_timer_queue;
end:
return handle;
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;
}
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) {
#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.
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);
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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 */
[ 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