f2dfa23a11826d935ec1e2f189d9bdeef4e6b958
[lttng-tools.git] / src / bin / lttng-relayd / sessiond-trace-chunks.c
1 /*
2 * Copyright (C) 2019 - Jérémie Galarneau <jeremie.galarneau@efficios.com>
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License, version 2 only,
6 * as 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
14 * with this program; if not, write to the Free Software Foundation, Inc.,
15 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
16 */
17
18 #include "sessiond-trace-chunks.h"
19 #include <urcu.h>
20 #include <urcu/rculfhash.h>
21 #include <urcu/ref.h>
22 #include <common/macros.h>
23 #include <common/hashtable/hashtable.h>
24 #include <common/hashtable/utils.h>
25 #include <common/trace-chunk-registry.h>
26 #include <common/defaults.h>
27 #include <common/error.h>
28 #include <common/string-utils/format.h>
29 #include <stdio.h>
30 #include <inttypes.h>
31
32 /*
33 * Lifetime of trace chunks within the relay daemon.
34 *
35 * Trace chunks are shared accross connections initiated from a given
36 * session daemon. When a session is created by a consumer daemon, the
37 * UUID of its associated session daemon is transmitted (in the case of
38 * 2.11+ consumer daemons).
39 *
40 * The sessiond_trace_chunk_registry_new_session() and
41 * sessiond_trace_chunk_registry_session_closed() methods create and
42 * manage the reference count of lttng_trace_chunk_registry objects
43 * associated to the various sessiond instances served by the relay daemon.
44 *
45 * When all sessions associated with a given sessiond instance are
46 * destroyed, its registry is destroyed.
47 *
48 * lttng_trace_chunk objects are uniquely identified by the
49 * (sessiond_uuid, sessiond_session_id, chunk_id) tuple. If a trace chunk
50 * matching that tuple already exists, a new reference to the trace chunk
51 * is acquired and it is returned to the caller. Otherwise, a new trace
52 * chunk is created. This is how trace chunks are de-duplicated across
53 * multiple consumer daemons managed by the same session daemon.
54 *
55 * Note that trace chunks are always added to their matching
56 * lttng_trace_chunk_registry. They are automatically removed from the
57 * trace chunk registry when their reference count reaches zero.
58 */
59
60 /*
61 * It is assumed that the sessiond_trace_chunk_registry is created and
62 * destroyed by the same thread.
63 */
64 struct sessiond_trace_chunk_registry {
65 /* Maps an lttng_uuid to an lttng_trace_chunk_registry. */
66 struct cds_lfht *ht;
67 };
68
69 struct trace_chunk_registry_ht_key {
70 lttng_uuid sessiond_uuid;
71 };
72
73 struct trace_chunk_registry_ht_element {
74 struct trace_chunk_registry_ht_key key;
75 struct urcu_ref ref;
76 /* Node into the sessiond_trace_chunk_registry's hash table. */
77 struct cds_lfht_node ht_node;
78 /* Used for defered call_rcu reclaim. */
79 struct rcu_head rcu_node;
80 struct lttng_trace_chunk_registry *trace_chunk_registry;
81 struct sessiond_trace_chunk_registry *sessiond_trace_chunk_registry;
82 };
83
84 static
85 unsigned long trace_chunk_registry_ht_key_hash(
86 const struct trace_chunk_registry_ht_key *key)
87 {
88 uint64_t uuid_h1 = ((uint64_t *) key->sessiond_uuid)[0];
89 uint64_t uuid_h2 = ((uint64_t *) key->sessiond_uuid)[1];
90
91 return hash_key_u64(&uuid_h1, lttng_ht_seed) ^
92 hash_key_u64(&uuid_h2, lttng_ht_seed);
93 }
94
95 /* cds_lfht match function */
96 static
97 int trace_chunk_registry_ht_key_match(struct cds_lfht_node *node,
98 const void *_key)
99 {
100 const struct trace_chunk_registry_ht_key *key =
101 (struct trace_chunk_registry_ht_key *) _key;
102 struct trace_chunk_registry_ht_element *registry;
103
104 registry = container_of(node, typeof(*registry), ht_node);
105 return lttng_uuid_is_equal(key->sessiond_uuid,
106 registry->key.sessiond_uuid);
107 }
108
109 static
110 void trace_chunk_registry_ht_element_free(struct rcu_head *node)
111 {
112 struct trace_chunk_registry_ht_element *element =
113 container_of(node, typeof(*element), rcu_node);
114
115 free(element);
116 }
117
118 static
119 void trace_chunk_registry_ht_element_release(struct urcu_ref *ref)
120 {
121 struct trace_chunk_registry_ht_element *element =
122 container_of(ref, typeof(*element), ref);
123 char uuid_str[UUID_STR_LEN];
124
125 lttng_uuid_to_str(element->key.sessiond_uuid, uuid_str);
126
127 DBG("Destroying trace chunk registry associated to sessiond {%s}",
128 uuid_str);
129 if (element->sessiond_trace_chunk_registry) {
130 /* Unpublish. */
131 rcu_read_lock();
132 cds_lfht_del(element->sessiond_trace_chunk_registry->ht,
133 &element->ht_node);
134 rcu_read_unlock();
135 element->sessiond_trace_chunk_registry = NULL;
136 }
137
138 lttng_trace_chunk_registry_destroy(element->trace_chunk_registry);
139 /* Defered reclaim of the object */
140 call_rcu(&element->rcu_node, trace_chunk_registry_ht_element_free);
141 }
142
143 static
144 bool trace_chunk_registry_ht_element_get(
145 struct trace_chunk_registry_ht_element *element)
146 {
147 return urcu_ref_get_unless_zero(&element->ref);
148 }
149
150 static
151 void trace_chunk_registry_ht_element_put(
152 struct trace_chunk_registry_ht_element *element)
153 {
154 if (!element) {
155 return;
156 }
157
158 urcu_ref_put(&element->ref, trace_chunk_registry_ht_element_release);
159 }
160
161 /* Acquires a reference to the returned element on behalf of the caller. */
162 static
163 struct trace_chunk_registry_ht_element *trace_chunk_registry_ht_element_find(
164 struct sessiond_trace_chunk_registry *sessiond_registry,
165 const struct trace_chunk_registry_ht_key *key)
166 {
167 struct trace_chunk_registry_ht_element *element = NULL;
168 struct cds_lfht_node *node;
169 struct cds_lfht_iter iter;
170
171 rcu_read_lock();
172 cds_lfht_lookup(sessiond_registry->ht,
173 trace_chunk_registry_ht_key_hash(key),
174 trace_chunk_registry_ht_key_match,
175 key,
176 &iter);
177 node = cds_lfht_iter_get_node(&iter);
178 if (node) {
179 element = container_of(node, typeof(*element), ht_node);
180 /*
181 * Only consider the look-up as successful if a reference
182 * could be acquired.
183 */
184 if (!trace_chunk_registry_ht_element_get(element)) {
185 element = NULL;
186 }
187 }
188 rcu_read_unlock();
189 return element;
190 }
191
192 static
193 int trace_chunk_registry_ht_element_create(
194 struct sessiond_trace_chunk_registry *sessiond_registry,
195 const struct trace_chunk_registry_ht_key *key)
196 {
197 int ret = 0;
198 struct trace_chunk_registry_ht_element *new_element;
199 struct lttng_trace_chunk_registry *trace_chunk_registry;
200 char uuid_str[UUID_STR_LEN];
201
202 lttng_uuid_to_str(key->sessiond_uuid, uuid_str);
203
204 trace_chunk_registry = lttng_trace_chunk_registry_create();
205 if (!trace_chunk_registry) {
206 ret = -1;
207 goto end;
208 }
209
210 new_element = zmalloc(sizeof(*new_element));
211 if (!new_element) {
212 ret = -1;
213 goto end;
214 }
215
216 memcpy(&new_element->key, key, sizeof(new_element->key));
217 urcu_ref_init(&new_element->ref);
218 cds_lfht_node_init(&new_element->ht_node);
219 new_element->trace_chunk_registry = trace_chunk_registry;
220 trace_chunk_registry = NULL;
221
222 /* Attempt to publish the new element. */
223 rcu_read_lock();
224 while (1) {
225 struct cds_lfht_node *published_node;
226 struct trace_chunk_registry_ht_element *published_element;
227
228 published_node = cds_lfht_add_unique(sessiond_registry->ht,
229 trace_chunk_registry_ht_key_hash(&new_element->key),
230 trace_chunk_registry_ht_key_match,
231 &new_element->key,
232 &new_element->ht_node);
233 if (published_node == &new_element->ht_node) {
234 /* New element published successfully. */
235 DBG("Created trace chunk registry for sessiond {%s}",
236 uuid_str);
237 new_element->sessiond_trace_chunk_registry =
238 sessiond_registry;
239 break;
240 }
241
242 /*
243 * An equivalent element was published during the creation of
244 * this element. Attempt to acquire a reference to the one that
245 * was already published and release the reference to the copy
246 * we created if successful.
247 */
248 published_element = container_of(published_node,
249 typeof(*published_element), ht_node);
250 if (trace_chunk_registry_ht_element_get(published_element)) {
251 DBG("Acquired reference to trace chunk registry of sessiond {%s}",
252 uuid_str);
253 trace_chunk_registry_ht_element_put(new_element);
254 new_element = NULL;
255 break;
256 }
257 /*
258 * A reference to the previously published element could not
259 * be acquired. Hence, retry to publish our copy of the
260 * element.
261 */
262 }
263 rcu_read_unlock();
264 end:
265 if (ret < 0) {
266 ERR("Failed to create trace chunk registry for session daemon {%s}",
267 uuid_str);
268 }
269 lttng_trace_chunk_registry_destroy(trace_chunk_registry);
270 return ret;
271 }
272
273 struct sessiond_trace_chunk_registry *sessiond_trace_chunk_registry_create(void)
274 {
275 struct sessiond_trace_chunk_registry *sessiond_registry =
276 zmalloc(sizeof(*sessiond_registry));
277
278 if (!sessiond_registry) {
279 goto end;
280 }
281
282 sessiond_registry->ht = cds_lfht_new(DEFAULT_HT_SIZE,
283 1, 0, CDS_LFHT_AUTO_RESIZE | CDS_LFHT_ACCOUNTING, NULL);
284 if (!sessiond_registry->ht) {
285 goto error;
286 }
287
288 end:
289 return sessiond_registry;
290 error:
291 sessiond_trace_chunk_registry_destroy(sessiond_registry);
292 return NULL;
293 }
294
295 void sessiond_trace_chunk_registry_destroy(
296 struct sessiond_trace_chunk_registry *sessiond_registry)
297 {
298 int ret = cds_lfht_destroy(sessiond_registry->ht, NULL);
299
300 assert(!ret);
301 free(sessiond_registry);
302 }
303
304 int sessiond_trace_chunk_registry_session_created(
305 struct sessiond_trace_chunk_registry *sessiond_registry,
306 const lttng_uuid sessiond_uuid)
307 {
308 int ret = 0;
309 struct trace_chunk_registry_ht_key key;
310 struct trace_chunk_registry_ht_element *element;
311
312 lttng_uuid_copy(key.sessiond_uuid, sessiond_uuid);
313
314 element = trace_chunk_registry_ht_element_find(sessiond_registry, &key);
315 if (element) {
316 char uuid_str[UUID_STR_LEN];
317
318 lttng_uuid_to_str(sessiond_uuid, uuid_str);
319 DBG("Acquired reference to trace chunk registry of sessiond {%s}",
320 uuid_str);
321 goto end;
322 } else {
323 ret = trace_chunk_registry_ht_element_create(
324 sessiond_registry, &key);
325 }
326 end:
327 return ret;
328 }
329
330 int sessiond_trace_chunk_registry_session_destroyed(
331 struct sessiond_trace_chunk_registry *sessiond_registry,
332 const lttng_uuid sessiond_uuid)
333 {
334 int ret = 0;
335 struct trace_chunk_registry_ht_key key;
336 struct trace_chunk_registry_ht_element *element;
337 char uuid_str[UUID_STR_LEN];
338
339 lttng_uuid_to_str(sessiond_uuid, uuid_str);
340 lttng_uuid_copy(key.sessiond_uuid, sessiond_uuid);
341
342 element = trace_chunk_registry_ht_element_find(sessiond_registry, &key);
343 if (element) {
344 DBG("Releasing reference to trace chunk registry of sessiond {%s}",
345 uuid_str);
346 /*
347 * Release the reference held by the session and the reference
348 * acquired through the "find" operation.
349 */
350 trace_chunk_registry_ht_element_put(element);
351 trace_chunk_registry_ht_element_put(element);
352 } else {
353 ERR("Failed to find trace chunk registry of sessiond {%s}",
354 uuid_str);
355 ret = -1;
356 }
357 return ret;
358 }
359
360 struct lttng_trace_chunk *sessiond_trace_chunk_registry_publish_chunk(
361 struct sessiond_trace_chunk_registry *sessiond_registry,
362 const lttng_uuid sessiond_uuid, uint64_t session_id,
363 struct lttng_trace_chunk *new_chunk)
364 {
365 enum lttng_trace_chunk_status status;
366 uint64_t chunk_id;
367 bool is_anonymous_chunk;
368 struct trace_chunk_registry_ht_key key;
369 struct trace_chunk_registry_ht_element *element = NULL;
370 char uuid_str[UUID_STR_LEN];
371 char chunk_id_str[MAX_INT_DEC_LEN(typeof(chunk_id))] = "-1";
372 struct lttng_trace_chunk *published_chunk = NULL;
373
374 lttng_uuid_to_str(sessiond_uuid, uuid_str);
375 lttng_uuid_copy(key.sessiond_uuid, sessiond_uuid);
376
377 status = lttng_trace_chunk_get_id(new_chunk, &chunk_id);
378 if (status == LTTNG_TRACE_CHUNK_STATUS_OK) {
379 int ret;
380
381 ret = snprintf(chunk_id_str, sizeof(chunk_id_str), "%" PRIu64,
382 chunk_id);
383 if (ret < 0) {
384 lttng_strncpy(chunk_id_str, "-1", sizeof(chunk_id_str));
385 WARN("Failed to format trace chunk id");
386 }
387 is_anonymous_chunk = false;
388 } else if (status == LTTNG_TRACE_CHUNK_STATUS_NONE) {
389 is_anonymous_chunk = true;
390 } else {
391 ERR("Failed to get trace chunk id");
392 goto end;
393 }
394
395 DBG("Attempting to publish trace chunk: sessiond {%s}, session_id = "
396 "%" PRIu64 ", chunk_id = %s",
397 uuid_str, session_id,
398 is_anonymous_chunk ? "anonymous" : chunk_id_str);
399
400 element = trace_chunk_registry_ht_element_find(sessiond_registry, &key);
401 if (!element) {
402 ERR("Failed to find registry of sessiond {%s}", uuid_str);
403 goto end;
404 }
405
406 published_chunk = lttng_trace_chunk_registry_publish_chunk(
407 element->trace_chunk_registry, session_id, new_chunk);
408 /*
409 * At this point, two references to the published chunks exist. One
410 * is taken by the registry while the other is being returned to the
411 * caller. In the use case of the relay daemon, the reference held
412 * by the registry itself is undesirable.
413 *
414 * We want the trace chunk to be removed from the registry as soon
415 * as it is not being used by the relay daemon (through a session
416 * or a stream). This differs from the behaviour of the consumer
417 * daemon which relies on an explicit command from the session
418 * daemon to release the registry's reference.
419 */
420 lttng_trace_chunk_put(published_chunk);
421 end:
422 trace_chunk_registry_ht_element_put(element);
423 return published_chunk;
424 }
425
426 struct lttng_trace_chunk *
427 sessiond_trace_chunk_registry_get_anonymous_chunk(
428 struct sessiond_trace_chunk_registry *sessiond_registry,
429 const lttng_uuid sessiond_uuid,
430 uint64_t session_id)
431 {
432 struct lttng_trace_chunk *chunk = NULL;
433 struct trace_chunk_registry_ht_element *element;
434 struct trace_chunk_registry_ht_key key;
435 char uuid_str[UUID_STR_LEN];
436
437 lttng_uuid_to_str(sessiond_uuid, uuid_str);
438
439 lttng_uuid_copy(key.sessiond_uuid, sessiond_uuid);
440 element = trace_chunk_registry_ht_element_find(sessiond_registry, &key);
441 if (!element) {
442 ERR("Failed to find trace chunk registry of sessiond {%s}",
443 uuid_str);
444 goto end;
445 }
446
447 chunk = lttng_trace_chunk_registry_find_anonymous_chunk(
448 element->trace_chunk_registry,
449 session_id);
450 trace_chunk_registry_ht_element_put(element);
451 end:
452 return chunk;
453 }
454
455 struct lttng_trace_chunk *
456 sessiond_trace_chunk_registry_get_chunk(
457 struct sessiond_trace_chunk_registry *sessiond_registry,
458 const lttng_uuid sessiond_uuid,
459 uint64_t session_id, uint64_t chunk_id)
460 {
461 struct lttng_trace_chunk *chunk = NULL;
462 struct trace_chunk_registry_ht_element *element;
463 struct trace_chunk_registry_ht_key key;
464 char uuid_str[UUID_STR_LEN];
465
466 lttng_uuid_to_str(sessiond_uuid, uuid_str);
467
468 lttng_uuid_copy(key.sessiond_uuid, sessiond_uuid);
469 element = trace_chunk_registry_ht_element_find(sessiond_registry, &key);
470 if (!element) {
471 ERR("Failed to find trace chunk registry of sessiond {%s}",
472 uuid_str);
473 goto end;
474 }
475
476 chunk = lttng_trace_chunk_registry_find_chunk(
477 element->trace_chunk_registry,
478 session_id, chunk_id);
479 trace_chunk_registry_ht_element_put(element);
480 end:
481 return chunk;
482 }
483
484 int sessiond_trace_chunk_registry_chunk_exists(
485 struct sessiond_trace_chunk_registry *sessiond_registry,
486 const lttng_uuid sessiond_uuid,
487 uint64_t session_id, uint64_t chunk_id, bool *chunk_exists)
488 {
489 int ret;
490 struct trace_chunk_registry_ht_element *element;
491 struct trace_chunk_registry_ht_key key;
492
493 lttng_uuid_copy(key.sessiond_uuid, sessiond_uuid);
494 element = trace_chunk_registry_ht_element_find(sessiond_registry, &key);
495 if (!element) {
496 char uuid_str[UUID_STR_LEN];
497
498 lttng_uuid_to_str(sessiond_uuid, uuid_str);
499 /*
500 * While this certainly means that the chunk does not exist,
501 * it is unexpected for a chunk existence query to target a
502 * session daemon that does not have an active
503 * connection/registry. This would indicate a protocol
504 * (or internal) error.
505 */
506 ERR("Failed to find trace chunk registry of sessiond {%s}",
507 uuid_str);
508 ret = -1;
509 goto end;
510 }
511
512 ret = lttng_trace_chunk_registry_chunk_exists(
513 element->trace_chunk_registry,
514 session_id, chunk_id, chunk_exists);
515 trace_chunk_registry_ht_element_put(element);
516 end:
517 return ret;
518 }
This page took 0.040541 seconds and 4 git commands to generate.