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>
21 #include <urcu/rculfhash.h>
25 * Lifetime of trace chunks within the relay daemon.
27 * Trace chunks are shared accross connections initiated from a given
28 * session daemon. When a session is created by a consumer daemon, the
29 * UUID of its associated session daemon is transmitted (in the case of
30 * 2.11+ consumer daemons).
32 * The sessiond_trace_chunk_registry_new_session() and
33 * sessiond_trace_chunk_registry_session_closed() methods create and
34 * manage the reference count of lttng_trace_chunk_registry objects
35 * associated to the various sessiond instances served by the relay daemon.
37 * When all sessions associated with a given sessiond instance are
38 * destroyed, its registry is destroyed.
40 * lttng_trace_chunk objects are uniquely identified by the
41 * (sessiond_uuid, sessiond_session_id, chunk_id) tuple. If a trace chunk
42 * matching that tuple already exists, a new reference to the trace chunk
43 * is acquired and it is returned to the caller. Otherwise, a new trace
44 * chunk is created. This is how trace chunks are de-duplicated across
45 * multiple consumer daemons managed by the same session daemon.
47 * Note that trace chunks are always added to their matching
48 * lttng_trace_chunk_registry. They are automatically removed from the
49 * trace chunk registry when their reference count reaches zero.
53 * It is assumed that the sessiond_trace_chunk_registry is created and
54 * destroyed by the same thread.
56 struct sessiond_trace_chunk_registry
{
57 /* Maps an lttng_uuid to an lttng_trace_chunk_registry. */
62 struct trace_chunk_registry_ht_key
{
63 lttng_uuid sessiond_uuid
;
66 struct trace_chunk_registry_ht_element
{
67 struct trace_chunk_registry_ht_key key
;
69 /* Node into the sessiond_trace_chunk_registry's hash table. */
70 struct cds_lfht_node ht_node
;
71 /* Used for defered call_rcu reclaim. */
72 struct rcu_head rcu_node
;
73 struct lttng_trace_chunk_registry
*trace_chunk_registry
;
74 struct sessiond_trace_chunk_registry
*sessiond_trace_chunk_registry
;
78 static unsigned long trace_chunk_registry_ht_key_hash(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
) ^ hash_key_u64(&uuid_h2
, lttng_ht_seed
);
86 /* cds_lfht match function */
87 static int trace_chunk_registry_ht_key_match(struct cds_lfht_node
*node
, const void *_key
)
89 const struct trace_chunk_registry_ht_key
*key
= (struct trace_chunk_registry_ht_key
*) _key
;
90 struct trace_chunk_registry_ht_element
*registry
;
92 registry
= lttng::utils::container_of(node
, &trace_chunk_registry_ht_element::ht_node
);
93 return key
->sessiond_uuid
== registry
->key
.sessiond_uuid
;
96 static void trace_chunk_registry_ht_element_free(struct rcu_head
*node
)
98 struct trace_chunk_registry_ht_element
*element
=
99 lttng::utils::container_of(node
, &trace_chunk_registry_ht_element::rcu_node
);
104 static void trace_chunk_registry_ht_element_release(struct urcu_ref
*ref
)
106 struct trace_chunk_registry_ht_element
*element
=
107 lttng::utils::container_of(ref
, &trace_chunk_registry_ht_element::ref
);
108 char uuid_str
[LTTNG_UUID_STR_LEN
];
110 lttng_uuid_to_str(element
->key
.sessiond_uuid
, uuid_str
);
112 DBG("Destroying trace chunk registry associated to sessiond {%s}", uuid_str
);
113 if (element
->sessiond_trace_chunk_registry
) {
116 cds_lfht_del(element
->sessiond_trace_chunk_registry
->ht
, &element
->ht_node
);
118 element
->sessiond_trace_chunk_registry
= NULL
;
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
= NULL
;
146 struct cds_lfht_node
*node
;
147 struct cds_lfht_iter iter
;
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
)) {
172 trace_chunk_registry_ht_element_create(struct sessiond_trace_chunk_registry
*sessiond_registry
,
173 const struct trace_chunk_registry_ht_key
*key
)
176 struct trace_chunk_registry_ht_element
*new_element
;
177 struct lttng_trace_chunk_registry
*trace_chunk_registry
;
178 char uuid_str
[LTTNG_UUID_STR_LEN
];
180 lttng_uuid_to_str(key
->sessiond_uuid
, uuid_str
);
182 trace_chunk_registry
= lttng_trace_chunk_registry_create();
183 if (!trace_chunk_registry
) {
188 new_element
= zmalloc
<trace_chunk_registry_ht_element
>();
194 memcpy(&new_element
->key
, key
, sizeof(new_element
->key
));
195 urcu_ref_init(&new_element
->ref
);
196 cds_lfht_node_init(&new_element
->ht_node
);
197 new_element
->trace_chunk_registry
= trace_chunk_registry
;
198 trace_chunk_registry
= NULL
;
200 /* Attempt to publish the new element. */
203 struct cds_lfht_node
*published_node
;
204 struct trace_chunk_registry_ht_element
*published_element
;
207 cds_lfht_add_unique(sessiond_registry
->ht
,
208 trace_chunk_registry_ht_key_hash(&new_element
->key
),
209 trace_chunk_registry_ht_key_match
,
211 &new_element
->ht_node
);
212 if (published_node
== &new_element
->ht_node
) {
213 /* New element published successfully. */
214 DBG("Created trace chunk registry for sessiond {%s}", uuid_str
);
215 new_element
->sessiond_trace_chunk_registry
= sessiond_registry
;
220 * An equivalent element was published during the creation of
221 * this element. Attempt to acquire a reference to the one that
222 * was already published and release the reference to the copy
223 * we created if successful.
225 published_element
= lttng::utils::container_of(
226 published_node
, &trace_chunk_registry_ht_element::ht_node
);
227 if (trace_chunk_registry_ht_element_get(published_element
)) {
228 DBG("Acquired reference to trace chunk registry of sessiond {%s}",
230 trace_chunk_registry_ht_element_put(new_element
);
235 * A reference to the previously published element could not
236 * be acquired. Hence, retry to publish our copy of the
243 ERR("Failed to create trace chunk registry for session daemon {%s}", uuid_str
);
245 lttng_trace_chunk_registry_destroy(trace_chunk_registry
);
249 struct sessiond_trace_chunk_registry
*sessiond_trace_chunk_registry_create(void)
251 struct sessiond_trace_chunk_registry
*sessiond_registry
=
252 zmalloc
<sessiond_trace_chunk_registry
>();
254 if (!sessiond_registry
) {
258 sessiond_registry
->ht
= cds_lfht_new(
259 DEFAULT_HT_SIZE
, 1, 0, CDS_LFHT_AUTO_RESIZE
| CDS_LFHT_ACCOUNTING
, NULL
);
260 if (!sessiond_registry
->ht
) {
265 return sessiond_registry
;
267 sessiond_trace_chunk_registry_destroy(sessiond_registry
);
271 void sessiond_trace_chunk_registry_destroy(struct sessiond_trace_chunk_registry
*sessiond_registry
)
273 int ret
= cds_lfht_destroy(sessiond_registry
->ht
, NULL
);
276 free(sessiond_registry
);
279 int sessiond_trace_chunk_registry_session_created(
280 struct sessiond_trace_chunk_registry
*sessiond_registry
, const lttng_uuid
& sessiond_uuid
)
283 struct trace_chunk_registry_ht_key key
;
284 struct trace_chunk_registry_ht_element
*element
;
286 key
.sessiond_uuid
= sessiond_uuid
;
288 element
= trace_chunk_registry_ht_element_find(sessiond_registry
, &key
);
290 char uuid_str
[LTTNG_UUID_STR_LEN
];
292 lttng_uuid_to_str(sessiond_uuid
, uuid_str
);
293 DBG("Acquired reference to trace chunk registry of sessiond {%s}", uuid_str
);
296 ret
= trace_chunk_registry_ht_element_create(sessiond_registry
, &key
);
302 int sessiond_trace_chunk_registry_session_destroyed(
303 struct sessiond_trace_chunk_registry
*sessiond_registry
, const lttng_uuid
& sessiond_uuid
)
306 struct trace_chunk_registry_ht_key key
;
307 struct trace_chunk_registry_ht_element
*element
;
308 char uuid_str
[LTTNG_UUID_STR_LEN
];
310 lttng_uuid_to_str(sessiond_uuid
, uuid_str
);
311 key
.sessiond_uuid
= sessiond_uuid
;
313 element
= trace_chunk_registry_ht_element_find(sessiond_registry
, &key
);
315 DBG("Releasing reference to trace chunk registry of sessiond {%s}", uuid_str
);
317 * Release the reference held by the session and the reference
318 * acquired through the "find" operation.
320 trace_chunk_registry_ht_element_put(element
);
321 trace_chunk_registry_ht_element_put(element
);
323 ERR("Failed to find trace chunk registry of sessiond {%s}", uuid_str
);
329 struct lttng_trace_chunk
*
330 sessiond_trace_chunk_registry_publish_chunk(struct sessiond_trace_chunk_registry
*sessiond_registry
,
331 const lttng_uuid
& sessiond_uuid
,
333 struct lttng_trace_chunk
*new_chunk
)
335 enum lttng_trace_chunk_status status
;
337 bool is_anonymous_chunk
;
338 struct trace_chunk_registry_ht_key key
;
339 struct trace_chunk_registry_ht_element
*element
= NULL
;
340 char uuid_str
[LTTNG_UUID_STR_LEN
];
341 char chunk_id_str
[MAX_INT_DEC_LEN(typeof(chunk_id
))] = "-1";
342 struct lttng_trace_chunk
*published_chunk
= NULL
;
343 bool trace_chunk_already_published
;
345 lttng_uuid_to_str(sessiond_uuid
, uuid_str
);
346 key
.sessiond_uuid
= sessiond_uuid
;
348 status
= lttng_trace_chunk_get_id(new_chunk
, &chunk_id
);
349 if (status
== LTTNG_TRACE_CHUNK_STATUS_OK
) {
352 ret
= snprintf(chunk_id_str
, sizeof(chunk_id_str
), "%" PRIu64
, chunk_id
);
354 lttng_strncpy(chunk_id_str
, "-1", sizeof(chunk_id_str
));
355 WARN("Failed to format trace chunk id");
357 is_anonymous_chunk
= false;
358 } else if (status
== LTTNG_TRACE_CHUNK_STATUS_NONE
) {
359 is_anonymous_chunk
= true;
361 ERR("Failed to get trace chunk id");
365 DBG("Attempting to publish trace chunk: sessiond {%s}, session_id = "
366 "%" PRIu64
", chunk_id = %s",
369 is_anonymous_chunk
? "anonymous" : chunk_id_str
);
371 element
= trace_chunk_registry_ht_element_find(sessiond_registry
, &key
);
373 ERR("Failed to find registry of sessiond {%s}", uuid_str
);
377 published_chunk
= lttng_trace_chunk_registry_publish_chunk(element
->trace_chunk_registry
,
380 &trace_chunk_already_published
);
382 * When the trace chunk is first published, two references to the
383 * published chunks exist. One is taken by the registry while the other
384 * is being returned to the caller. In the use case of the relay daemon,
385 * the reference held by the registry itself is undesirable.
387 * We want the trace chunk to be removed from the registry as soon
388 * as it is not being used by the relay daemon (through a session
389 * or a stream). This differs from the behaviour of the consumer
390 * daemon which relies on an explicit command from the session
391 * daemon to release the registry's reference.
393 * In cases where the trace chunk had already been published,
394 * the reference belonging to the sessiond trace chunk
395 * registry instance has already been 'put'. We simply return
396 * the published trace chunk with a reference taken on behalf of the
399 if (!trace_chunk_already_published
) {
400 lttng_trace_chunk_put(published_chunk
);
403 trace_chunk_registry_ht_element_put(element
);
404 return published_chunk
;
407 struct lttng_trace_chunk
*sessiond_trace_chunk_registry_get_anonymous_chunk(
408 struct sessiond_trace_chunk_registry
*sessiond_registry
,
409 const lttng_uuid
& sessiond_uuid
,
412 struct lttng_trace_chunk
*chunk
= NULL
;
413 struct trace_chunk_registry_ht_element
*element
;
414 struct trace_chunk_registry_ht_key key
;
415 char uuid_str
[LTTNG_UUID_STR_LEN
];
417 lttng_uuid_to_str(sessiond_uuid
, uuid_str
);
419 key
.sessiond_uuid
= sessiond_uuid
;
420 element
= trace_chunk_registry_ht_element_find(sessiond_registry
, &key
);
422 ERR("Failed to find trace chunk registry of sessiond {%s}", uuid_str
);
426 chunk
= lttng_trace_chunk_registry_find_anonymous_chunk(element
->trace_chunk_registry
,
428 trace_chunk_registry_ht_element_put(element
);
433 struct lttng_trace_chunk
*
434 sessiond_trace_chunk_registry_get_chunk(struct sessiond_trace_chunk_registry
*sessiond_registry
,
435 const lttng_uuid
& sessiond_uuid
,
439 struct lttng_trace_chunk
*chunk
= NULL
;
440 struct trace_chunk_registry_ht_element
*element
;
441 struct trace_chunk_registry_ht_key key
;
442 char uuid_str
[LTTNG_UUID_STR_LEN
];
444 lttng_uuid_to_str(sessiond_uuid
, uuid_str
);
446 key
.sessiond_uuid
= sessiond_uuid
;
447 element
= trace_chunk_registry_ht_element_find(sessiond_registry
, &key
);
449 ERR("Failed to find trace chunk registry of sessiond {%s}", uuid_str
);
453 chunk
= lttng_trace_chunk_registry_find_chunk(
454 element
->trace_chunk_registry
, session_id
, chunk_id
);
455 trace_chunk_registry_ht_element_put(element
);
460 int sessiond_trace_chunk_registry_chunk_exists(
461 struct sessiond_trace_chunk_registry
*sessiond_registry
,
462 const lttng_uuid
& sessiond_uuid
,
468 struct trace_chunk_registry_ht_element
*element
;
469 struct trace_chunk_registry_ht_key key
;
471 key
.sessiond_uuid
= sessiond_uuid
;
472 element
= trace_chunk_registry_ht_element_find(sessiond_registry
, &key
);
474 char uuid_str
[LTTNG_UUID_STR_LEN
];
476 lttng_uuid_to_str(sessiond_uuid
, uuid_str
);
478 * While this certainly means that the chunk does not exist,
479 * it is unexpected for a chunk existence query to target a
480 * session daemon that does not have an active
481 * connection/registry. This would indicate a protocol
482 * (or internal) error.
484 ERR("Failed to find trace chunk registry of sessiond {%s}", uuid_str
);
489 ret
= lttng_trace_chunk_registry_chunk_exists(
490 element
->trace_chunk_registry
, session_id
, chunk_id
, chunk_exists
);
491 trace_chunk_registry_ht_element_put(element
);