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 uint64_t uuid_h1
= ((uint64_t *) key
->sessiond_uuid
)[0];
81 uint64_t uuid_h2
= ((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 lttng_uuid_is_equal(key
->sessiond_uuid
,
98 registry
->key
.sessiond_uuid
);
102 void trace_chunk_registry_ht_element_free(struct rcu_head
*node
)
104 struct trace_chunk_registry_ht_element
*element
=
105 container_of(node
, typeof(*element
), rcu_node
);
111 void trace_chunk_registry_ht_element_release(struct urcu_ref
*ref
)
113 struct trace_chunk_registry_ht_element
*element
=
114 container_of(ref
, typeof(*element
), ref
);
115 char uuid_str
[LTTNG_UUID_STR_LEN
];
117 lttng_uuid_to_str(element
->key
.sessiond_uuid
, uuid_str
);
119 DBG("Destroying trace chunk registry associated to sessiond {%s}",
121 if (element
->sessiond_trace_chunk_registry
) {
124 cds_lfht_del(element
->sessiond_trace_chunk_registry
->ht
,
127 element
->sessiond_trace_chunk_registry
= NULL
;
130 lttng_trace_chunk_registry_destroy(element
->trace_chunk_registry
);
131 /* Defered reclaim of the object */
132 call_rcu(&element
->rcu_node
, trace_chunk_registry_ht_element_free
);
136 bool trace_chunk_registry_ht_element_get(
137 struct trace_chunk_registry_ht_element
*element
)
139 return urcu_ref_get_unless_zero(&element
->ref
);
143 void trace_chunk_registry_ht_element_put(
144 struct trace_chunk_registry_ht_element
*element
)
150 urcu_ref_put(&element
->ref
, trace_chunk_registry_ht_element_release
);
153 /* Acquires a reference to the returned element on behalf of the caller. */
155 struct trace_chunk_registry_ht_element
*trace_chunk_registry_ht_element_find(
156 struct sessiond_trace_chunk_registry
*sessiond_registry
,
157 const struct trace_chunk_registry_ht_key
*key
)
159 struct trace_chunk_registry_ht_element
*element
= NULL
;
160 struct cds_lfht_node
*node
;
161 struct cds_lfht_iter iter
;
164 cds_lfht_lookup(sessiond_registry
->ht
,
165 trace_chunk_registry_ht_key_hash(key
),
166 trace_chunk_registry_ht_key_match
,
169 node
= cds_lfht_iter_get_node(&iter
);
171 element
= container_of(node
, typeof(*element
), ht_node
);
173 * Only consider the look-up as successful if a reference
176 if (!trace_chunk_registry_ht_element_get(element
)) {
185 int trace_chunk_registry_ht_element_create(
186 struct sessiond_trace_chunk_registry
*sessiond_registry
,
187 const struct trace_chunk_registry_ht_key
*key
)
190 struct trace_chunk_registry_ht_element
*new_element
;
191 struct lttng_trace_chunk_registry
*trace_chunk_registry
;
192 char uuid_str
[LTTNG_UUID_STR_LEN
];
194 lttng_uuid_to_str(key
->sessiond_uuid
, uuid_str
);
196 trace_chunk_registry
= lttng_trace_chunk_registry_create();
197 if (!trace_chunk_registry
) {
202 new_element
= zmalloc
<trace_chunk_registry_ht_element
>();
208 memcpy(&new_element
->key
, key
, sizeof(new_element
->key
));
209 urcu_ref_init(&new_element
->ref
);
210 cds_lfht_node_init(&new_element
->ht_node
);
211 new_element
->trace_chunk_registry
= trace_chunk_registry
;
212 trace_chunk_registry
= NULL
;
214 /* Attempt to publish the new element. */
217 struct cds_lfht_node
*published_node
;
218 struct trace_chunk_registry_ht_element
*published_element
;
220 published_node
= cds_lfht_add_unique(sessiond_registry
->ht
,
221 trace_chunk_registry_ht_key_hash(&new_element
->key
),
222 trace_chunk_registry_ht_key_match
,
224 &new_element
->ht_node
);
225 if (published_node
== &new_element
->ht_node
) {
226 /* New element published successfully. */
227 DBG("Created trace chunk registry for sessiond {%s}",
229 new_element
->sessiond_trace_chunk_registry
=
235 * An equivalent element was published during the creation of
236 * this element. Attempt to acquire a reference to the one that
237 * was already published and release the reference to the copy
238 * we created if successful.
240 published_element
= container_of(published_node
,
241 typeof(*published_element
), ht_node
);
242 if (trace_chunk_registry_ht_element_get(published_element
)) {
243 DBG("Acquired reference to trace chunk registry of sessiond {%s}",
245 trace_chunk_registry_ht_element_put(new_element
);
250 * A reference to the previously published element could not
251 * be acquired. Hence, retry to publish our copy of the
258 ERR("Failed to create trace chunk registry for session daemon {%s}",
261 lttng_trace_chunk_registry_destroy(trace_chunk_registry
);
265 struct sessiond_trace_chunk_registry
*sessiond_trace_chunk_registry_create(void)
267 struct sessiond_trace_chunk_registry
*sessiond_registry
=
268 zmalloc
<sessiond_trace_chunk_registry
>();
270 if (!sessiond_registry
) {
274 sessiond_registry
->ht
= cds_lfht_new(DEFAULT_HT_SIZE
,
275 1, 0, CDS_LFHT_AUTO_RESIZE
| CDS_LFHT_ACCOUNTING
, NULL
);
276 if (!sessiond_registry
->ht
) {
281 return sessiond_registry
;
283 sessiond_trace_chunk_registry_destroy(sessiond_registry
);
287 void sessiond_trace_chunk_registry_destroy(
288 struct sessiond_trace_chunk_registry
*sessiond_registry
)
290 int ret
= cds_lfht_destroy(sessiond_registry
->ht
, NULL
);
293 free(sessiond_registry
);
296 int sessiond_trace_chunk_registry_session_created(
297 struct sessiond_trace_chunk_registry
*sessiond_registry
,
298 const lttng_uuid sessiond_uuid
)
301 struct trace_chunk_registry_ht_key key
;
302 struct trace_chunk_registry_ht_element
*element
;
304 lttng_uuid_copy(key
.sessiond_uuid
, sessiond_uuid
);
306 element
= trace_chunk_registry_ht_element_find(sessiond_registry
, &key
);
308 char uuid_str
[LTTNG_UUID_STR_LEN
];
310 lttng_uuid_to_str(sessiond_uuid
, uuid_str
);
311 DBG("Acquired reference to trace chunk registry of sessiond {%s}",
315 ret
= trace_chunk_registry_ht_element_create(
316 sessiond_registry
, &key
);
322 int sessiond_trace_chunk_registry_session_destroyed(
323 struct sessiond_trace_chunk_registry
*sessiond_registry
,
324 const lttng_uuid sessiond_uuid
)
327 struct trace_chunk_registry_ht_key key
;
328 struct trace_chunk_registry_ht_element
*element
;
329 char uuid_str
[LTTNG_UUID_STR_LEN
];
331 lttng_uuid_to_str(sessiond_uuid
, uuid_str
);
332 lttng_uuid_copy(key
.sessiond_uuid
, sessiond_uuid
);
334 element
= trace_chunk_registry_ht_element_find(sessiond_registry
, &key
);
336 DBG("Releasing reference to trace chunk registry of sessiond {%s}",
339 * Release the reference held by the session and the reference
340 * acquired through the "find" operation.
342 trace_chunk_registry_ht_element_put(element
);
343 trace_chunk_registry_ht_element_put(element
);
345 ERR("Failed to find trace chunk registry of sessiond {%s}",
352 struct lttng_trace_chunk
*sessiond_trace_chunk_registry_publish_chunk(
353 struct sessiond_trace_chunk_registry
*sessiond_registry
,
354 const lttng_uuid sessiond_uuid
, uint64_t session_id
,
355 struct lttng_trace_chunk
*new_chunk
)
357 enum lttng_trace_chunk_status status
;
359 bool is_anonymous_chunk
;
360 struct trace_chunk_registry_ht_key key
;
361 struct trace_chunk_registry_ht_element
*element
= NULL
;
362 char uuid_str
[LTTNG_UUID_STR_LEN
];
363 char chunk_id_str
[MAX_INT_DEC_LEN(typeof(chunk_id
))] = "-1";
364 struct lttng_trace_chunk
*published_chunk
= NULL
;
365 bool trace_chunk_already_published
;
367 lttng_uuid_to_str(sessiond_uuid
, uuid_str
);
368 lttng_uuid_copy(key
.sessiond_uuid
, sessiond_uuid
);
370 status
= lttng_trace_chunk_get_id(new_chunk
, &chunk_id
);
371 if (status
== LTTNG_TRACE_CHUNK_STATUS_OK
) {
374 ret
= snprintf(chunk_id_str
, sizeof(chunk_id_str
), "%" PRIu64
,
377 lttng_strncpy(chunk_id_str
, "-1", sizeof(chunk_id_str
));
378 WARN("Failed to format trace chunk id");
380 is_anonymous_chunk
= false;
381 } else if (status
== LTTNG_TRACE_CHUNK_STATUS_NONE
) {
382 is_anonymous_chunk
= true;
384 ERR("Failed to get trace chunk id");
388 DBG("Attempting to publish trace chunk: sessiond {%s}, session_id = "
389 "%" PRIu64
", chunk_id = %s",
390 uuid_str
, session_id
,
391 is_anonymous_chunk
? "anonymous" : chunk_id_str
);
393 element
= trace_chunk_registry_ht_element_find(sessiond_registry
, &key
);
395 ERR("Failed to find registry of sessiond {%s}", uuid_str
);
399 published_chunk
= lttng_trace_chunk_registry_publish_chunk(
400 element
->trace_chunk_registry
, session_id
, new_chunk
,
401 &trace_chunk_already_published
);
403 * When the trace chunk is first published, two references to the
404 * published chunks exist. One is taken by the registry while the other
405 * is being returned to the caller. In the use case of the relay daemon,
406 * the reference held by the registry itself is undesirable.
408 * We want the trace chunk to be removed from the registry as soon
409 * as it is not being used by the relay daemon (through a session
410 * or a stream). This differs from the behaviour of the consumer
411 * daemon which relies on an explicit command from the session
412 * daemon to release the registry's reference.
414 * In cases where the trace chunk had already been published,
415 * the reference belonging to the sessiond trace chunk
416 * registry instance has already been 'put'. We simply return
417 * the published trace chunk with a reference taken on behalf of the
420 if (!trace_chunk_already_published
) {
421 lttng_trace_chunk_put(published_chunk
);
424 trace_chunk_registry_ht_element_put(element
);
425 return published_chunk
;
428 struct lttng_trace_chunk
*
429 sessiond_trace_chunk_registry_get_anonymous_chunk(
430 struct sessiond_trace_chunk_registry
*sessiond_registry
,
431 const lttng_uuid sessiond_uuid
,
434 struct lttng_trace_chunk
*chunk
= NULL
;
435 struct trace_chunk_registry_ht_element
*element
;
436 struct trace_chunk_registry_ht_key key
;
437 char uuid_str
[LTTNG_UUID_STR_LEN
];
439 lttng_uuid_to_str(sessiond_uuid
, uuid_str
);
441 lttng_uuid_copy(key
.sessiond_uuid
, sessiond_uuid
);
442 element
= trace_chunk_registry_ht_element_find(sessiond_registry
, &key
);
444 ERR("Failed to find trace chunk registry of sessiond {%s}",
449 chunk
= lttng_trace_chunk_registry_find_anonymous_chunk(
450 element
->trace_chunk_registry
,
452 trace_chunk_registry_ht_element_put(element
);
457 struct lttng_trace_chunk
*
458 sessiond_trace_chunk_registry_get_chunk(
459 struct sessiond_trace_chunk_registry
*sessiond_registry
,
460 const lttng_uuid sessiond_uuid
,
461 uint64_t session_id
, uint64_t chunk_id
)
463 struct lttng_trace_chunk
*chunk
= NULL
;
464 struct trace_chunk_registry_ht_element
*element
;
465 struct trace_chunk_registry_ht_key key
;
466 char uuid_str
[LTTNG_UUID_STR_LEN
];
468 lttng_uuid_to_str(sessiond_uuid
, uuid_str
);
470 lttng_uuid_copy(key
.sessiond_uuid
, sessiond_uuid
);
471 element
= trace_chunk_registry_ht_element_find(sessiond_registry
, &key
);
473 ERR("Failed to find trace chunk registry of sessiond {%s}",
478 chunk
= lttng_trace_chunk_registry_find_chunk(
479 element
->trace_chunk_registry
,
480 session_id
, chunk_id
);
481 trace_chunk_registry_ht_element_put(element
);
486 int sessiond_trace_chunk_registry_chunk_exists(
487 struct sessiond_trace_chunk_registry
*sessiond_registry
,
488 const lttng_uuid sessiond_uuid
,
489 uint64_t session_id
, uint64_t chunk_id
, bool *chunk_exists
)
492 struct trace_chunk_registry_ht_element
*element
;
493 struct trace_chunk_registry_ht_key key
;
495 lttng_uuid_copy(key
.sessiond_uuid
, sessiond_uuid
);
496 element
= trace_chunk_registry_ht_element_find(sessiond_registry
, &key
);
498 char uuid_str
[LTTNG_UUID_STR_LEN
];
500 lttng_uuid_to_str(sessiond_uuid
, uuid_str
);
502 * While this certainly means that the chunk does not exist,
503 * it is unexpected for a chunk existence query to target a
504 * session daemon that does not have an active
505 * connection/registry. This would indicate a protocol
506 * (or internal) error.
508 ERR("Failed to find trace chunk registry of sessiond {%s}",
514 ret
= lttng_trace_chunk_registry_chunk_exists(
515 element
->trace_chunk_registry
,
516 session_id
, chunk_id
, chunk_exists
);
517 trace_chunk_registry_ht_element_put(element
);