Fix: mark rotation pending check timer is never marked as disabled
[lttng-tools.git] / src / bin / lttng-sessiond / timer.c
CommitLineData
d086f507
JD
1/*
2 * Copyright (C) 2017 - Julien Desfossez <jdesfossez@efficios.com>
92816cc3 3 * Copyright (C) 2018 - Jérémie Galarneau <jeremie.galarneau@efficios.com>
d086f507
JD
4 *
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.
8 *
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
12 * more details.
13 *
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.
17 */
18
19#define _LGPL_SOURCE
20#include <assert.h>
21#include <inttypes.h>
22#include <signal.h>
23
8e319828 24#include "timer.h"
d086f507
JD
25#include "health-sessiond.h"
26#include "rotation-thread.h"
27
92816cc3
JG
28#define LTTNG_SESSIOND_SIG_QS SIGRTMIN + 10
29#define LTTNG_SESSIOND_SIG_EXIT SIGRTMIN + 11
30#define LTTNG_SESSIOND_SIG_PENDING_ROTATION_CHECK SIGRTMIN + 12
31#define LTTNG_SESSIOND_SIG_SCHEDULED_ROTATION SIGRTMIN + 13
32
33#define UINT_TO_PTR(value) \
34 ({ \
35 assert(value <= UINTPTR_MAX); \
36 (void *) (uintptr_t) value; \
37 })
38#define PTR_TO_UINT(ptr) ((uintptr_t) ptr)
39
40/*
41 * Handle timer teardown race wrt memory free of private data by sessiond
42 * signals are handled by a single thread, which permits a synchronization
43 * point between handling of each signal. Internal lock ensures mutual
44 * exclusion.
45 */
d086f507 46static
92816cc3
JG
47struct timer_signal_data {
48 /* Thread managing signals. */
49 pthread_t tid;
50 int qs_done;
51 pthread_mutex_t lock;
52} timer_signal = {
d086f507
JD
53 .tid = 0,
54 .qs_done = 0,
55 .lock = PTHREAD_MUTEX_INITIALIZER,
56};
57
58/*
59 * Set custom signal mask to current thread.
60 */
61static
62void setmask(sigset_t *mask)
63{
64 int ret;
65
66 ret = sigemptyset(mask);
67 if (ret) {
68 PERROR("sigemptyset");
69 }
92816cc3 70 ret = sigaddset(mask, LTTNG_SESSIOND_SIG_QS);
d086f507
JD
71 if (ret) {
72 PERROR("sigaddset teardown");
73 }
74 ret = sigaddset(mask, LTTNG_SESSIOND_SIG_EXIT);
75 if (ret) {
76 PERROR("sigaddset exit");
77 }
92816cc3 78 ret = sigaddset(mask, LTTNG_SESSIOND_SIG_PENDING_ROTATION_CHECK);
d88744a4 79 if (ret) {
92816cc3 80 PERROR("sigaddset pending rotation check");
d88744a4 81 }
92816cc3 82 ret = sigaddset(mask, LTTNG_SESSIOND_SIG_SCHEDULED_ROTATION);
259c2674 83 if (ret) {
92816cc3 84 PERROR("sigaddset scheduled rotation");
259c2674 85 }
d086f507
JD
86}
87
88/*
92816cc3 89 * This is the same function as timer_signal_thread_qs, when it
d086f507
JD
90 * returns, it means that no timer signr is currently pending or being handled
91 * by the timer thread. This cannot be called from the timer thread.
92 */
93static
92816cc3 94void timer_signal_thread_qs(unsigned int signr)
d086f507
JD
95{
96 sigset_t pending_set;
97 int ret;
98
99 /*
100 * We need to be the only thread interacting with the thread
101 * that manages signals for teardown synchronization.
102 */
103 pthread_mutex_lock(&timer_signal.lock);
104
105 /* Ensure we don't have any signal queued for this session. */
106 for (;;) {
107 ret = sigemptyset(&pending_set);
108 if (ret == -1) {
109 PERROR("sigemptyset");
110 }
111 ret = sigpending(&pending_set);
112 if (ret == -1) {
113 PERROR("sigpending");
114 }
115 if (!sigismember(&pending_set, signr)) {
116 break;
117 }
118 caa_cpu_relax();
119 }
120
121 /*
122 * From this point, no new signal handler will be fired that would try to
123 * access "session". However, we still need to wait for any currently
124 * executing handler to complete.
125 */
126 cmm_smp_mb();
127 CMM_STORE_SHARED(timer_signal.qs_done, 0);
128 cmm_smp_mb();
129
130 /*
92816cc3 131 * Kill with LTTNG_SESSIOND_SIG_QS, so signal management thread
d086f507
JD
132 * wakes up.
133 */
92816cc3 134 kill(getpid(), LTTNG_SESSIOND_SIG_QS);
d086f507
JD
135
136 while (!CMM_LOAD_SHARED(timer_signal.qs_done)) {
137 caa_cpu_relax();
138 }
139 cmm_smp_mb();
140
141 pthread_mutex_unlock(&timer_signal.lock);
142}
143
144/*
145 * Start a timer on a session that will fire at a given interval
146 * (timer_interval_us) and fire a given signal (signal).
147 *
148 * Returns a negative value on error, 0 if a timer was created, and
149 * a positive value if no timer was created (not an error).
150 */
151static
c7031a2c 152int timer_start(timer_t *timer_id, struct ltt_session *session,
d086f507
JD
153 unsigned int timer_interval_us, int signal, bool one_shot)
154{
155 int ret = 0, delete_ret;
156 struct sigevent sev;
157 struct itimerspec its;
158
d086f507
JD
159 sev.sigev_notify = SIGEV_SIGNAL;
160 sev.sigev_signo = signal;
c7031a2c 161 sev.sigev_value.sival_ptr = session;
92816cc3 162 ret = timer_create(CLOCK_MONOTONIC, &sev, timer_id);
d086f507
JD
163 if (ret == -1) {
164 PERROR("timer_create");
165 goto end;
166 }
167
168 its.it_value.tv_sec = timer_interval_us / 1000000;
169 its.it_value.tv_nsec = (timer_interval_us % 1000000) * 1000;
170 if (one_shot) {
171 its.it_interval.tv_sec = 0;
172 its.it_interval.tv_nsec = 0;
173 } else {
174 its.it_interval.tv_sec = its.it_value.tv_sec;
175 its.it_interval.tv_nsec = its.it_value.tv_nsec;
176 }
177
178 ret = timer_settime(*timer_id, 0, &its, NULL);
179 if (ret == -1) {
180 PERROR("timer_settime");
181 goto error_destroy_timer;
182 }
183 goto end;
184
185error_destroy_timer:
186 delete_ret = timer_delete(*timer_id);
187 if (delete_ret == -1) {
188 PERROR("timer_delete");
189 }
190
191end:
192 return ret;
193}
194
195static
92816cc3 196int timer_stop(timer_t *timer_id, int signal)
d086f507
JD
197{
198 int ret = 0;
199
200 ret = timer_delete(*timer_id);
201 if (ret == -1) {
202 PERROR("timer_delete");
203 goto end;
204 }
205
92816cc3 206 timer_signal_thread_qs(signal);
d086f507
JD
207 *timer_id = 0;
208end:
209 return ret;
210}
211
92816cc3 212int timer_session_rotation_pending_check_start(struct ltt_session *session,
d88744a4
JD
213 unsigned int interval_us)
214{
215 int ret;
216
c7031a2c
JG
217 if (!session_get(session)) {
218 ret = -1;
219 goto end;
220 }
92816cc3
JG
221 DBG("Enabling session rotation pending check timer on session %" PRIu64,
222 session->id);
d88744a4
JD
223 /*
224 * We arm this timer in a one-shot mode so we don't have to disable it
92816cc3
JG
225 * explicitly (which could deadlock if the timer thread is blocked
226 * writing in the rotation_timer_pipe).
227 *
d88744a4 228 * Instead, we re-arm it if needed after the rotation_pending check as
92816cc3
JG
229 * returned. Also, this timer is usually only needed once, so there is
230 * no need to go through the whole signal teardown scheme everytime.
d88744a4 231 */
92816cc3 232 ret = timer_start(&session->rotation_pending_check_timer,
c7031a2c 233 session, interval_us,
92816cc3 234 LTTNG_SESSIOND_SIG_PENDING_ROTATION_CHECK,
d88744a4
JD
235 /* one-shot */ true);
236 if (ret == 0) {
92816cc3 237 session->rotation_pending_check_timer_enabled = true;
d88744a4 238 }
c7031a2c 239end:
d88744a4
JD
240 return ret;
241}
242
243/*
92816cc3 244 * Call with session and session_list locks held.
d88744a4 245 */
92816cc3 246int timer_session_rotation_pending_check_stop(struct ltt_session *session)
d88744a4
JD
247{
248 int ret;
249
250 assert(session);
3cf0ebee 251 assert(session->rotation_pending_check_timer_enabled);
d88744a4 252
92816cc3
JG
253 DBG("Disabling session rotation pending check timer on session %" PRIu64,
254 session->id);
255 ret = timer_stop(&session->rotation_pending_check_timer,
256 LTTNG_SESSIOND_SIG_PENDING_ROTATION_CHECK);
d88744a4 257 if (ret == -1) {
92816cc3 258 ERR("Failed to stop rotate_pending_check timer");
259c2674 259 } else {
92816cc3 260 session->rotation_pending_check_timer_enabled = false;
c7031a2c
JG
261 /*
262 * The timer's reference to the session can be released safely.
263 */
264 session_put(session);
259c2674
JD
265 }
266 return ret;
267}
268
92816cc3
JG
269/*
270 * Call with session and session_list locks held.
271 */
272int timer_session_rotation_schedule_timer_start(struct ltt_session *session,
259c2674
JD
273 unsigned int interval_us)
274{
275 int ret;
276
c7031a2c
JG
277 if (!session_get(session)) {
278 ret = -1;
279 goto end;
280 }
92816cc3 281 DBG("Enabling scheduled rotation timer on session \"%s\" (%ui µs)", session->name,
259c2674 282 interval_us);
c7031a2c 283 ret = timer_start(&session->rotation_schedule_timer, session,
92816cc3
JG
284 interval_us, LTTNG_SESSIOND_SIG_SCHEDULED_ROTATION,
285 /* one-shot */ false);
259c2674
JD
286 if (ret < 0) {
287 goto end;
288 }
92816cc3 289 session->rotation_schedule_timer_enabled = true;
259c2674
JD
290end:
291 return ret;
292}
293
294/*
92816cc3 295 * Call with session and session_list locks held.
259c2674 296 */
92816cc3 297int timer_session_rotation_schedule_timer_stop(struct ltt_session *session)
259c2674
JD
298{
299 int ret = 0;
300
301 assert(session);
302
92816cc3 303 if (!session->rotation_schedule_timer_enabled) {
259c2674
JD
304 goto end;
305 }
306
92816cc3
JG
307 DBG("Disabling scheduled rotation timer on session %s", session->name);
308 ret = timer_stop(&session->rotation_schedule_timer,
309 LTTNG_SESSIOND_SIG_SCHEDULED_ROTATION);
259c2674 310 if (ret < 0) {
92816cc3 311 ERR("Failed to stop scheduled rotation timer of session \"%s\"",
259c2674
JD
312 session->name);
313 goto end;
d88744a4
JD
314 }
315
92816cc3 316 session->rotation_schedule_timer_enabled = false;
c7031a2c
JG
317 /* The timer's reference to the session can be released safely. */
318 session_put(session);
259c2674
JD
319 ret = 0;
320end:
321 return ret;
d88744a4
JD
322}
323
d086f507
JD
324/*
325 * Block the RT signals for the entire process. It must be called from the
326 * sessiond main before creating the threads
327 */
92816cc3 328int timer_signal_init(void)
d086f507
JD
329{
330 int ret;
331 sigset_t mask;
332
333 /* Block signal for entire process, so only our thread processes it. */
334 setmask(&mask);
335 ret = pthread_sigmask(SIG_BLOCK, &mask, NULL);
336 if (ret) {
337 errno = ret;
338 PERROR("pthread_sigmask");
339 return -1;
340 }
341 return 0;
342}
343
344/*
345 * This thread is the sighandler for the timer signals.
346 */
92816cc3 347void *timer_thread_func(void *data)
d086f507
JD
348{
349 int signr;
350 sigset_t mask;
351 siginfo_t info;
352 struct timer_thread_parameters *ctx = data;
353
354 rcu_register_thread();
355 rcu_thread_online();
356
357 health_register(health_sessiond, HEALTH_SESSIOND_TYPE_TIMER);
d086f507
JD
358 health_code_update();
359
360 /* Only self thread will receive signal mask. */
361 setmask(&mask);
362 CMM_STORE_SHARED(timer_signal.tid, pthread_self());
363
364 while (1) {
365 health_code_update();
366
367 health_poll_entry();
368 signr = sigwaitinfo(&mask, &info);
369 health_poll_exit();
370
371 /*
372 * NOTE: cascading conditions are used instead of a switch case
373 * since the use of SIGRTMIN in the definition of the signals'
374 * values prevents the reduction to an integer constant.
375 */
376 if (signr == -1) {
377 if (errno != EINTR) {
378 PERROR("sigwaitinfo");
379 }
380 continue;
92816cc3 381 } else if (signr == LTTNG_SESSIOND_SIG_QS) {
d086f507
JD
382 cmm_smp_mb();
383 CMM_STORE_SHARED(timer_signal.qs_done, 1);
384 cmm_smp_mb();
d086f507
JD
385 } else if (signr == LTTNG_SESSIOND_SIG_EXIT) {
386 goto end;
92816cc3 387 } else if (signr == LTTNG_SESSIOND_SIG_PENDING_ROTATION_CHECK) {
c7031a2c
JG
388 struct ltt_session *session =
389 (struct ltt_session *) info.si_value.sival_ptr;
390
3cf0ebee
JG
391 session_lock_list();
392 session_lock(session);
393 /* Acquires a reference to the session. */
92816cc3
JG
394 rotation_thread_enqueue_job(ctx->rotation_thread_job_queue,
395 ROTATION_THREAD_JOB_TYPE_CHECK_PENDING_ROTATION,
c7031a2c 396 session);
3cf0ebee
JG
397 /* Release the timer's reference to the session. */
398 (void) timer_session_rotation_pending_check_stop(session);
c7031a2c 399 session_unlock_list();
92816cc3
JG
400 } else if (signr == LTTNG_SESSIOND_SIG_SCHEDULED_ROTATION) {
401 rotation_thread_enqueue_job(ctx->rotation_thread_job_queue,
402 ROTATION_THREAD_JOB_TYPE_SCHEDULED_ROTATION,
c7031a2c
JG
403 (struct ltt_session *) info.si_value.sival_ptr);
404 /*
405 * The scheduled periodic rotation timer is not in
406 * "one-shot" mode. The reference to the session is not
407 * released since the timer is still enabled and can
408 * still fire.
409 */
d086f507
JD
410 } else {
411 ERR("Unexpected signal %d\n", info.si_signo);
412 }
413 }
414
415end:
416 DBG("[timer-thread] Exit");
417 health_unregister(health_sessiond);
418 rcu_thread_offline();
419 rcu_unregister_thread();
420 return NULL;
421}
92816cc3
JG
422
423void timer_exit(void)
424{
425 kill(getpid(), LTTNG_SESSIOND_SIG_EXIT);
426}
This page took 0.046845 seconds and 5 git commands to generate.