2 * Copyright (C) 2019 - Jérémie Galarneau <jeremie.galarneau@efficios.com>
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License, version 2 only,
6 * as published by the Free Software Foundation.
8 * This program is distributed in the hope that it will be useful, but WITHOUT
9 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
10 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
13 * You should have received a copy of the GNU General Public License along
14 * with this program; if not, write to the Free Software Foundation, Inc.,
15 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 #include "sessiond-trace-chunks.h"
20 #include <urcu/rculfhash.h>
22 #include <common/macros.h>
23 #include <common/hashtable/hashtable.h>
24 #include <common/hashtable/utils.h>
25 #include <common/trace-chunk-registry.h>
26 #include <common/defaults.h>
27 #include <common/error.h>
28 #include <common/string-utils/format.h>
33 * Lifetime of trace chunks within the relay daemon.
35 * Trace chunks are shared accross connections initiated from a given
36 * session daemon. When a session is created by a consumer daemon, the
37 * UUID of its associated session daemon is transmitted (in the case of
38 * 2.11+ consumer daemons).
40 * The sessiond_trace_chunk_registry_new_session() and
41 * sessiond_trace_chunk_registry_session_closed() methods create and
42 * manage the reference count of lttng_trace_chunk_registry objects
43 * associated to the various sessiond instances served by the relay daemon.
45 * When all sessions associated with a given sessiond instance are
46 * destroyed, its registry is destroyed.
48 * lttng_trace_chunk objects are uniquely identified by the
49 * (sessiond_uuid, sessiond_session_id, chunk_id) tuple. If a trace chunk
50 * matching that tuple already exists, a new reference to the trace chunk
51 * is acquired and it is returned to the caller. Otherwise, a new trace
52 * chunk is created. This is how trace chunks are de-duplicated across
53 * multiple consumer daemons managed by the same session daemon.
55 * Note that trace chunks are always added to their matching
56 * lttng_trace_chunk_registry. They are automatically removed from the
57 * trace chunk registry when their reference count reaches zero.
61 * It is assumed that the sessiond_trace_chunk_registry is created and
62 * destroyed by the same thread.
64 struct sessiond_trace_chunk_registry
{
65 /* Maps an lttng_uuid to an lttng_trace_chunk_registry. */
69 struct trace_chunk_registry_ht_key
{
70 lttng_uuid sessiond_uuid
;
73 struct trace_chunk_registry_ht_element
{
74 struct trace_chunk_registry_ht_key key
;
76 /* Node into the sessiond_trace_chunk_registry's hash table. */
77 struct cds_lfht_node ht_node
;
78 /* Used for defered call_rcu reclaim. */
79 struct rcu_head rcu_node
;
80 struct lttng_trace_chunk_registry
*trace_chunk_registry
;
81 struct sessiond_trace_chunk_registry
*sessiond_trace_chunk_registry
;
85 unsigned long trace_chunk_registry_ht_key_hash(
86 const struct trace_chunk_registry_ht_key
*key
)
88 uint64_t uuid_h1
= ((uint64_t *) key
->sessiond_uuid
)[0];
89 uint64_t uuid_h2
= ((uint64_t *) key
->sessiond_uuid
)[1];
91 return hash_key_u64(&uuid_h1
, lttng_ht_seed
) ^
92 hash_key_u64(&uuid_h2
, lttng_ht_seed
);
95 /* cds_lfht match function */
97 int trace_chunk_registry_ht_key_match(struct cds_lfht_node
*node
,
100 const struct trace_chunk_registry_ht_key
*key
=
101 (struct trace_chunk_registry_ht_key
*) _key
;
102 struct trace_chunk_registry_ht_element
*registry
;
104 registry
= container_of(node
, typeof(*registry
), ht_node
);
105 return lttng_uuid_is_equal(key
->sessiond_uuid
,
106 registry
->key
.sessiond_uuid
);
110 void trace_chunk_registry_ht_element_free(struct rcu_head
*node
)
112 struct trace_chunk_registry_ht_element
*element
=
113 container_of(node
, typeof(*element
), rcu_node
);
119 void trace_chunk_registry_ht_element_release(struct urcu_ref
*ref
)
121 struct trace_chunk_registry_ht_element
*element
=
122 container_of(ref
, typeof(*element
), ref
);
123 char uuid_str
[UUID_STR_LEN
];
125 lttng_uuid_to_str(element
->key
.sessiond_uuid
, uuid_str
);
127 DBG("Destroying trace chunk registry associated to sessiond {%s}",
129 if (element
->sessiond_trace_chunk_registry
) {
132 cds_lfht_del(element
->sessiond_trace_chunk_registry
->ht
,
135 element
->sessiond_trace_chunk_registry
= NULL
;
138 lttng_trace_chunk_registry_destroy(element
->trace_chunk_registry
);
139 /* Defered reclaim of the object */
140 call_rcu(&element
->rcu_node
, trace_chunk_registry_ht_element_free
);
144 bool trace_chunk_registry_ht_element_get(
145 struct trace_chunk_registry_ht_element
*element
)
147 return urcu_ref_get_unless_zero(&element
->ref
);
151 void trace_chunk_registry_ht_element_put(
152 struct trace_chunk_registry_ht_element
*element
)
158 urcu_ref_put(&element
->ref
, trace_chunk_registry_ht_element_release
);
161 /* Acquires a reference to the returned element on behalf of the caller. */
163 struct trace_chunk_registry_ht_element
*trace_chunk_registry_ht_element_find(
164 struct sessiond_trace_chunk_registry
*sessiond_registry
,
165 const struct trace_chunk_registry_ht_key
*key
)
167 struct trace_chunk_registry_ht_element
*element
= NULL
;
168 struct cds_lfht_node
*node
;
169 struct cds_lfht_iter iter
;
172 cds_lfht_lookup(sessiond_registry
->ht
,
173 trace_chunk_registry_ht_key_hash(key
),
174 trace_chunk_registry_ht_key_match
,
177 node
= cds_lfht_iter_get_node(&iter
);
179 element
= container_of(node
, typeof(*element
), ht_node
);
181 * Only consider the look-up as successful if a reference
184 if (!trace_chunk_registry_ht_element_get(element
)) {
193 int trace_chunk_registry_ht_element_create(
194 struct sessiond_trace_chunk_registry
*sessiond_registry
,
195 const struct trace_chunk_registry_ht_key
*key
)
198 struct trace_chunk_registry_ht_element
*new_element
;
199 struct lttng_trace_chunk_registry
*trace_chunk_registry
;
200 char uuid_str
[UUID_STR_LEN
];
202 lttng_uuid_to_str(key
->sessiond_uuid
, uuid_str
);
204 trace_chunk_registry
= lttng_trace_chunk_registry_create();
205 if (!trace_chunk_registry
) {
210 new_element
= zmalloc(sizeof(*new_element
));
216 memcpy(&new_element
->key
, key
, sizeof(new_element
->key
));
217 urcu_ref_init(&new_element
->ref
);
218 cds_lfht_node_init(&new_element
->ht_node
);
219 new_element
->trace_chunk_registry
= trace_chunk_registry
;
220 trace_chunk_registry
= NULL
;
222 /* Attempt to publish the new element. */
225 struct cds_lfht_node
*published_node
;
226 struct trace_chunk_registry_ht_element
*published_element
;
228 published_node
= cds_lfht_add_unique(sessiond_registry
->ht
,
229 trace_chunk_registry_ht_key_hash(&new_element
->key
),
230 trace_chunk_registry_ht_key_match
,
232 &new_element
->ht_node
);
233 if (published_node
== &new_element
->ht_node
) {
234 /* New element published successfully. */
235 DBG("Created trace chunk registry for sessiond {%s}",
237 new_element
->sessiond_trace_chunk_registry
=
243 * An equivalent element was published during the creation of
244 * this element. Attempt to acquire a reference to the one that
245 * was already published and release the reference to the copy
246 * we created if successful.
248 published_element
= container_of(published_node
,
249 typeof(*published_element
), ht_node
);
250 if (trace_chunk_registry_ht_element_get(published_element
)) {
251 DBG("Acquired reference to trace chunk registry of sessiond {%s}",
253 trace_chunk_registry_ht_element_put(new_element
);
258 * A reference to the previously published element could not
259 * be acquired. Hence, retry to publish our copy of the
266 ERR("Failed to create trace chunk registry for session daemon {%s}",
269 lttng_trace_chunk_registry_destroy(trace_chunk_registry
);
273 struct sessiond_trace_chunk_registry
*sessiond_trace_chunk_registry_create(void)
275 struct sessiond_trace_chunk_registry
*sessiond_registry
=
276 zmalloc(sizeof(*sessiond_registry
));
278 if (!sessiond_registry
) {
282 sessiond_registry
->ht
= cds_lfht_new(DEFAULT_HT_SIZE
,
283 1, 0, CDS_LFHT_AUTO_RESIZE
| CDS_LFHT_ACCOUNTING
, NULL
);
284 if (!sessiond_registry
->ht
) {
289 return sessiond_registry
;
291 sessiond_trace_chunk_registry_destroy(sessiond_registry
);
295 void sessiond_trace_chunk_registry_destroy(
296 struct sessiond_trace_chunk_registry
*sessiond_registry
)
298 int ret
= cds_lfht_destroy(sessiond_registry
->ht
, NULL
);
301 free(sessiond_registry
);
304 int sessiond_trace_chunk_registry_session_created(
305 struct sessiond_trace_chunk_registry
*sessiond_registry
,
306 const lttng_uuid sessiond_uuid
)
309 struct trace_chunk_registry_ht_key key
;
310 struct trace_chunk_registry_ht_element
*element
;
312 lttng_uuid_copy(key
.sessiond_uuid
, sessiond_uuid
);
314 element
= trace_chunk_registry_ht_element_find(sessiond_registry
, &key
);
316 char uuid_str
[UUID_STR_LEN
];
318 lttng_uuid_to_str(sessiond_uuid
, uuid_str
);
319 DBG("Acquired reference to trace chunk registry of sessiond {%s}",
323 ret
= trace_chunk_registry_ht_element_create(
324 sessiond_registry
, &key
);
330 int sessiond_trace_chunk_registry_session_destroyed(
331 struct sessiond_trace_chunk_registry
*sessiond_registry
,
332 const lttng_uuid sessiond_uuid
)
335 struct trace_chunk_registry_ht_key key
;
336 struct trace_chunk_registry_ht_element
*element
;
337 char uuid_str
[UUID_STR_LEN
];
339 lttng_uuid_to_str(sessiond_uuid
, uuid_str
);
340 lttng_uuid_copy(key
.sessiond_uuid
, sessiond_uuid
);
342 element
= trace_chunk_registry_ht_element_find(sessiond_registry
, &key
);
344 DBG("Releasing reference to trace chunk registry of sessiond {%s}",
347 * Release the reference held by the session and the reference
348 * acquired through the "find" operation.
350 trace_chunk_registry_ht_element_put(element
);
351 trace_chunk_registry_ht_element_put(element
);
353 ERR("Failed to find trace chunk registry of sessiond {%s}",
360 struct lttng_trace_chunk
*sessiond_trace_chunk_registry_publish_chunk(
361 struct sessiond_trace_chunk_registry
*sessiond_registry
,
362 const lttng_uuid sessiond_uuid
, uint64_t session_id
,
363 struct lttng_trace_chunk
*new_chunk
)
365 enum lttng_trace_chunk_status status
;
367 bool is_anonymous_chunk
;
368 struct trace_chunk_registry_ht_key key
;
369 struct trace_chunk_registry_ht_element
*element
= NULL
;
370 char uuid_str
[UUID_STR_LEN
];
371 char chunk_id_str
[MAX_INT_DEC_LEN(typeof(chunk_id
))] = "-1";
372 struct lttng_trace_chunk
*published_chunk
= NULL
;
374 lttng_uuid_to_str(sessiond_uuid
, uuid_str
);
375 lttng_uuid_copy(key
.sessiond_uuid
, sessiond_uuid
);
377 status
= lttng_trace_chunk_get_id(new_chunk
, &chunk_id
);
378 if (status
== LTTNG_TRACE_CHUNK_STATUS_OK
) {
381 ret
= snprintf(chunk_id_str
, sizeof(chunk_id_str
), "%" PRIu64
,
384 lttng_strncpy(chunk_id_str
, "-1", sizeof(chunk_id_str
));
385 WARN("Failed to format trace chunk id");
387 is_anonymous_chunk
= false;
388 } else if (status
== LTTNG_TRACE_CHUNK_STATUS_NONE
) {
389 is_anonymous_chunk
= true;
391 ERR("Failed to get trace chunk id");
395 DBG("Attempting to publish trace chunk: sessiond {%s}, session_id = "
396 "%" PRIu64
", chunk_id = %s",
397 uuid_str
, session_id
,
398 is_anonymous_chunk
? "anonymous" : chunk_id_str
);
400 element
= trace_chunk_registry_ht_element_find(sessiond_registry
, &key
);
402 ERR("Failed to find registry of sessiond {%s}", uuid_str
);
406 published_chunk
= lttng_trace_chunk_registry_publish_chunk(
407 element
->trace_chunk_registry
, session_id
, new_chunk
);
409 * At this point, two references to the published chunks exist. One
410 * is taken by the registry while the other is being returned to the
411 * caller. In the use case of the relay daemon, the reference held
412 * by the registry itself is undesirable.
414 * We want the trace chunk to be removed from the registry as soon
415 * as it is not being used by the relay daemon (through a session
416 * or a stream). This differs from the behaviour of the consumer
417 * daemon which relies on an explicit command from the session
418 * daemon to release the registry's reference.
420 lttng_trace_chunk_put(published_chunk
);
422 trace_chunk_registry_ht_element_put(element
);
423 return published_chunk
;
426 struct lttng_trace_chunk
*
427 sessiond_trace_chunk_registry_get_anonymous_chunk(
428 struct sessiond_trace_chunk_registry
*sessiond_registry
,
429 const lttng_uuid sessiond_uuid
,
432 struct lttng_trace_chunk
*chunk
= NULL
;
433 struct trace_chunk_registry_ht_element
*element
;
434 struct trace_chunk_registry_ht_key key
;
435 char uuid_str
[UUID_STR_LEN
];
437 lttng_uuid_to_str(sessiond_uuid
, uuid_str
);
439 lttng_uuid_copy(key
.sessiond_uuid
, sessiond_uuid
);
440 element
= trace_chunk_registry_ht_element_find(sessiond_registry
, &key
);
442 ERR("Failed to find trace chunk registry of sessiond {%s}",
447 chunk
= lttng_trace_chunk_registry_find_anonymous_chunk(
448 element
->trace_chunk_registry
,
450 trace_chunk_registry_ht_element_put(element
);
455 struct lttng_trace_chunk
*
456 sessiond_trace_chunk_registry_get_chunk(
457 struct sessiond_trace_chunk_registry
*sessiond_registry
,
458 const lttng_uuid sessiond_uuid
,
459 uint64_t session_id
, uint64_t chunk_id
)
461 struct lttng_trace_chunk
*chunk
= NULL
;
462 struct trace_chunk_registry_ht_element
*element
;
463 struct trace_chunk_registry_ht_key key
;
464 char uuid_str
[UUID_STR_LEN
];
466 lttng_uuid_to_str(sessiond_uuid
, uuid_str
);
468 lttng_uuid_copy(key
.sessiond_uuid
, sessiond_uuid
);
469 element
= trace_chunk_registry_ht_element_find(sessiond_registry
, &key
);
471 ERR("Failed to find trace chunk registry of sessiond {%s}",
476 chunk
= lttng_trace_chunk_registry_find_chunk(
477 element
->trace_chunk_registry
,
478 session_id
, chunk_id
);
479 trace_chunk_registry_ht_element_put(element
);
484 int sessiond_trace_chunk_registry_chunk_exists(
485 struct sessiond_trace_chunk_registry
*sessiond_registry
,
486 const lttng_uuid sessiond_uuid
,
487 uint64_t session_id
, uint64_t chunk_id
, bool *chunk_exists
)
490 struct trace_chunk_registry_ht_element
*element
;
491 struct trace_chunk_registry_ht_key key
;
493 lttng_uuid_copy(key
.sessiond_uuid
, sessiond_uuid
);
494 element
= trace_chunk_registry_ht_element_find(sessiond_registry
, &key
);
496 char uuid_str
[UUID_STR_LEN
];
498 lttng_uuid_to_str(sessiond_uuid
, uuid_str
);
500 * While this certainly means that the chunk does not exist,
501 * it is unexpected for a chunk existence query to target a
502 * session daemon that does not have an active
503 * connection/registry. This would indicate a protocol
504 * (or internal) error.
506 ERR("Failed to find trace chunk registry of sessiond {%s}",
512 ret
= lttng_trace_chunk_registry_chunk_exists(
513 element
->trace_chunk_registry
,
514 session_id
, chunk_id
, chunk_exists
);
515 trace_chunk_registry_ht_element_put(element
);