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