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.h"
10 #include <urcu/rculfhash.h>
12 #include <common/macros.h>
13 #include <common/hashtable/hashtable.h>
14 #include <common/hashtable/utils.h>
15 #include <common/trace-chunk-registry.h>
16 #include <common/defaults.h>
17 #include <common/error.h>
18 #include <common/string-utils/format.h>
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. */
59 struct trace_chunk_registry_ht_key
{
60 lttng_uuid sessiond_uuid
;
63 struct trace_chunk_registry_ht_element
{
64 struct trace_chunk_registry_ht_key key
;
66 /* Node into the sessiond_trace_chunk_registry's hash table. */
67 struct cds_lfht_node ht_node
;
68 /* Used for defered call_rcu reclaim. */
69 struct rcu_head rcu_node
;
70 struct lttng_trace_chunk_registry
*trace_chunk_registry
;
71 struct sessiond_trace_chunk_registry
*sessiond_trace_chunk_registry
;
75 unsigned long trace_chunk_registry_ht_key_hash(
76 const struct trace_chunk_registry_ht_key
*key
)
78 uint64_t uuid_h1
= ((uint64_t *) key
->sessiond_uuid
)[0];
79 uint64_t uuid_h2
= ((uint64_t *) key
->sessiond_uuid
)[1];
81 return hash_key_u64(&uuid_h1
, lttng_ht_seed
) ^
82 hash_key_u64(&uuid_h2
, lttng_ht_seed
);
85 /* cds_lfht match function */
87 int trace_chunk_registry_ht_key_match(struct cds_lfht_node
*node
,
90 const struct trace_chunk_registry_ht_key
*key
=
91 (struct trace_chunk_registry_ht_key
*) _key
;
92 struct trace_chunk_registry_ht_element
*registry
;
94 registry
= container_of(node
, typeof(*registry
), ht_node
);
95 return lttng_uuid_is_equal(key
->sessiond_uuid
,
96 registry
->key
.sessiond_uuid
);
100 void trace_chunk_registry_ht_element_free(struct rcu_head
*node
)
102 struct trace_chunk_registry_ht_element
*element
=
103 container_of(node
, typeof(*element
), rcu_node
);
109 void trace_chunk_registry_ht_element_release(struct urcu_ref
*ref
)
111 struct trace_chunk_registry_ht_element
*element
=
112 container_of(ref
, typeof(*element
), ref
);
113 char uuid_str
[LTTNG_UUID_STR_LEN
];
115 lttng_uuid_to_str(element
->key
.sessiond_uuid
, uuid_str
);
117 DBG("Destroying trace chunk registry associated to sessiond {%s}",
119 if (element
->sessiond_trace_chunk_registry
) {
122 cds_lfht_del(element
->sessiond_trace_chunk_registry
->ht
,
125 element
->sessiond_trace_chunk_registry
= NULL
;
128 lttng_trace_chunk_registry_destroy(element
->trace_chunk_registry
);
129 /* Defered reclaim of the object */
130 call_rcu(&element
->rcu_node
, trace_chunk_registry_ht_element_free
);
134 bool trace_chunk_registry_ht_element_get(
135 struct trace_chunk_registry_ht_element
*element
)
137 return urcu_ref_get_unless_zero(&element
->ref
);
141 void trace_chunk_registry_ht_element_put(
142 struct trace_chunk_registry_ht_element
*element
)
148 urcu_ref_put(&element
->ref
, trace_chunk_registry_ht_element_release
);
151 /* Acquires a reference to the returned element on behalf of the caller. */
153 struct trace_chunk_registry_ht_element
*trace_chunk_registry_ht_element_find(
154 struct sessiond_trace_chunk_registry
*sessiond_registry
,
155 const struct trace_chunk_registry_ht_key
*key
)
157 struct trace_chunk_registry_ht_element
*element
= NULL
;
158 struct cds_lfht_node
*node
;
159 struct cds_lfht_iter iter
;
162 cds_lfht_lookup(sessiond_registry
->ht
,
163 trace_chunk_registry_ht_key_hash(key
),
164 trace_chunk_registry_ht_key_match
,
167 node
= cds_lfht_iter_get_node(&iter
);
169 element
= container_of(node
, typeof(*element
), ht_node
);
171 * Only consider the look-up as successful if a reference
174 if (!trace_chunk_registry_ht_element_get(element
)) {
183 int trace_chunk_registry_ht_element_create(
184 struct sessiond_trace_chunk_registry
*sessiond_registry
,
185 const struct trace_chunk_registry_ht_key
*key
)
188 struct trace_chunk_registry_ht_element
*new_element
;
189 struct lttng_trace_chunk_registry
*trace_chunk_registry
;
190 char uuid_str
[LTTNG_UUID_STR_LEN
];
192 lttng_uuid_to_str(key
->sessiond_uuid
, uuid_str
);
194 trace_chunk_registry
= lttng_trace_chunk_registry_create();
195 if (!trace_chunk_registry
) {
200 new_element
= zmalloc(sizeof(*new_element
));
206 memcpy(&new_element
->key
, key
, sizeof(new_element
->key
));
207 urcu_ref_init(&new_element
->ref
);
208 cds_lfht_node_init(&new_element
->ht_node
);
209 new_element
->trace_chunk_registry
= trace_chunk_registry
;
210 trace_chunk_registry
= NULL
;
212 /* Attempt to publish the new element. */
215 struct cds_lfht_node
*published_node
;
216 struct trace_chunk_registry_ht_element
*published_element
;
218 published_node
= cds_lfht_add_unique(sessiond_registry
->ht
,
219 trace_chunk_registry_ht_key_hash(&new_element
->key
),
220 trace_chunk_registry_ht_key_match
,
222 &new_element
->ht_node
);
223 if (published_node
== &new_element
->ht_node
) {
224 /* New element published successfully. */
225 DBG("Created trace chunk registry for sessiond {%s}",
227 new_element
->sessiond_trace_chunk_registry
=
233 * An equivalent element was published during the creation of
234 * this element. Attempt to acquire a reference to the one that
235 * was already published and release the reference to the copy
236 * we created if successful.
238 published_element
= container_of(published_node
,
239 typeof(*published_element
), ht_node
);
240 if (trace_chunk_registry_ht_element_get(published_element
)) {
241 DBG("Acquired reference to trace chunk registry of sessiond {%s}",
243 trace_chunk_registry_ht_element_put(new_element
);
248 * A reference to the previously published element could not
249 * be acquired. Hence, retry to publish our copy of the
256 ERR("Failed to create trace chunk registry for session daemon {%s}",
259 lttng_trace_chunk_registry_destroy(trace_chunk_registry
);
263 struct sessiond_trace_chunk_registry
*sessiond_trace_chunk_registry_create(void)
265 struct sessiond_trace_chunk_registry
*sessiond_registry
=
266 zmalloc(sizeof(*sessiond_registry
));
268 if (!sessiond_registry
) {
272 sessiond_registry
->ht
= cds_lfht_new(DEFAULT_HT_SIZE
,
273 1, 0, CDS_LFHT_AUTO_RESIZE
| CDS_LFHT_ACCOUNTING
, NULL
);
274 if (!sessiond_registry
->ht
) {
279 return sessiond_registry
;
281 sessiond_trace_chunk_registry_destroy(sessiond_registry
);
285 void sessiond_trace_chunk_registry_destroy(
286 struct sessiond_trace_chunk_registry
*sessiond_registry
)
288 int ret
= cds_lfht_destroy(sessiond_registry
->ht
, NULL
);
291 free(sessiond_registry
);
294 int sessiond_trace_chunk_registry_session_created(
295 struct sessiond_trace_chunk_registry
*sessiond_registry
,
296 const lttng_uuid sessiond_uuid
)
299 struct trace_chunk_registry_ht_key key
;
300 struct trace_chunk_registry_ht_element
*element
;
302 lttng_uuid_copy(key
.sessiond_uuid
, sessiond_uuid
);
304 element
= trace_chunk_registry_ht_element_find(sessiond_registry
, &key
);
306 char uuid_str
[LTTNG_UUID_STR_LEN
];
308 lttng_uuid_to_str(sessiond_uuid
, uuid_str
);
309 DBG("Acquired reference to trace chunk registry of sessiond {%s}",
313 ret
= trace_chunk_registry_ht_element_create(
314 sessiond_registry
, &key
);
320 int sessiond_trace_chunk_registry_session_destroyed(
321 struct sessiond_trace_chunk_registry
*sessiond_registry
,
322 const lttng_uuid sessiond_uuid
)
325 struct trace_chunk_registry_ht_key key
;
326 struct trace_chunk_registry_ht_element
*element
;
327 char uuid_str
[LTTNG_UUID_STR_LEN
];
329 lttng_uuid_to_str(sessiond_uuid
, uuid_str
);
330 lttng_uuid_copy(key
.sessiond_uuid
, sessiond_uuid
);
332 element
= trace_chunk_registry_ht_element_find(sessiond_registry
, &key
);
334 DBG("Releasing reference to trace chunk registry of sessiond {%s}",
337 * Release the reference held by the session and the reference
338 * acquired through the "find" operation.
340 trace_chunk_registry_ht_element_put(element
);
341 trace_chunk_registry_ht_element_put(element
);
343 ERR("Failed to find trace chunk registry of sessiond {%s}",
350 struct lttng_trace_chunk
*sessiond_trace_chunk_registry_publish_chunk(
351 struct sessiond_trace_chunk_registry
*sessiond_registry
,
352 const lttng_uuid sessiond_uuid
, uint64_t session_id
,
353 struct lttng_trace_chunk
*new_chunk
)
355 enum lttng_trace_chunk_status status
;
357 bool is_anonymous_chunk
;
358 struct trace_chunk_registry_ht_key key
;
359 struct trace_chunk_registry_ht_element
*element
= NULL
;
360 char uuid_str
[LTTNG_UUID_STR_LEN
];
361 char chunk_id_str
[MAX_INT_DEC_LEN(typeof(chunk_id
))] = "-1";
362 struct lttng_trace_chunk
*published_chunk
= NULL
;
364 lttng_uuid_to_str(sessiond_uuid
, uuid_str
);
365 lttng_uuid_copy(key
.sessiond_uuid
, sessiond_uuid
);
367 status
= lttng_trace_chunk_get_id(new_chunk
, &chunk_id
);
368 if (status
== LTTNG_TRACE_CHUNK_STATUS_OK
) {
371 ret
= snprintf(chunk_id_str
, sizeof(chunk_id_str
), "%" PRIu64
,
374 lttng_strncpy(chunk_id_str
, "-1", sizeof(chunk_id_str
));
375 WARN("Failed to format trace chunk id");
377 is_anonymous_chunk
= false;
378 } else if (status
== LTTNG_TRACE_CHUNK_STATUS_NONE
) {
379 is_anonymous_chunk
= true;
381 ERR("Failed to get trace chunk id");
385 DBG("Attempting to publish trace chunk: sessiond {%s}, session_id = "
386 "%" PRIu64
", chunk_id = %s",
387 uuid_str
, session_id
,
388 is_anonymous_chunk
? "anonymous" : chunk_id_str
);
390 element
= trace_chunk_registry_ht_element_find(sessiond_registry
, &key
);
392 ERR("Failed to find registry of sessiond {%s}", uuid_str
);
396 published_chunk
= lttng_trace_chunk_registry_publish_chunk(
397 element
->trace_chunk_registry
, session_id
, new_chunk
);
399 * At this point, two references to the published chunks exist. One
400 * is taken by the registry while the other is being returned to the
401 * caller. In the use case of the relay daemon, the reference held
402 * by the registry itself is undesirable.
404 * We want the trace chunk to be removed from the registry as soon
405 * as it is not being used by the relay daemon (through a session
406 * or a stream). This differs from the behaviour of the consumer
407 * daemon which relies on an explicit command from the session
408 * daemon to release the registry's reference.
410 lttng_trace_chunk_put(published_chunk
);
412 trace_chunk_registry_ht_element_put(element
);
413 return published_chunk
;
416 struct lttng_trace_chunk
*
417 sessiond_trace_chunk_registry_get_anonymous_chunk(
418 struct sessiond_trace_chunk_registry
*sessiond_registry
,
419 const lttng_uuid sessiond_uuid
,
422 struct lttng_trace_chunk
*chunk
= NULL
;
423 struct trace_chunk_registry_ht_element
*element
;
424 struct trace_chunk_registry_ht_key key
;
425 char uuid_str
[LTTNG_UUID_STR_LEN
];
427 lttng_uuid_to_str(sessiond_uuid
, uuid_str
);
429 lttng_uuid_copy(key
.sessiond_uuid
, sessiond_uuid
);
430 element
= trace_chunk_registry_ht_element_find(sessiond_registry
, &key
);
432 ERR("Failed to find trace chunk registry of sessiond {%s}",
437 chunk
= lttng_trace_chunk_registry_find_anonymous_chunk(
438 element
->trace_chunk_registry
,
440 trace_chunk_registry_ht_element_put(element
);
445 struct lttng_trace_chunk
*
446 sessiond_trace_chunk_registry_get_chunk(
447 struct sessiond_trace_chunk_registry
*sessiond_registry
,
448 const lttng_uuid sessiond_uuid
,
449 uint64_t session_id
, uint64_t chunk_id
)
451 struct lttng_trace_chunk
*chunk
= NULL
;
452 struct trace_chunk_registry_ht_element
*element
;
453 struct trace_chunk_registry_ht_key key
;
454 char uuid_str
[LTTNG_UUID_STR_LEN
];
456 lttng_uuid_to_str(sessiond_uuid
, uuid_str
);
458 lttng_uuid_copy(key
.sessiond_uuid
, sessiond_uuid
);
459 element
= trace_chunk_registry_ht_element_find(sessiond_registry
, &key
);
461 ERR("Failed to find trace chunk registry of sessiond {%s}",
466 chunk
= lttng_trace_chunk_registry_find_chunk(
467 element
->trace_chunk_registry
,
468 session_id
, chunk_id
);
469 trace_chunk_registry_ht_element_put(element
);
474 int sessiond_trace_chunk_registry_chunk_exists(
475 struct sessiond_trace_chunk_registry
*sessiond_registry
,
476 const lttng_uuid sessiond_uuid
,
477 uint64_t session_id
, uint64_t chunk_id
, bool *chunk_exists
)
480 struct trace_chunk_registry_ht_element
*element
;
481 struct trace_chunk_registry_ht_key key
;
483 lttng_uuid_copy(key
.sessiond_uuid
, sessiond_uuid
);
484 element
= trace_chunk_registry_ht_element_find(sessiond_registry
, &key
);
486 char uuid_str
[LTTNG_UUID_STR_LEN
];
488 lttng_uuid_to_str(sessiond_uuid
, uuid_str
);
490 * While this certainly means that the chunk does not exist,
491 * it is unexpected for a chunk existence query to target a
492 * session daemon that does not have an active
493 * connection/registry. This would indicate a protocol
494 * (or internal) error.
496 ERR("Failed to find trace chunk registry of sessiond {%s}",
502 ret
= lttng_trace_chunk_registry_chunk_exists(
503 element
->trace_chunk_registry
,
504 session_id
, chunk_id
, chunk_exists
);
505 trace_chunk_registry_ht_element_put(element
);