2 * Copyright (C) 2019 Jérémie Galarneau <jeremie.galarneau@efficios.com>
4 * SPDX-License-Identifier: GPL-2.0-only
8 #include "sessiond-trace-chunks.hpp"
10 #include <urcu/rculfhash.h>
12 #include <common/macros.hpp>
13 #include <common/hashtable/hashtable.hpp>
14 #include <common/hashtable/utils.hpp>
15 #include <common/trace-chunk-registry.hpp>
16 #include <common/defaults.hpp>
17 #include <common/error.hpp>
18 #include <common/string-utils/format.hpp>
23 * Lifetime of trace chunks within the relay daemon.
25 * Trace chunks are shared accross connections initiated from a given
26 * session daemon. When a session is created by a consumer daemon, the
27 * UUID of its associated session daemon is transmitted (in the case of
28 * 2.11+ consumer daemons).
30 * The sessiond_trace_chunk_registry_new_session() and
31 * sessiond_trace_chunk_registry_session_closed() methods create and
32 * manage the reference count of lttng_trace_chunk_registry objects
33 * associated to the various sessiond instances served by the relay daemon.
35 * When all sessions associated with a given sessiond instance are
36 * destroyed, its registry is destroyed.
38 * lttng_trace_chunk objects are uniquely identified by the
39 * (sessiond_uuid, sessiond_session_id, chunk_id) tuple. If a trace chunk
40 * matching that tuple already exists, a new reference to the trace chunk
41 * is acquired and it is returned to the caller. Otherwise, a new trace
42 * chunk is created. This is how trace chunks are de-duplicated across
43 * multiple consumer daemons managed by the same session daemon.
45 * Note that trace chunks are always added to their matching
46 * lttng_trace_chunk_registry. They are automatically removed from the
47 * trace chunk registry when their reference count reaches zero.
51 * It is assumed that the sessiond_trace_chunk_registry is created and
52 * destroyed by the same thread.
54 struct sessiond_trace_chunk_registry
{
55 /* Maps an lttng_uuid to an lttng_trace_chunk_registry. */
60 struct trace_chunk_registry_ht_key
{
61 lttng_uuid sessiond_uuid
;
64 struct trace_chunk_registry_ht_element
{
65 struct trace_chunk_registry_ht_key key
;
67 /* Node into the sessiond_trace_chunk_registry's hash table. */
68 struct cds_lfht_node ht_node
;
69 /* Used for defered call_rcu reclaim. */
70 struct rcu_head rcu_node
;
71 struct lttng_trace_chunk_registry
*trace_chunk_registry
;
72 struct sessiond_trace_chunk_registry
*sessiond_trace_chunk_registry
;
77 unsigned long trace_chunk_registry_ht_key_hash(
78 const struct trace_chunk_registry_ht_key
*key
)
80 const uint64_t uuid_h1
= *reinterpret_cast<const uint64_t *>(&key
->sessiond_uuid
[0]);
81 const uint64_t uuid_h2
= *reinterpret_cast<const uint64_t *>(&key
->sessiond_uuid
[1]);
83 return hash_key_u64(&uuid_h1
, lttng_ht_seed
) ^
84 hash_key_u64(&uuid_h2
, lttng_ht_seed
);
87 /* cds_lfht match function */
89 int trace_chunk_registry_ht_key_match(struct cds_lfht_node
*node
,
92 const struct trace_chunk_registry_ht_key
*key
=
93 (struct trace_chunk_registry_ht_key
*) _key
;
94 struct trace_chunk_registry_ht_element
*registry
;
96 registry
= container_of(node
, typeof(*registry
), ht_node
);
97 return key
->sessiond_uuid
== registry
->key
.sessiond_uuid
;
101 void trace_chunk_registry_ht_element_free(struct rcu_head
*node
)
103 struct trace_chunk_registry_ht_element
*element
=
104 container_of(node
, typeof(*element
), rcu_node
);
110 void trace_chunk_registry_ht_element_release(struct urcu_ref
*ref
)
112 struct trace_chunk_registry_ht_element
*element
=
113 container_of(ref
, typeof(*element
), ref
);
114 char uuid_str
[LTTNG_UUID_STR_LEN
];
116 lttng_uuid_to_str(element
->key
.sessiond_uuid
, uuid_str
);
118 DBG("Destroying trace chunk registry associated to sessiond {%s}",
120 if (element
->sessiond_trace_chunk_registry
) {
123 cds_lfht_del(element
->sessiond_trace_chunk_registry
->ht
,
126 element
->sessiond_trace_chunk_registry
= NULL
;
129 lttng_trace_chunk_registry_destroy(element
->trace_chunk_registry
);
130 /* Defered reclaim of the object */
131 call_rcu(&element
->rcu_node
, trace_chunk_registry_ht_element_free
);
135 bool trace_chunk_registry_ht_element_get(
136 struct trace_chunk_registry_ht_element
*element
)
138 return urcu_ref_get_unless_zero(&element
->ref
);
142 void trace_chunk_registry_ht_element_put(
143 struct trace_chunk_registry_ht_element
*element
)
149 urcu_ref_put(&element
->ref
, trace_chunk_registry_ht_element_release
);
152 /* Acquires a reference to the returned element on behalf of the caller. */
154 struct trace_chunk_registry_ht_element
*trace_chunk_registry_ht_element_find(
155 struct sessiond_trace_chunk_registry
*sessiond_registry
,
156 const struct trace_chunk_registry_ht_key
*key
)
158 struct trace_chunk_registry_ht_element
*element
= NULL
;
159 struct cds_lfht_node
*node
;
160 struct cds_lfht_iter iter
;
163 cds_lfht_lookup(sessiond_registry
->ht
,
164 trace_chunk_registry_ht_key_hash(key
),
165 trace_chunk_registry_ht_key_match
,
168 node
= cds_lfht_iter_get_node(&iter
);
170 element
= container_of(node
, typeof(*element
), ht_node
);
172 * Only consider the look-up as successful if a reference
175 if (!trace_chunk_registry_ht_element_get(element
)) {
184 int trace_chunk_registry_ht_element_create(
185 struct sessiond_trace_chunk_registry
*sessiond_registry
,
186 const struct trace_chunk_registry_ht_key
*key
)
189 struct trace_chunk_registry_ht_element
*new_element
;
190 struct lttng_trace_chunk_registry
*trace_chunk_registry
;
191 char uuid_str
[LTTNG_UUID_STR_LEN
];
193 lttng_uuid_to_str(key
->sessiond_uuid
, uuid_str
);
195 trace_chunk_registry
= lttng_trace_chunk_registry_create();
196 if (!trace_chunk_registry
) {
201 new_element
= zmalloc
<trace_chunk_registry_ht_element
>();
207 memcpy(&new_element
->key
, key
, sizeof(new_element
->key
));
208 urcu_ref_init(&new_element
->ref
);
209 cds_lfht_node_init(&new_element
->ht_node
);
210 new_element
->trace_chunk_registry
= trace_chunk_registry
;
211 trace_chunk_registry
= NULL
;
213 /* Attempt to publish the new element. */
216 struct cds_lfht_node
*published_node
;
217 struct trace_chunk_registry_ht_element
*published_element
;
219 published_node
= cds_lfht_add_unique(sessiond_registry
->ht
,
220 trace_chunk_registry_ht_key_hash(&new_element
->key
),
221 trace_chunk_registry_ht_key_match
,
223 &new_element
->ht_node
);
224 if (published_node
== &new_element
->ht_node
) {
225 /* New element published successfully. */
226 DBG("Created trace chunk registry for sessiond {%s}",
228 new_element
->sessiond_trace_chunk_registry
=
234 * An equivalent element was published during the creation of
235 * this element. Attempt to acquire a reference to the one that
236 * was already published and release the reference to the copy
237 * we created if successful.
239 published_element
= container_of(published_node
,
240 typeof(*published_element
), ht_node
);
241 if (trace_chunk_registry_ht_element_get(published_element
)) {
242 DBG("Acquired reference to trace chunk registry of sessiond {%s}",
244 trace_chunk_registry_ht_element_put(new_element
);
249 * A reference to the previously published element could not
250 * be acquired. Hence, retry to publish our copy of the
257 ERR("Failed to create trace chunk registry for session daemon {%s}",
260 lttng_trace_chunk_registry_destroy(trace_chunk_registry
);
264 struct sessiond_trace_chunk_registry
*sessiond_trace_chunk_registry_create(void)
266 struct sessiond_trace_chunk_registry
*sessiond_registry
=
267 zmalloc
<sessiond_trace_chunk_registry
>();
269 if (!sessiond_registry
) {
273 sessiond_registry
->ht
= cds_lfht_new(DEFAULT_HT_SIZE
,
274 1, 0, CDS_LFHT_AUTO_RESIZE
| CDS_LFHT_ACCOUNTING
, NULL
);
275 if (!sessiond_registry
->ht
) {
280 return sessiond_registry
;
282 sessiond_trace_chunk_registry_destroy(sessiond_registry
);
286 void sessiond_trace_chunk_registry_destroy(
287 struct sessiond_trace_chunk_registry
*sessiond_registry
)
289 int ret
= cds_lfht_destroy(sessiond_registry
->ht
, NULL
);
292 free(sessiond_registry
);
295 int sessiond_trace_chunk_registry_session_created(
296 struct sessiond_trace_chunk_registry
*sessiond_registry
,
297 const lttng_uuid
& sessiond_uuid
)
300 struct trace_chunk_registry_ht_key key
;
301 struct trace_chunk_registry_ht_element
*element
;
303 key
.sessiond_uuid
= sessiond_uuid
;
305 element
= trace_chunk_registry_ht_element_find(sessiond_registry
, &key
);
307 char uuid_str
[LTTNG_UUID_STR_LEN
];
309 lttng_uuid_to_str(sessiond_uuid
, uuid_str
);
310 DBG("Acquired reference to trace chunk registry of sessiond {%s}",
314 ret
= trace_chunk_registry_ht_element_create(
315 sessiond_registry
, &key
);
321 int sessiond_trace_chunk_registry_session_destroyed(
322 struct sessiond_trace_chunk_registry
*sessiond_registry
,
323 const lttng_uuid
& sessiond_uuid
)
326 struct trace_chunk_registry_ht_key key
;
327 struct trace_chunk_registry_ht_element
*element
;
328 char uuid_str
[LTTNG_UUID_STR_LEN
];
330 lttng_uuid_to_str(sessiond_uuid
, uuid_str
);
331 key
.sessiond_uuid
= sessiond_uuid
;
333 element
= trace_chunk_registry_ht_element_find(sessiond_registry
, &key
);
335 DBG("Releasing reference to trace chunk registry of sessiond {%s}",
338 * Release the reference held by the session and the reference
339 * acquired through the "find" operation.
341 trace_chunk_registry_ht_element_put(element
);
342 trace_chunk_registry_ht_element_put(element
);
344 ERR("Failed to find trace chunk registry of sessiond {%s}",
351 struct lttng_trace_chunk
*sessiond_trace_chunk_registry_publish_chunk(
352 struct sessiond_trace_chunk_registry
*sessiond_registry
,
353 const lttng_uuid
& sessiond_uuid
, uint64_t session_id
,
354 struct lttng_trace_chunk
*new_chunk
)
356 enum lttng_trace_chunk_status status
;
358 bool is_anonymous_chunk
;
359 struct trace_chunk_registry_ht_key key
;
360 struct trace_chunk_registry_ht_element
*element
= NULL
;
361 char uuid_str
[LTTNG_UUID_STR_LEN
];
362 char chunk_id_str
[MAX_INT_DEC_LEN(typeof(chunk_id
))] = "-1";
363 struct lttng_trace_chunk
*published_chunk
= NULL
;
364 bool trace_chunk_already_published
;
366 lttng_uuid_to_str(sessiond_uuid
, uuid_str
);
367 key
.sessiond_uuid
= sessiond_uuid
;
369 status
= lttng_trace_chunk_get_id(new_chunk
, &chunk_id
);
370 if (status
== LTTNG_TRACE_CHUNK_STATUS_OK
) {
373 ret
= snprintf(chunk_id_str
, sizeof(chunk_id_str
), "%" PRIu64
,
376 lttng_strncpy(chunk_id_str
, "-1", sizeof(chunk_id_str
));
377 WARN("Failed to format trace chunk id");
379 is_anonymous_chunk
= false;
380 } else if (status
== LTTNG_TRACE_CHUNK_STATUS_NONE
) {
381 is_anonymous_chunk
= true;
383 ERR("Failed to get trace chunk id");
387 DBG("Attempting to publish trace chunk: sessiond {%s}, session_id = "
388 "%" PRIu64
", chunk_id = %s",
389 uuid_str
, session_id
,
390 is_anonymous_chunk
? "anonymous" : chunk_id_str
);
392 element
= trace_chunk_registry_ht_element_find(sessiond_registry
, &key
);
394 ERR("Failed to find registry of sessiond {%s}", uuid_str
);
398 published_chunk
= lttng_trace_chunk_registry_publish_chunk(
399 element
->trace_chunk_registry
, session_id
, new_chunk
,
400 &trace_chunk_already_published
);
402 * When the trace chunk is first published, two references to the
403 * published chunks exist. One is taken by the registry while the other
404 * is being returned to the caller. In the use case of the relay daemon,
405 * the reference held by the registry itself is undesirable.
407 * We want the trace chunk to be removed from the registry as soon
408 * as it is not being used by the relay daemon (through a session
409 * or a stream). This differs from the behaviour of the consumer
410 * daemon which relies on an explicit command from the session
411 * daemon to release the registry's reference.
413 * In cases where the trace chunk had already been published,
414 * the reference belonging to the sessiond trace chunk
415 * registry instance has already been 'put'. We simply return
416 * the published trace chunk with a reference taken on behalf of the
419 if (!trace_chunk_already_published
) {
420 lttng_trace_chunk_put(published_chunk
);
423 trace_chunk_registry_ht_element_put(element
);
424 return published_chunk
;
427 struct lttng_trace_chunk
*
428 sessiond_trace_chunk_registry_get_anonymous_chunk(
429 struct sessiond_trace_chunk_registry
*sessiond_registry
,
430 const lttng_uuid
& sessiond_uuid
,
433 struct lttng_trace_chunk
*chunk
= NULL
;
434 struct trace_chunk_registry_ht_element
*element
;
435 struct trace_chunk_registry_ht_key key
;
436 char uuid_str
[LTTNG_UUID_STR_LEN
];
438 lttng_uuid_to_str(sessiond_uuid
, uuid_str
);
440 key
.sessiond_uuid
= sessiond_uuid
;
441 element
= trace_chunk_registry_ht_element_find(sessiond_registry
, &key
);
443 ERR("Failed to find trace chunk registry of sessiond {%s}",
448 chunk
= lttng_trace_chunk_registry_find_anonymous_chunk(
449 element
->trace_chunk_registry
,
451 trace_chunk_registry_ht_element_put(element
);
456 struct lttng_trace_chunk
*
457 sessiond_trace_chunk_registry_get_chunk(
458 struct sessiond_trace_chunk_registry
*sessiond_registry
,
459 const lttng_uuid
& sessiond_uuid
,
460 uint64_t session_id
, uint64_t chunk_id
)
462 struct lttng_trace_chunk
*chunk
= NULL
;
463 struct trace_chunk_registry_ht_element
*element
;
464 struct trace_chunk_registry_ht_key key
;
465 char uuid_str
[LTTNG_UUID_STR_LEN
];
467 lttng_uuid_to_str(sessiond_uuid
, uuid_str
);
469 key
.sessiond_uuid
= sessiond_uuid
;
470 element
= trace_chunk_registry_ht_element_find(sessiond_registry
, &key
);
472 ERR("Failed to find trace chunk registry of sessiond {%s}",
477 chunk
= lttng_trace_chunk_registry_find_chunk(
478 element
->trace_chunk_registry
,
479 session_id
, chunk_id
);
480 trace_chunk_registry_ht_element_put(element
);
485 int sessiond_trace_chunk_registry_chunk_exists(
486 struct sessiond_trace_chunk_registry
*sessiond_registry
,
487 const lttng_uuid
& sessiond_uuid
,
488 uint64_t session_id
, uint64_t chunk_id
, bool *chunk_exists
)
491 struct trace_chunk_registry_ht_element
*element
;
492 struct trace_chunk_registry_ht_key key
;
494 key
.sessiond_uuid
= sessiond_uuid
;
495 element
= trace_chunk_registry_ht_element_find(sessiond_registry
, &key
);
497 char uuid_str
[LTTNG_UUID_STR_LEN
];
499 lttng_uuid_to_str(sessiond_uuid
, uuid_str
);
501 * While this certainly means that the chunk does not exist,
502 * it is unexpected for a chunk existence query to target a
503 * session daemon that does not have an active
504 * connection/registry. This would indicate a protocol
505 * (or internal) error.
507 ERR("Failed to find trace chunk registry of sessiond {%s}",
513 ret
= lttng_trace_chunk_registry_chunk_exists(
514 element
->trace_chunk_registry
,
515 session_id
, chunk_id
, chunk_exists
);
516 trace_chunk_registry_ht_element_put(element
);