Refactoring: use an opaque lttng_tracker_id type
[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"
bc26e826 27#include "thread.h"
d086f507 28
92816cc3
JG
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
33
34#define UINT_TO_PTR(value) \
35 ({ \
36 assert(value <= UINTPTR_MAX); \
37 (void *) (uintptr_t) value; \
38 })
39#define PTR_TO_UINT(ptr) ((uintptr_t) ptr)
40
41/*
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
45 * exclusion.
46 */
d086f507 47static
92816cc3
JG
48struct timer_signal_data {
49 /* Thread managing signals. */
50 pthread_t tid;
51 int qs_done;
52 pthread_mutex_t lock;
53} timer_signal = {
d086f507
JD
54 .tid = 0,
55 .qs_done = 0,
56 .lock = PTHREAD_MUTEX_INITIALIZER,
57};
58
59/*
60 * Set custom signal mask to current thread.
61 */
62static
63void setmask(sigset_t *mask)
64{
65 int ret;
66
67 ret = sigemptyset(mask);
68 if (ret) {
69 PERROR("sigemptyset");
70 }
92816cc3 71 ret = sigaddset(mask, LTTNG_SESSIOND_SIG_QS);
d086f507
JD
72 if (ret) {
73 PERROR("sigaddset teardown");
74 }
75 ret = sigaddset(mask, LTTNG_SESSIOND_SIG_EXIT);
76 if (ret) {
77 PERROR("sigaddset exit");
78 }
92816cc3 79 ret = sigaddset(mask, LTTNG_SESSIOND_SIG_PENDING_ROTATION_CHECK);
d88744a4 80 if (ret) {
92816cc3 81 PERROR("sigaddset pending rotation check");
d88744a4 82 }
92816cc3 83 ret = sigaddset(mask, LTTNG_SESSIOND_SIG_SCHEDULED_ROTATION);
259c2674 84 if (ret) {
92816cc3 85 PERROR("sigaddset scheduled rotation");
259c2674 86 }
d086f507
JD
87}
88
89/*
92816cc3 90 * This is the same function as timer_signal_thread_qs, when it
d086f507
JD
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.
93 */
94static
92816cc3 95void timer_signal_thread_qs(unsigned int signr)
d086f507
JD
96{
97 sigset_t pending_set;
98 int ret;
99
100 /*
101 * We need to be the only thread interacting with the thread
102 * that manages signals for teardown synchronization.
103 */
104 pthread_mutex_lock(&timer_signal.lock);
105
106 /* Ensure we don't have any signal queued for this session. */
107 for (;;) {
108 ret = sigemptyset(&pending_set);
109 if (ret == -1) {
110 PERROR("sigemptyset");
111 }
112 ret = sigpending(&pending_set);
113 if (ret == -1) {
114 PERROR("sigpending");
115 }
116 if (!sigismember(&pending_set, signr)) {
117 break;
118 }
119 caa_cpu_relax();
120 }
121
122 /*
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.
126 */
127 cmm_smp_mb();
128 CMM_STORE_SHARED(timer_signal.qs_done, 0);
129 cmm_smp_mb();
130
131 /*
92816cc3 132 * Kill with LTTNG_SESSIOND_SIG_QS, so signal management thread
d086f507
JD
133 * wakes up.
134 */
92816cc3 135 kill(getpid(), LTTNG_SESSIOND_SIG_QS);
d086f507
JD
136
137 while (!CMM_LOAD_SHARED(timer_signal.qs_done)) {
138 caa_cpu_relax();
139 }
140 cmm_smp_mb();
141
142 pthread_mutex_unlock(&timer_signal.lock);
143}
144
145/*
146 * Start a timer on a session that will fire at a given interval
147 * (timer_interval_us) and fire a given signal (signal).
148 *
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).
151 */
152static
c7031a2c 153int timer_start(timer_t *timer_id, struct ltt_session *session,
d086f507
JD
154 unsigned int timer_interval_us, int signal, bool one_shot)
155{
156 int ret = 0, delete_ret;
157 struct sigevent sev;
158 struct itimerspec its;
159
d086f507
JD
160 sev.sigev_notify = SIGEV_SIGNAL;
161 sev.sigev_signo = signal;
c7031a2c 162 sev.sigev_value.sival_ptr = session;
92816cc3 163 ret = timer_create(CLOCK_MONOTONIC, &sev, timer_id);
d086f507
JD
164 if (ret == -1) {
165 PERROR("timer_create");
166 goto end;
167 }
168
169 its.it_value.tv_sec = timer_interval_us / 1000000;
170 its.it_value.tv_nsec = (timer_interval_us % 1000000) * 1000;
171 if (one_shot) {
172 its.it_interval.tv_sec = 0;
173 its.it_interval.tv_nsec = 0;
174 } else {
175 its.it_interval.tv_sec = its.it_value.tv_sec;
176 its.it_interval.tv_nsec = its.it_value.tv_nsec;
177 }
178
179 ret = timer_settime(*timer_id, 0, &its, NULL);
180 if (ret == -1) {
181 PERROR("timer_settime");
182 goto error_destroy_timer;
183 }
184 goto end;
185
186error_destroy_timer:
187 delete_ret = timer_delete(*timer_id);
188 if (delete_ret == -1) {
189 PERROR("timer_delete");
190 }
191
192end:
193 return ret;
194}
195
196static
92816cc3 197int timer_stop(timer_t *timer_id, int signal)
d086f507
JD
198{
199 int ret = 0;
200
201 ret = timer_delete(*timer_id);
202 if (ret == -1) {
203 PERROR("timer_delete");
204 goto end;
205 }
206
92816cc3 207 timer_signal_thread_qs(signal);
d086f507
JD
208 *timer_id = 0;
209end:
210 return ret;
211}
212
92816cc3 213int timer_session_rotation_pending_check_start(struct ltt_session *session,
d88744a4
JD
214 unsigned int interval_us)
215{
216 int ret;
217
c7031a2c
JG
218 if (!session_get(session)) {
219 ret = -1;
220 goto end;
221 }
92816cc3
JG
222 DBG("Enabling session rotation pending check timer on session %" PRIu64,
223 session->id);
d88744a4
JD
224 /*
225 * We arm this timer in a one-shot mode so we don't have to disable it
92816cc3
JG
226 * explicitly (which could deadlock if the timer thread is blocked
227 * writing in the rotation_timer_pipe).
228 *
d88744a4 229 * Instead, we re-arm it if needed after the rotation_pending check as
92816cc3
JG
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.
d88744a4 232 */
92816cc3 233 ret = timer_start(&session->rotation_pending_check_timer,
c7031a2c 234 session, interval_us,
92816cc3 235 LTTNG_SESSIOND_SIG_PENDING_ROTATION_CHECK,
d88744a4
JD
236 /* one-shot */ true);
237 if (ret == 0) {
92816cc3 238 session->rotation_pending_check_timer_enabled = true;
d88744a4 239 }
c7031a2c 240end:
d88744a4
JD
241 return ret;
242}
243
244/*
92816cc3 245 * Call with session and session_list locks held.
d88744a4 246 */
92816cc3 247int timer_session_rotation_pending_check_stop(struct ltt_session *session)
d88744a4
JD
248{
249 int ret;
250
251 assert(session);
3cf0ebee 252 assert(session->rotation_pending_check_timer_enabled);
d88744a4 253
92816cc3
JG
254 DBG("Disabling session rotation pending check timer on session %" PRIu64,
255 session->id);
256 ret = timer_stop(&session->rotation_pending_check_timer,
257 LTTNG_SESSIOND_SIG_PENDING_ROTATION_CHECK);
d88744a4 258 if (ret == -1) {
92816cc3 259 ERR("Failed to stop rotate_pending_check timer");
259c2674 260 } else {
92816cc3 261 session->rotation_pending_check_timer_enabled = false;
c7031a2c
JG
262 /*
263 * The timer's reference to the session can be released safely.
264 */
265 session_put(session);
259c2674
JD
266 }
267 return ret;
268}
269
92816cc3
JG
270/*
271 * Call with session and session_list locks held.
272 */
273int timer_session_rotation_schedule_timer_start(struct ltt_session *session,
259c2674
JD
274 unsigned int interval_us)
275{
276 int ret;
277
c7031a2c
JG
278 if (!session_get(session)) {
279 ret = -1;
280 goto end;
281 }
2a1135fa
JG
282 DBG("Enabling scheduled rotation timer on session \"%s\" (%ui %s)", session->name,
283 interval_us, USEC_UNIT);
c7031a2c 284 ret = timer_start(&session->rotation_schedule_timer, session,
92816cc3
JG
285 interval_us, LTTNG_SESSIOND_SIG_SCHEDULED_ROTATION,
286 /* one-shot */ false);
259c2674
JD
287 if (ret < 0) {
288 goto end;
289 }
92816cc3 290 session->rotation_schedule_timer_enabled = true;
259c2674
JD
291end:
292 return ret;
293}
294
295/*
92816cc3 296 * Call with session and session_list locks held.
259c2674 297 */
92816cc3 298int timer_session_rotation_schedule_timer_stop(struct ltt_session *session)
259c2674
JD
299{
300 int ret = 0;
301
302 assert(session);
303
92816cc3 304 if (!session->rotation_schedule_timer_enabled) {
259c2674
JD
305 goto end;
306 }
307
92816cc3
JG
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);
259c2674 311 if (ret < 0) {
92816cc3 312 ERR("Failed to stop scheduled rotation timer of session \"%s\"",
259c2674
JD
313 session->name);
314 goto end;
d88744a4
JD
315 }
316
92816cc3 317 session->rotation_schedule_timer_enabled = false;
c7031a2c
JG
318 /* The timer's reference to the session can be released safely. */
319 session_put(session);
259c2674
JD
320 ret = 0;
321end:
322 return ret;
d88744a4
JD
323}
324
d086f507
JD
325/*
326 * Block the RT signals for the entire process. It must be called from the
327 * sessiond main before creating the threads
328 */
92816cc3 329int timer_signal_init(void)
d086f507
JD
330{
331 int ret;
332 sigset_t mask;
333
334 /* Block signal for entire process, so only our thread processes it. */
335 setmask(&mask);
336 ret = pthread_sigmask(SIG_BLOCK, &mask, NULL);
337 if (ret) {
338 errno = ret;
339 PERROR("pthread_sigmask");
340 return -1;
341 }
342 return 0;
343}
344
345/*
346 * This thread is the sighandler for the timer signals.
347 */
bc26e826
JG
348static
349void *thread_timer(void *data)
d086f507
JD
350{
351 int signr;
352 sigset_t mask;
353 siginfo_t info;
354 struct timer_thread_parameters *ctx = data;
355
356 rcu_register_thread();
357 rcu_thread_online();
358
359 health_register(health_sessiond, HEALTH_SESSIOND_TYPE_TIMER);
d086f507
JD
360 health_code_update();
361
362 /* Only self thread will receive signal mask. */
363 setmask(&mask);
364 CMM_STORE_SHARED(timer_signal.tid, pthread_self());
365
366 while (1) {
367 health_code_update();
368
369 health_poll_entry();
370 signr = sigwaitinfo(&mask, &info);
371 health_poll_exit();
372
373 /*
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.
377 */
378 if (signr == -1) {
379 if (errno != EINTR) {
380 PERROR("sigwaitinfo");
381 }
382 continue;
92816cc3 383 } else if (signr == LTTNG_SESSIOND_SIG_QS) {
d086f507
JD
384 cmm_smp_mb();
385 CMM_STORE_SHARED(timer_signal.qs_done, 1);
386 cmm_smp_mb();
d086f507
JD
387 } else if (signr == LTTNG_SESSIOND_SIG_EXIT) {
388 goto end;
92816cc3 389 } else if (signr == LTTNG_SESSIOND_SIG_PENDING_ROTATION_CHECK) {
c7031a2c
JG
390 struct ltt_session *session =
391 (struct ltt_session *) info.si_value.sival_ptr;
392
92816cc3
JG
393 rotation_thread_enqueue_job(ctx->rotation_thread_job_queue,
394 ROTATION_THREAD_JOB_TYPE_CHECK_PENDING_ROTATION,
c7031a2c 395 session);
92816cc3
JG
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,
c7031a2c
JG
399 (struct ltt_session *) info.si_value.sival_ptr);
400 /*
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
404 * still fire.
405 */
d086f507
JD
406 } else {
407 ERR("Unexpected signal %d\n", info.si_signo);
408 }
409 }
410
411end:
412 DBG("[timer-thread] Exit");
413 health_unregister(health_sessiond);
414 rcu_thread_offline();
415 rcu_unregister_thread();
416 return NULL;
417}
92816cc3 418
bc26e826
JG
419static
420bool shutdown_timer_thread(void *data)
421{
422 return kill(getpid(), LTTNG_SESSIOND_SIG_EXIT) == 0;
423}
424
425bool launch_timer_thread(
426 struct timer_thread_parameters *timer_thread_parameters)
92816cc3 427{
bc26e826
JG
428 struct lttng_thread *thread;
429
430 thread = lttng_thread_create("Timer",
431 thread_timer,
432 shutdown_timer_thread,
433 NULL,
434 timer_thread_parameters);
435 if (!thread) {
436 goto error;
437 }
438 lttng_thread_put(thread);
439 return true;
440error:
441 return false;
92816cc3 442}
This page took 0.050747 seconds and 4 git commands to generate.