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
)
154 urcu_ref_put(&element
->ref
, trace_chunk_registry_ht_element_release
);
157 /* Acquires a reference to the returned element on behalf of the caller. */
159 struct trace_chunk_registry_ht_element
*trace_chunk_registry_ht_element_find(
160 struct sessiond_trace_chunk_registry
*sessiond_registry
,
161 const struct trace_chunk_registry_ht_key
*key
)
163 struct trace_chunk_registry_ht_element
*element
= NULL
;
164 struct cds_lfht_node
*node
;
165 struct cds_lfht_iter iter
;
168 cds_lfht_lookup(sessiond_registry
->ht
,
169 trace_chunk_registry_ht_key_hash(key
),
170 trace_chunk_registry_ht_key_match
,
173 node
= cds_lfht_iter_get_node(&iter
);
175 element
= container_of(node
, typeof(*element
), ht_node
);
177 * Only consider the look-up as successful if a reference
180 if (!trace_chunk_registry_ht_element_get(element
)) {
189 int trace_chunk_registry_ht_element_create(
190 struct sessiond_trace_chunk_registry
*sessiond_registry
,
191 const struct trace_chunk_registry_ht_key
*key
)
194 struct trace_chunk_registry_ht_element
*new_element
;
195 struct lttng_trace_chunk_registry
*trace_chunk_registry
;
196 char uuid_str
[UUID_STR_LEN
];
198 lttng_uuid_to_str(key
->sessiond_uuid
, uuid_str
);
200 trace_chunk_registry
= lttng_trace_chunk_registry_create();
201 if (!trace_chunk_registry
) {
206 new_element
= zmalloc(sizeof(*new_element
));
212 memcpy(&new_element
->key
, key
, sizeof(new_element
->key
));
213 urcu_ref_init(&new_element
->ref
);
214 cds_lfht_node_init(&new_element
->ht_node
);
215 new_element
->trace_chunk_registry
= trace_chunk_registry
;
217 /* Attempt to publish the new element. */
220 struct cds_lfht_node
*published_node
;
221 struct trace_chunk_registry_ht_element
*published_element
;
223 published_node
= cds_lfht_add_unique(sessiond_registry
->ht
,
224 trace_chunk_registry_ht_key_hash(&new_element
->key
),
225 trace_chunk_registry_ht_key_match
,
227 &new_element
->ht_node
);
228 if (published_node
== &new_element
->ht_node
) {
229 /* New element published successfully. */
230 DBG("Created trace chunk registry for sessiond {%s}",
232 new_element
->sessiond_trace_chunk_registry
=
238 * An equivalent element was published during the creation of
239 * this element. Attempt to acquire a reference to the one that
240 * was already published and release the reference to the copy
241 * we created if successful.
243 published_element
= container_of(published_node
,
244 typeof(*published_element
), ht_node
);
245 if (trace_chunk_registry_ht_element_get(published_element
)) {
246 DBG("Acquired reference to trace chunk registry of sessiond {%s}",
248 trace_chunk_registry_ht_element_put(new_element
);
253 * A reference to the previously published element could not
254 * be acquired. Hence, retry to publish our copy of the
261 ERR("Failed to create trace chunk registry for session daemon {%s}",
267 struct sessiond_trace_chunk_registry
*sessiond_trace_chunk_registry_create(void)
269 struct sessiond_trace_chunk_registry
*sessiond_registry
=
270 zmalloc(sizeof(*sessiond_registry
));
272 if (!sessiond_registry
) {
276 sessiond_registry
->ht
= cds_lfht_new(DEFAULT_HT_SIZE
,
277 1, 0, CDS_LFHT_AUTO_RESIZE
| CDS_LFHT_ACCOUNTING
, NULL
);
278 if (!sessiond_registry
->ht
) {
283 return sessiond_registry
;
285 sessiond_trace_chunk_registry_destroy(sessiond_registry
);
289 void sessiond_trace_chunk_registry_destroy(
290 struct sessiond_trace_chunk_registry
*sessiond_registry
)
292 int ret
= cds_lfht_destroy(sessiond_registry
->ht
, NULL
);
295 free(sessiond_registry
);
298 int sessiond_trace_chunk_registry_session_created(
299 struct sessiond_trace_chunk_registry
*sessiond_registry
,
300 const lttng_uuid sessiond_uuid
)
303 struct trace_chunk_registry_ht_key key
;
304 struct trace_chunk_registry_ht_element
*element
;
306 lttng_uuid_copy(key
.sessiond_uuid
, sessiond_uuid
);
308 element
= trace_chunk_registry_ht_element_find(sessiond_registry
, &key
);
310 char uuid_str
[UUID_STR_LEN
];
312 lttng_uuid_to_str(sessiond_uuid
, uuid_str
);
313 DBG("Acquired reference to trace chunk registry of sessiond {%s}",
317 ret
= trace_chunk_registry_ht_element_create(
318 sessiond_registry
, &key
);
324 int sessiond_trace_chunk_registry_session_destroyed(
325 struct sessiond_trace_chunk_registry
*sessiond_registry
,
326 const lttng_uuid sessiond_uuid
)
329 struct trace_chunk_registry_ht_key key
;
330 struct trace_chunk_registry_ht_element
*element
;
331 char uuid_str
[UUID_STR_LEN
];
333 lttng_uuid_to_str(sessiond_uuid
, uuid_str
);
334 lttng_uuid_copy(key
.sessiond_uuid
, sessiond_uuid
);
336 element
= trace_chunk_registry_ht_element_find(sessiond_registry
, &key
);
338 DBG("Releasing reference to trace chunk registry of sessiond {%s}",
341 * Release the reference held by the session and the reference
342 * acquired through the "find" operation.
344 trace_chunk_registry_ht_element_put(element
);
345 trace_chunk_registry_ht_element_put(element
);
347 ERR("Failed to find trace chunk registry of sessiond {%s}",
354 struct lttng_trace_chunk
*sessiond_trace_chunk_registry_publish_chunk(
355 struct sessiond_trace_chunk_registry
*sessiond_registry
,
356 const lttng_uuid sessiond_uuid
, uint64_t session_id
,
357 struct lttng_trace_chunk
*new_chunk
)
359 enum lttng_trace_chunk_status status
;
361 bool is_anonymous_chunk
;
362 struct trace_chunk_registry_ht_key key
;
363 struct trace_chunk_registry_ht_element
*element
= NULL
;
364 char uuid_str
[UUID_STR_LEN
];
365 char chunk_id_str
[MAX_INT_DEC_LEN(typeof(chunk_id
))] = "-1";
366 struct lttng_trace_chunk
*published_chunk
= NULL
;
368 lttng_uuid_to_str(sessiond_uuid
, uuid_str
);
369 lttng_uuid_copy(key
.sessiond_uuid
, sessiond_uuid
);
371 status
= lttng_trace_chunk_get_id(new_chunk
, &chunk_id
);
372 if (status
== LTTNG_TRACE_CHUNK_STATUS_OK
) {
375 ret
= snprintf(chunk_id_str
, sizeof(chunk_id_str
), "%" PRIu64
,
378 lttng_strncpy(chunk_id_str
, "-1", sizeof(chunk_id_str
));
379 WARN("Failed to format trace chunk id");
381 is_anonymous_chunk
= false;
382 } else if (status
== LTTNG_TRACE_CHUNK_STATUS_NONE
) {
383 is_anonymous_chunk
= true;
385 ERR("Failed to get trace chunk id");
389 DBG("Attempting to publish trace chunk: sessiond {%s}, session_id = "
390 "%" PRIu64
", chunk_id = %s",
391 uuid_str
, session_id
,
392 is_anonymous_chunk
? "anonymous" : chunk_id_str
);
394 element
= trace_chunk_registry_ht_element_find(sessiond_registry
, &key
);
396 ERR("Failed to find registry of sessiond {%s}", uuid_str
);
400 published_chunk
= lttng_trace_chunk_registry_publish_chunk(
401 element
->trace_chunk_registry
, session_id
, new_chunk
);
403 * At this point, two references to the published chunks exist. One
404 * is taken by the registry while the other is being returned to the
405 * caller. In the use case of the relay daemon, the reference held
406 * 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 lttng_trace_chunk_put(published_chunk
);
416 trace_chunk_registry_ht_element_put(element
);
417 return published_chunk
;
420 struct lttng_trace_chunk
*
421 sessiond_trace_chunk_registry_get_anonymous_chunk(
422 struct sessiond_trace_chunk_registry
*sessiond_registry
,
423 const lttng_uuid sessiond_uuid
,
426 struct lttng_trace_chunk
*chunk
= NULL
;
427 struct trace_chunk_registry_ht_element
*element
;
428 struct trace_chunk_registry_ht_key key
;
429 char uuid_str
[UUID_STR_LEN
];
431 lttng_uuid_to_str(sessiond_uuid
, uuid_str
);
433 lttng_uuid_copy(key
.sessiond_uuid
, sessiond_uuid
);
434 element
= trace_chunk_registry_ht_element_find(sessiond_registry
, &key
);
436 ERR("Failed to find trace chunk registry of sessiond {%s}",
441 chunk
= lttng_trace_chunk_registry_find_anonymous_chunk(
442 element
->trace_chunk_registry
,
444 trace_chunk_registry_ht_element_put(element
);
449 struct lttng_trace_chunk
*
450 sessiond_trace_chunk_registry_get_chunk(
451 struct sessiond_trace_chunk_registry
*sessiond_registry
,
452 const lttng_uuid sessiond_uuid
,
453 uint64_t session_id
, uint64_t chunk_id
)
455 struct lttng_trace_chunk
*chunk
= NULL
;
456 struct trace_chunk_registry_ht_element
*element
;
457 struct trace_chunk_registry_ht_key key
;
458 char uuid_str
[UUID_STR_LEN
];
460 lttng_uuid_to_str(sessiond_uuid
, uuid_str
);
462 lttng_uuid_copy(key
.sessiond_uuid
, sessiond_uuid
);
463 element
= trace_chunk_registry_ht_element_find(sessiond_registry
, &key
);
465 ERR("Failed to find trace chunk registry of sessiond {%s}",
470 chunk
= lttng_trace_chunk_registry_find_chunk(
471 element
->trace_chunk_registry
,
472 session_id
, chunk_id
);
473 trace_chunk_registry_ht_element_put(element
);