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 <common/defaults.hpp>
11 #include <common/error.hpp>
12 #include <common/hashtable/hashtable.hpp>
13 #include <common/hashtable/utils.hpp>
14 #include <common/macros.hpp>
15 #include <common/string-utils/format.hpp>
16 #include <common/trace-chunk-registry.hpp>
17 #include <common/urcu.hpp>
22 #include <urcu/rculfhash.h>
26 * Lifetime of trace chunks within the relay daemon.
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).
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.
38 * When all sessions associated with a given sessiond instance are
39 * destroyed, its registry is destroyed.
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.
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.
54 * It is assumed that the sessiond_trace_chunk_registry is created and
55 * destroyed by the same thread.
57 struct sessiond_trace_chunk_registry
{
58 /* Maps an lttng_uuid to an lttng_trace_chunk_registry. */
63 struct trace_chunk_registry_ht_key
{
64 lttng_uuid sessiond_uuid
;
67 struct trace_chunk_registry_ht_element
{
68 struct trace_chunk_registry_ht_key key
;
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
;
79 static unsigned long trace_chunk_registry_ht_key_hash(const struct trace_chunk_registry_ht_key
*key
)
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]);
84 return hash_key_u64(&uuid_h1
, lttng_ht_seed
) ^ hash_key_u64(&uuid_h2
, lttng_ht_seed
);
87 /* cds_lfht match function */
88 static int trace_chunk_registry_ht_key_match(struct cds_lfht_node
*node
, const void *_key
)
90 const struct trace_chunk_registry_ht_key
*key
= (struct trace_chunk_registry_ht_key
*) _key
;
91 struct trace_chunk_registry_ht_element
*registry
;
93 registry
= lttng::utils::container_of(node
, &trace_chunk_registry_ht_element::ht_node
);
94 return key
->sessiond_uuid
== registry
->key
.sessiond_uuid
;
97 static void trace_chunk_registry_ht_element_free(struct rcu_head
*node
)
99 struct trace_chunk_registry_ht_element
*element
=
100 lttng::utils::container_of(node
, &trace_chunk_registry_ht_element::rcu_node
);
105 static void trace_chunk_registry_ht_element_release(struct urcu_ref
*ref
)
107 struct trace_chunk_registry_ht_element
*element
=
108 lttng::utils::container_of(ref
, &trace_chunk_registry_ht_element::ref
);
109 char uuid_str
[LTTNG_UUID_STR_LEN
];
111 lttng_uuid_to_str(element
->key
.sessiond_uuid
, uuid_str
);
113 DBG("Destroying trace chunk registry associated to sessiond {%s}", uuid_str
);
114 if (element
->sessiond_trace_chunk_registry
) {
116 lttng::urcu::read_lock_guard read_lock
;
117 cds_lfht_del(element
->sessiond_trace_chunk_registry
->ht
, &element
->ht_node
);
118 element
->sessiond_trace_chunk_registry
= nullptr;
121 lttng_trace_chunk_registry_destroy(element
->trace_chunk_registry
);
122 /* Defered reclaim of the object */
123 call_rcu(&element
->rcu_node
, trace_chunk_registry_ht_element_free
);
126 static bool trace_chunk_registry_ht_element_get(struct trace_chunk_registry_ht_element
*element
)
128 return urcu_ref_get_unless_zero(&element
->ref
);
131 static void trace_chunk_registry_ht_element_put(struct trace_chunk_registry_ht_element
*element
)
137 urcu_ref_put(&element
->ref
, trace_chunk_registry_ht_element_release
);
140 /* Acquires a reference to the returned element on behalf of the caller. */
141 static struct trace_chunk_registry_ht_element
*
142 trace_chunk_registry_ht_element_find(struct sessiond_trace_chunk_registry
*sessiond_registry
,
143 const struct trace_chunk_registry_ht_key
*key
)
145 struct trace_chunk_registry_ht_element
*element
= nullptr;
146 struct cds_lfht_node
*node
;
147 struct cds_lfht_iter iter
;
149 lttng::urcu::read_lock_guard read_lock
;
150 cds_lfht_lookup(sessiond_registry
->ht
,
151 trace_chunk_registry_ht_key_hash(key
),
152 trace_chunk_registry_ht_key_match
,
155 node
= cds_lfht_iter_get_node(&iter
);
158 lttng::utils::container_of(node
, &trace_chunk_registry_ht_element::ht_node
);
160 * Only consider the look-up as successful if a reference
163 if (!trace_chunk_registry_ht_element_get(element
)) {
171 trace_chunk_registry_ht_element_create(struct sessiond_trace_chunk_registry
*sessiond_registry
,
172 const struct trace_chunk_registry_ht_key
*key
)
175 struct trace_chunk_registry_ht_element
*new_element
;
176 struct lttng_trace_chunk_registry
*trace_chunk_registry
;
177 char uuid_str
[LTTNG_UUID_STR_LEN
];
179 lttng_uuid_to_str(key
->sessiond_uuid
, uuid_str
);
181 trace_chunk_registry
= lttng_trace_chunk_registry_create();
182 if (!trace_chunk_registry
) {
187 new_element
= zmalloc
<trace_chunk_registry_ht_element
>();
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
;
197 trace_chunk_registry
= nullptr;
199 /* Attempt to publish the new element. */
202 * Keep the rcu read lock is held accross all attempts
203 * purely for efficiency reasons.
205 lttng::urcu::read_lock_guard read_lock
;
207 struct cds_lfht_node
*published_node
;
208 struct trace_chunk_registry_ht_element
*published_element
;
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
,
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
;
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.
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}",
234 trace_chunk_registry_ht_element_put(new_element
);
235 new_element
= nullptr;
239 * A reference to the previously published element could not
240 * be acquired. Hence, retry to publish our copy of the
247 ERR("Failed to create trace chunk registry for session daemon {%s}", uuid_str
);
249 lttng_trace_chunk_registry_destroy(trace_chunk_registry
);
253 struct sessiond_trace_chunk_registry
*sessiond_trace_chunk_registry_create()
255 struct sessiond_trace_chunk_registry
*sessiond_registry
=
256 zmalloc
<sessiond_trace_chunk_registry
>();
258 if (!sessiond_registry
) {
262 sessiond_registry
->ht
= cds_lfht_new(
263 DEFAULT_HT_SIZE
, 1, 0, CDS_LFHT_AUTO_RESIZE
| CDS_LFHT_ACCOUNTING
, nullptr);
264 if (!sessiond_registry
->ht
) {
269 return sessiond_registry
;
271 sessiond_trace_chunk_registry_destroy(sessiond_registry
);
275 void sessiond_trace_chunk_registry_destroy(struct sessiond_trace_chunk_registry
*sessiond_registry
)
277 int ret
= cds_lfht_destroy(sessiond_registry
->ht
, nullptr);
280 free(sessiond_registry
);
283 int sessiond_trace_chunk_registry_session_created(
284 struct sessiond_trace_chunk_registry
*sessiond_registry
, const lttng_uuid
& sessiond_uuid
)
287 struct trace_chunk_registry_ht_key key
;
288 struct trace_chunk_registry_ht_element
*element
;
290 key
.sessiond_uuid
= sessiond_uuid
;
292 element
= trace_chunk_registry_ht_element_find(sessiond_registry
, &key
);
294 char uuid_str
[LTTNG_UUID_STR_LEN
];
296 lttng_uuid_to_str(sessiond_uuid
, uuid_str
);
297 DBG("Acquired reference to trace chunk registry of sessiond {%s}", uuid_str
);
300 ret
= trace_chunk_registry_ht_element_create(sessiond_registry
, &key
);
306 int sessiond_trace_chunk_registry_session_destroyed(
307 struct sessiond_trace_chunk_registry
*sessiond_registry
, const lttng_uuid
& sessiond_uuid
)
310 struct trace_chunk_registry_ht_key key
;
311 struct trace_chunk_registry_ht_element
*element
;
312 char uuid_str
[LTTNG_UUID_STR_LEN
];
314 lttng_uuid_to_str(sessiond_uuid
, uuid_str
);
315 key
.sessiond_uuid
= sessiond_uuid
;
317 element
= trace_chunk_registry_ht_element_find(sessiond_registry
, &key
);
319 DBG("Releasing reference to trace chunk registry of sessiond {%s}", uuid_str
);
321 * Release the reference held by the session and the reference
322 * acquired through the "find" operation.
324 trace_chunk_registry_ht_element_put(element
);
325 trace_chunk_registry_ht_element_put(element
);
327 ERR("Failed to find trace chunk registry of sessiond {%s}", uuid_str
);
333 struct lttng_trace_chunk
*
334 sessiond_trace_chunk_registry_publish_chunk(struct sessiond_trace_chunk_registry
*sessiond_registry
,
335 const lttng_uuid
& sessiond_uuid
,
337 struct lttng_trace_chunk
*new_chunk
)
339 enum lttng_trace_chunk_status status
;
341 bool is_anonymous_chunk
;
342 struct trace_chunk_registry_ht_key key
;
343 struct trace_chunk_registry_ht_element
*element
= nullptr;
344 char uuid_str
[LTTNG_UUID_STR_LEN
];
345 char chunk_id_str
[MAX_INT_DEC_LEN(typeof(chunk_id
))] = "-1";
346 struct lttng_trace_chunk
*published_chunk
= nullptr;
347 bool trace_chunk_already_published
;
349 lttng_uuid_to_str(sessiond_uuid
, uuid_str
);
350 key
.sessiond_uuid
= sessiond_uuid
;
352 status
= lttng_trace_chunk_get_id(new_chunk
, &chunk_id
);
353 if (status
== LTTNG_TRACE_CHUNK_STATUS_OK
) {
356 ret
= snprintf(chunk_id_str
, sizeof(chunk_id_str
), "%" PRIu64
, chunk_id
);
358 lttng_strncpy(chunk_id_str
, "-1", sizeof(chunk_id_str
));
359 WARN("Failed to format trace chunk id");
361 is_anonymous_chunk
= false;
362 } else if (status
== LTTNG_TRACE_CHUNK_STATUS_NONE
) {
363 is_anonymous_chunk
= true;
365 ERR("Failed to get trace chunk id");
369 DBG("Attempting to publish trace chunk: sessiond {%s}, session_id = "
370 "%" PRIu64
", chunk_id = %s",
373 is_anonymous_chunk
? "anonymous" : chunk_id_str
);
375 element
= trace_chunk_registry_ht_element_find(sessiond_registry
, &key
);
377 ERR("Failed to find registry of sessiond {%s}", uuid_str
);
381 published_chunk
= lttng_trace_chunk_registry_publish_chunk(element
->trace_chunk_registry
,
384 &trace_chunk_already_published
);
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.
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.
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
403 if (!trace_chunk_already_published
) {
404 lttng_trace_chunk_put(published_chunk
);
407 trace_chunk_registry_ht_element_put(element
);
408 return published_chunk
;
411 struct lttng_trace_chunk
*sessiond_trace_chunk_registry_get_anonymous_chunk(
412 struct sessiond_trace_chunk_registry
*sessiond_registry
,
413 const lttng_uuid
& sessiond_uuid
,
416 struct lttng_trace_chunk
*chunk
= nullptr;
417 struct trace_chunk_registry_ht_element
*element
;
418 struct trace_chunk_registry_ht_key key
;
419 char uuid_str
[LTTNG_UUID_STR_LEN
];
421 lttng_uuid_to_str(sessiond_uuid
, uuid_str
);
423 key
.sessiond_uuid
= sessiond_uuid
;
424 element
= trace_chunk_registry_ht_element_find(sessiond_registry
, &key
);
426 ERR("Failed to find trace chunk registry of sessiond {%s}", uuid_str
);
430 chunk
= lttng_trace_chunk_registry_find_anonymous_chunk(element
->trace_chunk_registry
,
432 trace_chunk_registry_ht_element_put(element
);
437 struct lttng_trace_chunk
*
438 sessiond_trace_chunk_registry_get_chunk(struct sessiond_trace_chunk_registry
*sessiond_registry
,
439 const lttng_uuid
& sessiond_uuid
,
443 struct lttng_trace_chunk
*chunk
= nullptr;
444 struct trace_chunk_registry_ht_element
*element
;
445 struct trace_chunk_registry_ht_key key
;
446 char uuid_str
[LTTNG_UUID_STR_LEN
];
448 lttng_uuid_to_str(sessiond_uuid
, uuid_str
);
450 key
.sessiond_uuid
= sessiond_uuid
;
451 element
= trace_chunk_registry_ht_element_find(sessiond_registry
, &key
);
453 ERR("Failed to find trace chunk registry of sessiond {%s}", uuid_str
);
457 chunk
= lttng_trace_chunk_registry_find_chunk(
458 element
->trace_chunk_registry
, session_id
, chunk_id
);
459 trace_chunk_registry_ht_element_put(element
);
464 int sessiond_trace_chunk_registry_chunk_exists(
465 struct sessiond_trace_chunk_registry
*sessiond_registry
,
466 const lttng_uuid
& sessiond_uuid
,
472 struct trace_chunk_registry_ht_element
*element
;
473 struct trace_chunk_registry_ht_key key
;
475 key
.sessiond_uuid
= sessiond_uuid
;
476 element
= trace_chunk_registry_ht_element_find(sessiond_registry
, &key
);
478 char uuid_str
[LTTNG_UUID_STR_LEN
];
480 lttng_uuid_to_str(sessiond_uuid
, uuid_str
);
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.
488 ERR("Failed to find trace chunk registry of sessiond {%s}", uuid_str
);
493 ret
= lttng_trace_chunk_registry_chunk_exists(
494 element
->trace_chunk_registry
, session_id
, chunk_id
, chunk_exists
);
495 trace_chunk_registry_ht_element_put(element
);