document metadata_switch_timer() deadlock
[lttng-tools.git] / src / common / consumer-timer.c
CommitLineData
331744e3
JD
1/*
2 * Copyright (C) 2012 - Julien Desfossez <julien.desfossez@efficios.com>
3 * David Goulet <dgoulet@efficios.com>
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 _GNU_SOURCE
20#include <assert.h>
21#include <inttypes.h>
22#include <signal.h>
23
24#include <common/common.h>
25
26#include "consumer-timer.h"
27#include "ust-consumer/ust-consumer.h"
28
2b8f8754
MD
29static struct timer_signal_data timer_signal = {
30 .tid = 0,
31 .setup_done = 0,
32 .qs_done = 0,
33 .lock = PTHREAD_MUTEX_INITIALIZER,
34};
331744e3
JD
35
36/*
37 * Set custom signal mask to current thread.
38 */
39static void setmask(sigset_t *mask)
40{
41 int ret;
42
43 ret = sigemptyset(mask);
44 if (ret) {
45 PERROR("sigemptyset");
46 }
47 ret = sigaddset(mask, LTTNG_CONSUMER_SIG_SWITCH);
48 if (ret) {
49 PERROR("sigaddset");
50 }
51 ret = sigaddset(mask, LTTNG_CONSUMER_SIG_TEARDOWN);
52 if (ret) {
53 PERROR("sigaddset");
54 }
55}
56
57/*
58 * Execute action on a timer switch.
d98a47c7
MD
59 *
60 * Beware: metadata_switch_timer() should *never* take a mutex also held
61 * while consumer_timer_switch_stop() is called. It would result in
62 * deadlocks.
331744e3
JD
63 */
64static void metadata_switch_timer(struct lttng_consumer_local_data *ctx,
65 int sig, siginfo_t *si, void *uc)
66{
67 int ret;
68 struct lttng_consumer_channel *channel;
69
70 channel = si->si_value.sival_ptr;
71 assert(channel);
72
4419b4fb
MD
73 if (channel->switch_timer_error) {
74 return;
75 }
76
331744e3
JD
77 DBG("Switch timer for channel %" PRIu64, channel->key);
78 switch (ctx->type) {
79 case LTTNG_CONSUMER32_UST:
80 case LTTNG_CONSUMER64_UST:
4fa3dc0e
MD
81 /*
82 * Locks taken by lttng_ustconsumer_request_metadata():
83 * - metadata_socket_lock
84 * - Calling lttng_ustconsumer_recv_metadata():
85 * - consumer_data.lock
86 * - channel->lock
87 * - channel->metadata_cache->lock
88 * - Calling consumer_metadata_cache_flushed():
89 * - consumer_data.lock
90 * - channel->lock
91 * - channel->metadata_cache->lock
92 *
93 * Both consumer_data.lock and channel->lock currently
94 * cause a deadlock, since they are held while
95 * consumer_timer_switch_stop() is called.
96 */
331744e3
JD
97 ret = lttng_ustconsumer_request_metadata(ctx, channel);
98 if (ret < 0) {
4419b4fb 99 channel->switch_timer_error = 1;
331744e3
JD
100 }
101 break;
102 case LTTNG_CONSUMER_KERNEL:
103 case LTTNG_CONSUMER_UNKNOWN:
104 assert(0);
105 break;
106 }
107}
108
2b8f8754
MD
109static
110void consumer_timer_signal_thread_qs(unsigned int signr)
111{
112 sigset_t pending_set;
113 int ret;
114
115 /*
116 * We need to be the only thread interacting with the thread
117 * that manages signals for teardown synchronization.
118 */
119 pthread_mutex_lock(&timer_signal.lock);
120
121 /* Ensure we don't have any signal queued for this channel. */
122 for (;;) {
123 ret = sigemptyset(&pending_set);
124 if (ret == -1) {
125 PERROR("sigemptyset");
126 }
127 ret = sigpending(&pending_set);
128 if (ret == -1) {
129 PERROR("sigpending");
130 }
131 if (!sigismember(&pending_set, LTTNG_CONSUMER_SIG_SWITCH)) {
132 break;
133 }
134 caa_cpu_relax();
135 }
136
137 /*
138 * From this point, no new signal handler will be fired that would try to
139 * access "chan". However, we still need to wait for any currently
140 * executing handler to complete.
141 */
142 cmm_smp_mb();
143 CMM_STORE_SHARED(timer_signal.qs_done, 0);
144 cmm_smp_mb();
145
146 /*
147 * Kill with LTTNG_CONSUMER_SIG_TEARDOWN, so signal management thread wakes
148 * up.
149 */
150 kill(getpid(), LTTNG_CONSUMER_SIG_TEARDOWN);
151
152 while (!CMM_LOAD_SHARED(timer_signal.qs_done)) {
153 caa_cpu_relax();
154 }
155 cmm_smp_mb();
156
157 pthread_mutex_unlock(&timer_signal.lock);
158}
159
331744e3
JD
160/*
161 * Set the timer for periodical metadata flush.
162 */
163void consumer_timer_switch_start(struct lttng_consumer_channel *channel,
164 unsigned int switch_timer_interval)
165{
166 int ret;
167 struct sigevent sev;
168 struct itimerspec its;
169
170 assert(channel);
171 assert(channel->key);
172
173 if (switch_timer_interval == 0) {
174 return;
175 }
176
177 sev.sigev_notify = SIGEV_SIGNAL;
178 sev.sigev_signo = LTTNG_CONSUMER_SIG_SWITCH;
179 sev.sigev_value.sival_ptr = channel;
180 ret = timer_create(CLOCKID, &sev, &channel->switch_timer);
181 if (ret == -1) {
182 PERROR("timer_create");
183 }
184 channel->switch_timer_enabled = 1;
185
186 its.it_value.tv_sec = switch_timer_interval / 1000000;
187 its.it_value.tv_nsec = switch_timer_interval % 1000000;
188 its.it_interval.tv_sec = its.it_value.tv_sec;
189 its.it_interval.tv_nsec = its.it_value.tv_nsec;
190
191 ret = timer_settime(channel->switch_timer, 0, &its, NULL);
192 if (ret == -1) {
193 PERROR("timer_settime");
194 }
195}
196
197/*
198 * Stop and delete timer.
199 */
200void consumer_timer_switch_stop(struct lttng_consumer_channel *channel)
201{
202 int ret;
331744e3
JD
203
204 assert(channel);
205
206 ret = timer_delete(channel->switch_timer);
207 if (ret == -1) {
208 PERROR("timer_delete");
209 }
210
2b8f8754 211 consumer_timer_signal_thread_qs(LTTNG_CONSUMER_SIG_SWITCH);
331744e3 212
2b8f8754
MD
213 channel->switch_timer = 0;
214 channel->switch_timer_enabled = 0;
331744e3
JD
215}
216
217/*
218 * Block the RT signals for the entire process. It must be called from the
219 * consumer main before creating the threads
220 */
221void consumer_signal_init(void)
222{
223 int ret;
224 sigset_t mask;
225
226 /* Block signal for entire process, so only our thread processes it. */
227 setmask(&mask);
228 ret = pthread_sigmask(SIG_BLOCK, &mask, NULL);
229 if (ret) {
230 errno = ret;
231 PERROR("pthread_sigmask");
232 }
233}
234
235/*
236 * This thread is the sighandler for signals LTTNG_CONSUMER_SIG_SWITCH and
237 * LTTNG_CONSUMER_SIG_TEARDOWN that are emitted by the periodic timer to check
238 * if new metadata is available.
239 */
240void *consumer_timer_metadata_thread(void *data)
241{
242 int signr;
243 sigset_t mask;
244 siginfo_t info;
245 struct lttng_consumer_local_data *ctx = data;
246
247 /* Only self thread will receive signal mask. */
248 setmask(&mask);
249 CMM_STORE_SHARED(timer_signal.tid, pthread_self());
250
251 while (1) {
252 signr = sigwaitinfo(&mask, &info);
253 if (signr == -1) {
254 if (errno != EINTR) {
255 PERROR("sigwaitinfo");
256 }
257 continue;
258 } else if (signr == LTTNG_CONSUMER_SIG_SWITCH) {
259 metadata_switch_timer(ctx, info.si_signo, &info, NULL);
260 } else if (signr == LTTNG_CONSUMER_SIG_TEARDOWN) {
261 cmm_smp_mb();
262 CMM_STORE_SHARED(timer_signal.qs_done, 1);
263 cmm_smp_mb();
264 DBG("Signal timer metadata thread teardown");
265 } else {
266 ERR("Unexpected signal %d\n", info.si_signo);
267 }
268 }
269
270 return NULL;
271}
This page took 0.033225 seconds and 4 git commands to generate.