if (ret) {
PERROR("sigaddset exit");
}
+ ret = sigaddset(mask, LTTNG_SESSIOND_SIG_ROTATE_PENDING);
+ if (ret) {
+ PERROR("sigaddset switch");
+ }
+ ret = sigaddset(mask, LTTNG_SESSIOND_SIG_ROTATE_TIMER);
+ if (ret) {
+ PERROR("sigaddset switch");
+ }
}
/*
return ret;
}
+int sessiond_timer_rotate_pending_start(struct ltt_session *session,
+ unsigned int interval_us)
+{
+ int ret;
+
+ DBG("Enabling rotate pending timer on session %" PRIu64, session->id);
+ /*
+ * We arm this timer in a one-shot mode so we don't have to disable it
+ * explicitly (which could deadlock if the timer thread is blocked writing
+ * in the rotation_timer_pipe).
+ * Instead, we re-arm it if needed after the rotation_pending check as
+ * returned. Also, this timer is usually only needed once, so there is no
+ * need to go through the whole signal teardown scheme everytime.
+ */
+ ret = session_timer_start(&session->rotate_relay_pending_timer,
+ session, interval_us,
+ LTTNG_SESSIOND_SIG_ROTATE_PENDING,
+ /* one-shot */ true);
+ if (ret == 0) {
+ session->rotate_relay_pending_timer_enabled = true;
+ }
+
+ return ret;
+}
+
+/*
+ * Stop and delete the channel's live timer.
+ * Called with session and session_list locks held.
+ */
+int sessiond_timer_rotate_pending_stop(struct ltt_session *session)
+{
+ int ret;
+
+ assert(session);
+
+ DBG("Disabling timer rotate pending on session %" PRIu64, session->id);
+ ret = session_timer_stop(&session->rotate_relay_pending_timer,
+ LTTNG_SESSIOND_SIG_ROTATE_PENDING);
+ if (ret == -1) {
+ ERR("Failed to stop rotate_pending timer");
+ } else {
+ session->rotate_relay_pending_timer_enabled = false;
+ }
+ return ret;
+}
+
+int sessiond_rotate_timer_start(struct ltt_session *session,
+ unsigned int interval_us)
+{
+ int ret;
+
+ DBG("Enabling rotation timer on session \"%s\" (%ui µs)", session->name,
+ interval_us);
+ ret = session_timer_start(&session->rotate_timer, session, interval_us,
+ LTTNG_SESSIOND_SIG_ROTATE_TIMER, false);
+ if (ret < 0) {
+ goto end;
+ }
+ session->rotate_timer_enabled = true;
+end:
+ return ret;
+}
+
+/*
+ * Stop and delete the channel's live timer.
+ */
+int sessiond_rotate_timer_stop(struct ltt_session *session)
+{
+ int ret = 0;
+
+ assert(session);
+
+ if (!session->rotate_timer_enabled) {
+ goto end;
+ }
+
+ DBG("Disabling rotation timer on session %s", session->name);
+ ret = session_timer_stop(&session->rotate_timer,
+ LTTNG_SESSIOND_SIG_ROTATE_TIMER);
+ if (ret < 0) {
+ ERR("Failed to stop rotate timer of session \"%s\"",
+ session->name);
+ goto end;
+ }
+
+ session->rotate_timer_enabled = false;
+ ret = 0;
+end:
+ return ret;
+}
+
/*
* Block the RT signals for the entire process. It must be called from the
* sessiond main before creating the threads
return 0;
}
+/*
+ * Called with the rotation_timer_queue lock held.
+ * Return true if the same timer job already exists in the queue, false if not.
+ */
+static
+bool check_duplicate_timer_job(struct timer_thread_parameters *ctx,
+ struct ltt_session *session, unsigned int signal)
+{
+ bool ret = false;
+ struct sessiond_rotation_timer *node;
+
+ rcu_read_lock();
+ cds_list_for_each_entry(node, &ctx->rotation_timer_queue->list, head) {
+ if (node->session_id == session->id && node->signal == signal) {
+ ret = true;
+ goto end;
+ }
+ }
+
+end:
+ rcu_read_unlock();
+ return ret;
+}
+
+/*
+ * Add the session ID and signal value to the rotation_timer_queue if it is
+ * not already there and wakeup the rotation thread. The rotation thread
+ * empties the whole queue everytime it is woken up. The event_pipe is
+ * non-blocking, if it would block, we just return because we know the
+ * rotation thread will be awaken anyway.
+ */
+static
+int enqueue_timer_rotate_job(struct timer_thread_parameters *ctx,
+ struct ltt_session *session, unsigned int signal)
+{
+ int ret;
+ char *c = "!";
+ struct sessiond_rotation_timer *timer_data = NULL;
+
+ pthread_mutex_lock(&ctx->rotation_timer_queue->lock);
+ if (check_duplicate_timer_job(ctx, session, signal)) {
+ /*
+ * This timer job is already pending, we don't need to add
+ * it.
+ */
+ ret = 0;
+ goto end;
+ }
+
+ timer_data = zmalloc(sizeof(struct sessiond_rotation_timer));
+ if (!timer_data) {
+ PERROR("Allocation of timer data");
+ ret = -1;
+ goto end;
+ }
+ timer_data->session_id = session->id;
+ timer_data->signal = signal;
+ cds_list_add_tail(&timer_data->head,
+ &ctx->rotation_timer_queue->list);
+
+ ret = lttng_write(
+ lttng_pipe_get_writefd(ctx->rotation_timer_queue->event_pipe),
+ c, 1);
+ if (ret < 0) {
+ /*
+ * We do not want to block in the timer handler, the job has been
+ * enqueued in the list, the wakeup pipe is probably full, the job
+ * will be processed when the rotation_thread catches up.
+ */
+ if (errno == EAGAIN || errno == EWOULDBLOCK) {
+ ret = 0;
+ goto end;
+ }
+ PERROR("Timer wakeup rotation thread");
+ goto end;
+ }
+
+ ret = 0;
+
+end:
+ pthread_mutex_unlock(&ctx->rotation_timer_queue->lock);
+ return ret;
+}
+
+/*
+ * Ask the rotation thread to check if the last rotation started in this
+ * session is still pending on the relay.
+ */
+static
+void relay_rotation_pending_timer(struct timer_thread_parameters *ctx,
+ int sig, siginfo_t *si)
+{
+ struct ltt_session *session = si->si_value.sival_ptr;
+
+ assert(session);
+
+ (void) enqueue_timer_rotate_job(ctx, session,
+ LTTNG_SESSIOND_SIG_ROTATE_PENDING);
+}
+
+/*
+ * Handle the LTTNG_SESSIOND_SIG_ROTATE_TIMER timer. Add the session ID to
+ * the rotation_timer_queue so the rotation thread can trigger a new rotation
+ * on that session.
+ */
+static
+void rotate_timer(struct timer_thread_parameters *ctx, int sig, siginfo_t *si)
+{
+ int ret;
+ /*
+ * The session cannot be freed/destroyed while we are running this
+ * signal handler.
+ */
+ struct ltt_session *session = si->si_value.sival_ptr;
+ assert(session);
+
+ ret = enqueue_timer_rotate_job(ctx, session, LTTNG_SESSIOND_SIG_ROTATE_TIMER);
+ if (ret) {
+ PERROR("wakeup rotate pipe");
+ }
+}
+
/*
* This thread is the sighandler for the timer signals.
*/
DBG("Signal timer metadata thread teardown");
} else if (signr == LTTNG_SESSIOND_SIG_EXIT) {
goto end;
+ } else if (signr == LTTNG_SESSIOND_SIG_ROTATE_PENDING) {
+ relay_rotation_pending_timer(ctx, info.si_signo, &info);
+ } else if (signr == LTTNG_SESSIOND_SIG_ROTATE_TIMER) {
+ rotate_timer(ctx, info.si_signo, &info);
} else {
ERR("Unexpected signal %d\n", info.si_signo);
}