2 * Copyright (C) 2017 - Julien Desfossez <jdesfossez@efficios.com>
3 * Copyright (C) 2018 - Jérémie Galarneau <jeremie.galarneau@efficios.com>
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License, version 2 only, as
7 * published by the Free Software Foundation.
9 * This program is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
14 * You should have received a copy of the GNU General Public License along with
15 * this program; if not, write to the Free Software Foundation, Inc., 51
16 * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
25 #include "health-sessiond.h"
26 #include "rotation-thread.h"
29 #define LTTNG_SESSIOND_SIG_QS SIGRTMIN + 10
30 #define LTTNG_SESSIOND_SIG_EXIT SIGRTMIN + 11
31 #define LTTNG_SESSIOND_SIG_PENDING_ROTATION_CHECK SIGRTMIN + 12
32 #define LTTNG_SESSIOND_SIG_SCHEDULED_ROTATION SIGRTMIN + 13
34 #define UINT_TO_PTR(value) \
36 assert(value <= UINTPTR_MAX); \
37 (void *) (uintptr_t) value; \
39 #define PTR_TO_UINT(ptr) ((uintptr_t) ptr)
42 * Handle timer teardown race wrt memory free of private data by sessiond
43 * signals are handled by a single thread, which permits a synchronization
44 * point between handling of each signal. Internal lock ensures mutual
48 struct timer_signal_data
{
49 /* Thread managing signals. */
56 .lock
= PTHREAD_MUTEX_INITIALIZER
,
60 * Set custom signal mask to current thread.
63 void setmask(sigset_t
*mask
)
67 ret
= sigemptyset(mask
);
69 PERROR("sigemptyset");
71 ret
= sigaddset(mask
, LTTNG_SESSIOND_SIG_QS
);
73 PERROR("sigaddset teardown");
75 ret
= sigaddset(mask
, LTTNG_SESSIOND_SIG_EXIT
);
77 PERROR("sigaddset exit");
79 ret
= sigaddset(mask
, LTTNG_SESSIOND_SIG_PENDING_ROTATION_CHECK
);
81 PERROR("sigaddset pending rotation check");
83 ret
= sigaddset(mask
, LTTNG_SESSIOND_SIG_SCHEDULED_ROTATION
);
85 PERROR("sigaddset scheduled rotation");
90 * This is the same function as timer_signal_thread_qs, when it
91 * returns, it means that no timer signr is currently pending or being handled
92 * by the timer thread. This cannot be called from the timer thread.
95 void timer_signal_thread_qs(unsigned int signr
)
101 * We need to be the only thread interacting with the thread
102 * that manages signals for teardown synchronization.
104 pthread_mutex_lock(&timer_signal
.lock
);
106 /* Ensure we don't have any signal queued for this session. */
108 ret
= sigemptyset(&pending_set
);
110 PERROR("sigemptyset");
112 ret
= sigpending(&pending_set
);
114 PERROR("sigpending");
116 if (!sigismember(&pending_set
, signr
)) {
123 * From this point, no new signal handler will be fired that would try to
124 * access "session". However, we still need to wait for any currently
125 * executing handler to complete.
128 CMM_STORE_SHARED(timer_signal
.qs_done
, 0);
132 * Kill with LTTNG_SESSIOND_SIG_QS, so signal management thread
135 kill(getpid(), LTTNG_SESSIOND_SIG_QS
);
137 while (!CMM_LOAD_SHARED(timer_signal
.qs_done
)) {
142 pthread_mutex_unlock(&timer_signal
.lock
);
146 * Start a timer on a session that will fire at a given interval
147 * (timer_interval_us) and fire a given signal (signal).
149 * Returns a negative value on error, 0 if a timer was created, and
150 * a positive value if no timer was created (not an error).
153 int timer_start(timer_t
*timer_id
, struct ltt_session
*session
,
154 unsigned int timer_interval_us
, int signal
, bool one_shot
)
156 int ret
= 0, delete_ret
;
158 struct itimerspec its
;
160 sev
.sigev_notify
= SIGEV_SIGNAL
;
161 sev
.sigev_signo
= signal
;
162 sev
.sigev_value
.sival_ptr
= session
;
163 ret
= timer_create(CLOCK_MONOTONIC
, &sev
, timer_id
);
165 PERROR("timer_create");
169 its
.it_value
.tv_sec
= timer_interval_us
/ 1000000;
170 its
.it_value
.tv_nsec
= (timer_interval_us
% 1000000) * 1000;
172 its
.it_interval
.tv_sec
= 0;
173 its
.it_interval
.tv_nsec
= 0;
175 its
.it_interval
.tv_sec
= its
.it_value
.tv_sec
;
176 its
.it_interval
.tv_nsec
= its
.it_value
.tv_nsec
;
179 ret
= timer_settime(*timer_id
, 0, &its
, NULL
);
181 PERROR("timer_settime");
182 goto error_destroy_timer
;
187 delete_ret
= timer_delete(*timer_id
);
188 if (delete_ret
== -1) {
189 PERROR("timer_delete");
197 int timer_stop(timer_t
*timer_id
, int signal
)
201 ret
= timer_delete(*timer_id
);
203 PERROR("timer_delete");
207 timer_signal_thread_qs(signal
);
213 int timer_session_rotation_pending_check_start(struct ltt_session
*session
,
214 unsigned int interval_us
)
218 if (!session_get(session
)) {
222 DBG("Enabling session rotation pending check timer on session %" PRIu64
,
225 * We arm this timer in a one-shot mode so we don't have to disable it
226 * explicitly (which could deadlock if the timer thread is blocked
227 * writing in the rotation_timer_pipe).
229 * Instead, we re-arm it if needed after the rotation_pending check as
230 * returned. Also, this timer is usually only needed once, so there is
231 * no need to go through the whole signal teardown scheme everytime.
233 ret
= timer_start(&session
->rotation_pending_check_timer
,
234 session
, interval_us
,
235 LTTNG_SESSIOND_SIG_PENDING_ROTATION_CHECK
,
236 /* one-shot */ true);
238 session
->rotation_pending_check_timer_enabled
= true;
245 * Call with session and session_list locks held.
247 int timer_session_rotation_pending_check_stop(struct ltt_session
*session
)
252 assert(session
->rotation_pending_check_timer_enabled
);
254 DBG("Disabling session rotation pending check timer on session %" PRIu64
,
256 ret
= timer_stop(&session
->rotation_pending_check_timer
,
257 LTTNG_SESSIOND_SIG_PENDING_ROTATION_CHECK
);
259 ERR("Failed to stop rotate_pending_check timer");
261 session
->rotation_pending_check_timer_enabled
= false;
263 * The timer's reference to the session can be released safely.
265 session_put(session
);
271 * Call with session and session_list locks held.
273 int timer_session_rotation_schedule_timer_start(struct ltt_session
*session
,
274 unsigned int interval_us
)
278 if (!session_get(session
)) {
282 DBG("Enabling scheduled rotation timer on session \"%s\" (%ui %s)", session
->name
,
283 interval_us
, USEC_UNIT
);
284 ret
= timer_start(&session
->rotation_schedule_timer
, session
,
285 interval_us
, LTTNG_SESSIOND_SIG_SCHEDULED_ROTATION
,
286 /* one-shot */ false);
290 session
->rotation_schedule_timer_enabled
= true;
296 * Call with session and session_list locks held.
298 int timer_session_rotation_schedule_timer_stop(struct ltt_session
*session
)
304 if (!session
->rotation_schedule_timer_enabled
) {
308 DBG("Disabling scheduled rotation timer on session %s", session
->name
);
309 ret
= timer_stop(&session
->rotation_schedule_timer
,
310 LTTNG_SESSIOND_SIG_SCHEDULED_ROTATION
);
312 ERR("Failed to stop scheduled rotation timer of session \"%s\"",
317 session
->rotation_schedule_timer_enabled
= false;
318 /* The timer's reference to the session can be released safely. */
319 session_put(session
);
326 * Block the RT signals for the entire process. It must be called from the
327 * sessiond main before creating the threads
329 int timer_signal_init(void)
334 /* Block signal for entire process, so only our thread processes it. */
336 ret
= pthread_sigmask(SIG_BLOCK
, &mask
, NULL
);
339 PERROR("pthread_sigmask");
346 * This thread is the sighandler for the timer signals.
349 void *thread_timer(void *data
)
354 struct timer_thread_parameters
*ctx
= data
;
356 rcu_register_thread();
359 health_register(health_sessiond
, HEALTH_SESSIOND_TYPE_TIMER
);
360 health_code_update();
362 /* Only self thread will receive signal mask. */
364 CMM_STORE_SHARED(timer_signal
.tid
, pthread_self());
367 health_code_update();
370 signr
= sigwaitinfo(&mask
, &info
);
374 * NOTE: cascading conditions are used instead of a switch case
375 * since the use of SIGRTMIN in the definition of the signals'
376 * values prevents the reduction to an integer constant.
379 if (errno
!= EINTR
) {
380 PERROR("sigwaitinfo");
383 } else if (signr
== LTTNG_SESSIOND_SIG_QS
) {
385 CMM_STORE_SHARED(timer_signal
.qs_done
, 1);
387 } else if (signr
== LTTNG_SESSIOND_SIG_EXIT
) {
389 } else if (signr
== LTTNG_SESSIOND_SIG_PENDING_ROTATION_CHECK
) {
390 struct ltt_session
*session
=
391 (struct ltt_session
*) info
.si_value
.sival_ptr
;
393 rotation_thread_enqueue_job(ctx
->rotation_thread_job_queue
,
394 ROTATION_THREAD_JOB_TYPE_CHECK_PENDING_ROTATION
,
396 } else if (signr
== LTTNG_SESSIOND_SIG_SCHEDULED_ROTATION
) {
397 rotation_thread_enqueue_job(ctx
->rotation_thread_job_queue
,
398 ROTATION_THREAD_JOB_TYPE_SCHEDULED_ROTATION
,
399 (struct ltt_session
*) info
.si_value
.sival_ptr
);
401 * The scheduled periodic rotation timer is not in
402 * "one-shot" mode. The reference to the session is not
403 * released since the timer is still enabled and can
407 ERR("Unexpected signal %d\n", info
.si_signo
);
412 DBG("[timer-thread] Exit");
413 health_unregister(health_sessiond
);
414 rcu_thread_offline();
415 rcu_unregister_thread();
420 bool shutdown_timer_thread(void *data
)
422 return kill(getpid(), LTTNG_SESSIOND_SIG_EXIT
) == 0;
425 bool launch_timer_thread(
426 struct timer_thread_parameters
*timer_thread_parameters
)
428 struct lttng_thread
*thread
;
430 thread
= lttng_thread_create("Timer",
432 shutdown_timer_thread
,
434 timer_thread_parameters
);
438 lttng_thread_put(thread
);