Sessiond rotation thread
[lttng-tools.git] / src / bin / lttng-sessiond / rotation-thread.c
1 /*
2 * Copyright (C) 2017 - Julien Desfossez <jdesfossez@efficios.com>
3 *
4 * This program is free software; you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License, version 2 only, as
6 * published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful, but WITHOUT
9 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
10 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
11 * more details.
12 *
13 * You should have received a copy of the GNU General Public License along with
14 * this program; if not, write to the Free Software Foundation, Inc., 51
15 * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
16 */
17
18 #define _LGPL_SOURCE
19 #include <lttng/trigger/trigger.h>
20 #include <common/error.h>
21 #include <common/config/session-config.h>
22 #include <common/defaults.h>
23 #include <common/utils.h>
24 #include <common/futex.h>
25 #include <common/align.h>
26 #include <common/time.h>
27 #include <common/hashtable/utils.h>
28 #include <sys/eventfd.h>
29 #include <sys/stat.h>
30 #include <time.h>
31 #include <signal.h>
32 #include <inttypes.h>
33
34 #include <common/kernel-ctl/kernel-ctl.h>
35 #include <lttng/notification/channel-internal.h>
36
37 #include "rotation-thread.h"
38 #include "lttng-sessiond.h"
39 #include "health-sessiond.h"
40 #include "rotate.h"
41 #include "cmd.h"
42 #include "session.h"
43
44 #include <urcu.h>
45 #include <urcu/list.h>
46 #include <urcu/rculfhash.h>
47
48 /*
49 * Store a struct rotation_channel_info for each channel that is currently
50 * being rotated by the consumer.
51 */
52 struct cds_lfht *channel_pending_rotate_ht;
53
54 struct rotation_thread_state {
55 struct lttng_poll_event events;
56 };
57
58 static
59 void channel_rotation_info_destroy(struct rotation_channel_info *channel_info)
60 {
61 assert(channel_info);
62 free(channel_info);
63 }
64
65 static
66 int match_channel_info(struct cds_lfht_node *node, const void *key)
67 {
68 struct rotation_channel_key *channel_key = (struct rotation_channel_key *) key;
69 struct rotation_channel_info *channel_info;
70
71 channel_info = caa_container_of(node, struct rotation_channel_info,
72 rotate_channels_ht_node);
73
74 return !!((channel_key->key == channel_info->channel_key.key) &&
75 (channel_key->domain == channel_info->channel_key.domain));
76 }
77
78 static
79 struct rotation_channel_info *lookup_channel_pending(uint64_t key,
80 enum lttng_domain_type domain)
81 {
82 struct cds_lfht_iter iter;
83 struct cds_lfht_node *node;
84 struct rotation_channel_info *channel_info = NULL;
85 struct rotation_channel_key channel_key = { .key = key,
86 .domain = domain };
87
88 cds_lfht_lookup(channel_pending_rotate_ht,
89 hash_channel_key(&channel_key),
90 match_channel_info,
91 &channel_key, &iter);
92 node = cds_lfht_iter_get_node(&iter);
93 if (!node) {
94 goto end;
95 }
96
97 channel_info = caa_container_of(node, struct rotation_channel_info,
98 rotate_channels_ht_node);
99 cds_lfht_del(channel_pending_rotate_ht, node);
100 end:
101 return channel_info;
102 }
103
104 /*
105 * Destroy the thread data previously created by the init function.
106 */
107 void rotation_thread_handle_destroy(
108 struct rotation_thread_handle *handle)
109 {
110 int ret;
111
112 if (!handle) {
113 goto end;
114 }
115
116 if (handle->ust32_consumer >= 0) {
117 ret = close(handle->ust32_consumer);
118 if (ret) {
119 PERROR("close 32-bit consumer channel rotation pipe");
120 }
121 }
122 if (handle->ust64_consumer >= 0) {
123 ret = close(handle->ust64_consumer);
124 if (ret) {
125 PERROR("close 64-bit consumer channel rotation pipe");
126 }
127 }
128 if (handle->kernel_consumer >= 0) {
129 ret = close(handle->kernel_consumer);
130 if (ret) {
131 PERROR("close kernel consumer channel rotation pipe");
132 }
133 }
134
135 end:
136 free(handle);
137 }
138
139 struct rotation_thread_handle *rotation_thread_handle_create(
140 struct lttng_pipe *ust32_channel_rotate_pipe,
141 struct lttng_pipe *ust64_channel_rotate_pipe,
142 struct lttng_pipe *kernel_channel_rotate_pipe,
143 int thread_quit_pipe)
144 {
145 struct rotation_thread_handle *handle;
146
147 handle = zmalloc(sizeof(*handle));
148 if (!handle) {
149 goto end;
150 }
151
152 if (ust32_channel_rotate_pipe) {
153 handle->ust32_consumer =
154 lttng_pipe_release_readfd(
155 ust32_channel_rotate_pipe);
156 if (handle->ust32_consumer < 0) {
157 goto error;
158 }
159 } else {
160 handle->ust32_consumer = -1;
161 }
162 if (ust64_channel_rotate_pipe) {
163 handle->ust64_consumer =
164 lttng_pipe_release_readfd(
165 ust64_channel_rotate_pipe);
166 if (handle->ust64_consumer < 0) {
167 goto error;
168 }
169 } else {
170 handle->ust64_consumer = -1;
171 }
172 if (kernel_channel_rotate_pipe) {
173 handle->kernel_consumer =
174 lttng_pipe_release_readfd(
175 kernel_channel_rotate_pipe);
176 if (handle->kernel_consumer < 0) {
177 goto error;
178 }
179 } else {
180 handle->kernel_consumer = -1;
181 }
182 handle->thread_quit_pipe = thread_quit_pipe;
183
184 end:
185 return handle;
186 error:
187 rotation_thread_handle_destroy(handle);
188 return NULL;
189 }
190
191 static
192 int init_poll_set(struct lttng_poll_event *poll_set,
193 struct rotation_thread_handle *handle)
194 {
195 int ret;
196
197 /*
198 * Create pollset with size 4:
199 * - sessiond quit pipe
200 * - consumerd (32-bit user space) channel rotate pipe,
201 * - consumerd (64-bit user space) channel rotate pipe,
202 * - consumerd (kernel) channel rotate pipe,
203 */
204 ret = lttng_poll_create(poll_set, 4, LTTNG_CLOEXEC);
205 if (ret < 0) {
206 goto end;
207 }
208
209 ret = lttng_poll_add(poll_set, handle->thread_quit_pipe,
210 LPOLLIN | LPOLLERR);
211 if (ret < 0) {
212 ERR("[rotation-thread] Failed to add thread_quit_pipe fd to pollset");
213 goto error;
214 }
215 ret = lttng_poll_add(poll_set, handle->ust32_consumer,
216 LPOLLIN | LPOLLERR);
217 if (ret < 0) {
218 ERR("[rotation-thread] Failed to add ust-32 channel rotation pipe fd to pollset");
219 goto error;
220 }
221 ret = lttng_poll_add(poll_set, handle->ust64_consumer,
222 LPOLLIN | LPOLLERR);
223 if (ret < 0) {
224 ERR("[rotation-thread] Failed to add ust-64 channel rotation pipe fd to pollset");
225 goto error;
226 }
227 if (handle->kernel_consumer >= 0) {
228 ret = lttng_poll_add(poll_set, handle->kernel_consumer,
229 LPOLLIN | LPOLLERR);
230 if (ret < 0) {
231 ERR("[rotation-thread] Failed to add kernel channel rotation pipe fd to pollset");
232 goto error;
233 }
234 }
235
236 end:
237 return ret;
238 error:
239 lttng_poll_clean(poll_set);
240 return ret;
241 }
242
243 static
244 void fini_thread_state(struct rotation_thread_state *state)
245 {
246 lttng_poll_clean(&state->events);
247 cds_lfht_destroy(channel_pending_rotate_ht, NULL);
248 }
249
250 static
251 int init_thread_state(struct rotation_thread_handle *handle,
252 struct rotation_thread_state *state)
253 {
254 int ret;
255
256 memset(state, 0, sizeof(*state));
257 lttng_poll_init(&state->events);
258
259 ret = init_poll_set(&state->events, handle);
260 if (ret) {
261 ERR("[rotation-thread] Failed to initialize rotation thread poll set");
262 goto end;
263 }
264
265 channel_pending_rotate_ht = cds_lfht_new(DEFAULT_HT_SIZE,
266 1, 0, CDS_LFHT_AUTO_RESIZE | CDS_LFHT_ACCOUNTING, NULL);
267 if (!channel_pending_rotate_ht) {
268 ERR("[rotation-thread] Failed to create channel pending rotation hash table");
269 ret = -1;
270 goto end;
271 }
272
273 end:
274 return ret;
275 }
276
277 static
278 int handle_channel_rotation_pipe(int fd, uint32_t revents,
279 struct rotation_thread_handle *handle,
280 struct rotation_thread_state *state)
281 {
282 int ret = 0;
283 enum lttng_domain_type domain;
284 struct rotation_channel_info *channel_info;
285 struct ltt_session *session = NULL;
286 uint64_t key;
287
288 if (fd == handle->ust32_consumer ||
289 fd == handle->ust64_consumer) {
290 domain = LTTNG_DOMAIN_UST;
291 } else if (fd == handle->kernel_consumer) {
292 domain = LTTNG_DOMAIN_KERNEL;
293 } else {
294 ERR("[rotation-thread] Unknown channel rotation pipe fd %d",
295 fd);
296 abort();
297 }
298
299 if (revents & (LPOLLERR | LPOLLHUP | LPOLLRDHUP)) {
300 ret = lttng_poll_del(&state->events, fd);
301 if (ret) {
302 ERR("[rotation-thread] Failed to remove consumer "
303 "rotation pipe from poll set");
304 }
305 goto end;
306 }
307
308 do {
309 ret = read(fd, &key, sizeof(key));
310 } while (ret == -1 && errno == EINTR);
311 if (ret != sizeof(key)) {
312 ERR("[rotation-thread] Failed to read from pipe (fd = %i)",
313 fd);
314 ret = -1;
315 goto end;
316 }
317
318 DBG("[rotation-thread] Received notification for chan %" PRIu64
319 ", domain %d\n", key, domain);
320
321 channel_info = lookup_channel_pending(key, domain);
322 if (!channel_info) {
323 ERR("[rotation-thread] Failed to find channel_info (key = %"
324 PRIu64 ")", key);
325 ret = -1;
326 goto end;
327 }
328 rcu_read_lock();
329 session_lock_list();
330 session = session_find_by_id(channel_info->session_id);
331 if (!session) {
332 /*
333 * The session may have been destroyed before we had a chance to
334 * perform this action, return gracefully.
335 */
336 DBG("[rotation-thread] Session %" PRIu64 " not found",
337 channel_info->session_id);
338 ret = 0;
339 goto end_unlock_session_list;
340 }
341
342 session_lock(session);
343 if (--session->nr_chan_rotate_pending == 0) {
344 time_t now = time(NULL);
345
346 if (now == (time_t) -1) {
347 session->rotation_status = LTTNG_ROTATION_STATUS_ERROR;
348 ret = LTTNG_ERR_UNK;
349 goto end_unlock_session;
350 }
351
352 ret = rename_complete_chunk(session, now);
353 if (ret < 0) {
354 ERR("Failed to rename completed rotation chunk");
355 goto end_unlock_session;
356 }
357 session->rotate_pending = false;
358 session->rotation_status = LTTNG_ROTATION_STATUS_COMPLETED;
359 session->last_chunk_start_ts = session->current_chunk_start_ts;
360 DBG("Rotation completed for session %s", session->name);
361 }
362
363 ret = 0;
364
365 end_unlock_session:
366 channel_rotation_info_destroy(channel_info);
367 session_unlock(session);
368 end_unlock_session_list:
369 session_unlock_list();
370 rcu_read_unlock();
371 end:
372 return ret;
373 }
374
375 void *thread_rotation(void *data)
376 {
377 int ret;
378 struct rotation_thread_handle *handle = data;
379 struct rotation_thread_state state;
380
381 DBG("[rotation-thread] Started rotation thread");
382
383 if (!handle) {
384 ERR("[rotation-thread] Invalid thread context provided");
385 goto end;
386 }
387
388 rcu_register_thread();
389 rcu_thread_online();
390
391 health_register(health_sessiond, HEALTH_SESSIOND_TYPE_ROTATION);
392 health_code_update();
393
394 ret = init_thread_state(handle, &state);
395 if (ret) {
396 goto end;
397 }
398
399 /* Ready to handle client connections. */
400 sessiond_notify_ready();
401
402 while (true) {
403 int fd_count, i;
404
405 health_poll_entry();
406 DBG("[rotation-thread] Entering poll wait");
407 ret = lttng_poll_wait(&state.events, -1);
408 DBG("[rotation-thread] Poll wait returned (%i)", ret);
409 health_poll_exit();
410 if (ret < 0) {
411 /*
412 * Restart interrupted system call.
413 */
414 if (errno == EINTR) {
415 continue;
416 }
417 ERR("[rotation-thread] Error encountered during lttng_poll_wait (%i)", ret);
418 goto error;
419 }
420
421 fd_count = ret;
422 for (i = 0; i < fd_count; i++) {
423 int fd = LTTNG_POLL_GETFD(&state.events, i);
424 uint32_t revents = LTTNG_POLL_GETEV(&state.events, i);
425
426 DBG("[rotation-thread] Handling fd (%i) activity (%u)",
427 fd, revents);
428
429 if (fd == handle->thread_quit_pipe) {
430 DBG("[rotation-thread] Quit pipe activity");
431 goto exit;
432 } else if (fd == handle->ust32_consumer ||
433 fd == handle->ust64_consumer ||
434 fd == handle->kernel_consumer) {
435 ret = handle_channel_rotation_pipe(fd,
436 revents, handle, &state);
437 if (ret) {
438 ERR("[rotation-thread] Handle channel rotation pipe");
439 goto error;
440 }
441 }
442 }
443 }
444 exit:
445 error:
446 DBG("[rotation-thread] Exit");
447 fini_thread_state(&state);
448 health_unregister(health_sessiond);
449 rcu_thread_offline();
450 rcu_unregister_thread();
451 end:
452 return NULL;
453 }
This page took 0.038025 seconds and 4 git commands to generate.