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