7201daed1ef74bb27c9c3d7b51464c659dc2e217
[lttng-tools.git] / src / bin / lttng-relayd / sessiond-trace-chunks.cpp
1 /*
2 * Copyright (C) 2019 Jérémie Galarneau <jeremie.galarneau@efficios.com>
3 *
4 * SPDX-License-Identifier: GPL-2.0-only
5 *
6 */
7
8 #include "sessiond-trace-chunks.hpp"
9
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
18 #include <inttypes.h>
19 #include <stdio.h>
20 #include <urcu.h>
21 #include <urcu/rculfhash.h>
22 #include <urcu/ref.h>
23
24 /*
25 * Lifetime of trace chunks within the relay daemon.
26 *
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).
31 *
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.
36 *
37 * When all sessions associated with a given sessiond instance are
38 * destroyed, its registry is destroyed.
39 *
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.
46 *
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.
50 */
51
52 /*
53 * It is assumed that the sessiond_trace_chunk_registry is created and
54 * destroyed by the same thread.
55 */
56 struct sessiond_trace_chunk_registry {
57 /* Maps an lttng_uuid to an lttng_trace_chunk_registry. */
58 struct cds_lfht *ht;
59 };
60
61 namespace {
62 struct trace_chunk_registry_ht_key {
63 lttng_uuid sessiond_uuid;
64 };
65
66 struct trace_chunk_registry_ht_element {
67 struct trace_chunk_registry_ht_key key;
68 struct urcu_ref ref;
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;
75 };
76 } /* namespace */
77
78 static unsigned long trace_chunk_registry_ht_key_hash(const struct trace_chunk_registry_ht_key *key)
79 {
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]);
82
83 return hash_key_u64(&uuid_h1, lttng_ht_seed) ^ hash_key_u64(&uuid_h2, lttng_ht_seed);
84 }
85
86 /* cds_lfht match function */
87 static int trace_chunk_registry_ht_key_match(struct cds_lfht_node *node, const void *_key)
88 {
89 const struct trace_chunk_registry_ht_key *key = (struct trace_chunk_registry_ht_key *) _key;
90 struct trace_chunk_registry_ht_element *registry;
91
92 registry = lttng::utils::container_of(node, &trace_chunk_registry_ht_element::ht_node);
93 return key->sessiond_uuid == registry->key.sessiond_uuid;
94 }
95
96 static void trace_chunk_registry_ht_element_free(struct rcu_head *node)
97 {
98 struct trace_chunk_registry_ht_element *element =
99 lttng::utils::container_of(node, &trace_chunk_registry_ht_element::rcu_node);
100
101 free(element);
102 }
103
104 static void trace_chunk_registry_ht_element_release(struct urcu_ref *ref)
105 {
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];
109
110 lttng_uuid_to_str(element->key.sessiond_uuid, uuid_str);
111
112 DBG("Destroying trace chunk registry associated to sessiond {%s}", uuid_str);
113 if (element->sessiond_trace_chunk_registry) {
114 /* Unpublish. */
115 rcu_read_lock();
116 cds_lfht_del(element->sessiond_trace_chunk_registry->ht, &element->ht_node);
117 rcu_read_unlock();
118 element->sessiond_trace_chunk_registry = NULL;
119 }
120
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);
124 }
125
126 static bool trace_chunk_registry_ht_element_get(struct trace_chunk_registry_ht_element *element)
127 {
128 return urcu_ref_get_unless_zero(&element->ref);
129 }
130
131 static void trace_chunk_registry_ht_element_put(struct trace_chunk_registry_ht_element *element)
132 {
133 if (!element) {
134 return;
135 }
136
137 urcu_ref_put(&element->ref, trace_chunk_registry_ht_element_release);
138 }
139
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)
144 {
145 struct trace_chunk_registry_ht_element *element = NULL;
146 struct cds_lfht_node *node;
147 struct cds_lfht_iter iter;
148
149 rcu_read_lock();
150 cds_lfht_lookup(sessiond_registry->ht,
151 trace_chunk_registry_ht_key_hash(key),
152 trace_chunk_registry_ht_key_match,
153 key,
154 &iter);
155 node = cds_lfht_iter_get_node(&iter);
156 if (node) {
157 element =
158 lttng::utils::container_of(node, &trace_chunk_registry_ht_element::ht_node);
159 /*
160 * Only consider the look-up as successful if a reference
161 * could be acquired.
162 */
163 if (!trace_chunk_registry_ht_element_get(element)) {
164 element = NULL;
165 }
166 }
167 rcu_read_unlock();
168 return element;
169 }
170
171 static int
172 trace_chunk_registry_ht_element_create(struct sessiond_trace_chunk_registry *sessiond_registry,
173 const struct trace_chunk_registry_ht_key *key)
174 {
175 int ret = 0;
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];
179
180 lttng_uuid_to_str(key->sessiond_uuid, uuid_str);
181
182 trace_chunk_registry = lttng_trace_chunk_registry_create();
183 if (!trace_chunk_registry) {
184 ret = -1;
185 goto end;
186 }
187
188 new_element = zmalloc<trace_chunk_registry_ht_element>();
189 if (!new_element) {
190 ret = -1;
191 goto end;
192 }
193
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;
199
200 /* Attempt to publish the new element. */
201 rcu_read_lock();
202 while (1) {
203 struct cds_lfht_node *published_node;
204 struct trace_chunk_registry_ht_element *published_element;
205
206 published_node =
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,
210 &new_element->key,
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;
216 break;
217 }
218
219 /*
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.
224 */
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}",
229 uuid_str);
230 trace_chunk_registry_ht_element_put(new_element);
231 new_element = NULL;
232 break;
233 }
234 /*
235 * A reference to the previously published element could not
236 * be acquired. Hence, retry to publish our copy of the
237 * element.
238 */
239 }
240 rcu_read_unlock();
241 end:
242 if (ret < 0) {
243 ERR("Failed to create trace chunk registry for session daemon {%s}", uuid_str);
244 }
245 lttng_trace_chunk_registry_destroy(trace_chunk_registry);
246 return ret;
247 }
248
249 struct sessiond_trace_chunk_registry *sessiond_trace_chunk_registry_create(void)
250 {
251 struct sessiond_trace_chunk_registry *sessiond_registry =
252 zmalloc<sessiond_trace_chunk_registry>();
253
254 if (!sessiond_registry) {
255 goto end;
256 }
257
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) {
261 goto error;
262 }
263
264 end:
265 return sessiond_registry;
266 error:
267 sessiond_trace_chunk_registry_destroy(sessiond_registry);
268 return NULL;
269 }
270
271 void sessiond_trace_chunk_registry_destroy(struct sessiond_trace_chunk_registry *sessiond_registry)
272 {
273 int ret = cds_lfht_destroy(sessiond_registry->ht, NULL);
274
275 LTTNG_ASSERT(!ret);
276 free(sessiond_registry);
277 }
278
279 int sessiond_trace_chunk_registry_session_created(
280 struct sessiond_trace_chunk_registry *sessiond_registry, const lttng_uuid& sessiond_uuid)
281 {
282 int ret = 0;
283 struct trace_chunk_registry_ht_key key;
284 struct trace_chunk_registry_ht_element *element;
285
286 key.sessiond_uuid = sessiond_uuid;
287
288 element = trace_chunk_registry_ht_element_find(sessiond_registry, &key);
289 if (element) {
290 char uuid_str[LTTNG_UUID_STR_LEN];
291
292 lttng_uuid_to_str(sessiond_uuid, uuid_str);
293 DBG("Acquired reference to trace chunk registry of sessiond {%s}", uuid_str);
294 goto end;
295 } else {
296 ret = trace_chunk_registry_ht_element_create(sessiond_registry, &key);
297 }
298 end:
299 return ret;
300 }
301
302 int sessiond_trace_chunk_registry_session_destroyed(
303 struct sessiond_trace_chunk_registry *sessiond_registry, const lttng_uuid& sessiond_uuid)
304 {
305 int ret = 0;
306 struct trace_chunk_registry_ht_key key;
307 struct trace_chunk_registry_ht_element *element;
308 char uuid_str[LTTNG_UUID_STR_LEN];
309
310 lttng_uuid_to_str(sessiond_uuid, uuid_str);
311 key.sessiond_uuid = sessiond_uuid;
312
313 element = trace_chunk_registry_ht_element_find(sessiond_registry, &key);
314 if (element) {
315 DBG("Releasing reference to trace chunk registry of sessiond {%s}", uuid_str);
316 /*
317 * Release the reference held by the session and the reference
318 * acquired through the "find" operation.
319 */
320 trace_chunk_registry_ht_element_put(element);
321 trace_chunk_registry_ht_element_put(element);
322 } else {
323 ERR("Failed to find trace chunk registry of sessiond {%s}", uuid_str);
324 ret = -1;
325 }
326 return ret;
327 }
328
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,
332 uint64_t session_id,
333 struct lttng_trace_chunk *new_chunk)
334 {
335 enum lttng_trace_chunk_status status;
336 uint64_t chunk_id;
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;
344
345 lttng_uuid_to_str(sessiond_uuid, uuid_str);
346 key.sessiond_uuid = sessiond_uuid;
347
348 status = lttng_trace_chunk_get_id(new_chunk, &chunk_id);
349 if (status == LTTNG_TRACE_CHUNK_STATUS_OK) {
350 int ret;
351
352 ret = snprintf(chunk_id_str, sizeof(chunk_id_str), "%" PRIu64, chunk_id);
353 if (ret < 0) {
354 lttng_strncpy(chunk_id_str, "-1", sizeof(chunk_id_str));
355 WARN("Failed to format trace chunk id");
356 }
357 is_anonymous_chunk = false;
358 } else if (status == LTTNG_TRACE_CHUNK_STATUS_NONE) {
359 is_anonymous_chunk = true;
360 } else {
361 ERR("Failed to get trace chunk id");
362 goto end;
363 }
364
365 DBG("Attempting to publish trace chunk: sessiond {%s}, session_id = "
366 "%" PRIu64 ", chunk_id = %s",
367 uuid_str,
368 session_id,
369 is_anonymous_chunk ? "anonymous" : chunk_id_str);
370
371 element = trace_chunk_registry_ht_element_find(sessiond_registry, &key);
372 if (!element) {
373 ERR("Failed to find registry of sessiond {%s}", uuid_str);
374 goto end;
375 }
376
377 published_chunk = lttng_trace_chunk_registry_publish_chunk(element->trace_chunk_registry,
378 session_id,
379 new_chunk,
380 &trace_chunk_already_published);
381 /*
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.
386 *
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.
392 *
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
397 * caller.
398 */
399 if (!trace_chunk_already_published) {
400 lttng_trace_chunk_put(published_chunk);
401 }
402 end:
403 trace_chunk_registry_ht_element_put(element);
404 return published_chunk;
405 }
406
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,
410 uint64_t session_id)
411 {
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];
416
417 lttng_uuid_to_str(sessiond_uuid, uuid_str);
418
419 key.sessiond_uuid = sessiond_uuid;
420 element = trace_chunk_registry_ht_element_find(sessiond_registry, &key);
421 if (!element) {
422 ERR("Failed to find trace chunk registry of sessiond {%s}", uuid_str);
423 goto end;
424 }
425
426 chunk = lttng_trace_chunk_registry_find_anonymous_chunk(element->trace_chunk_registry,
427 session_id);
428 trace_chunk_registry_ht_element_put(element);
429 end:
430 return chunk;
431 }
432
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,
436 uint64_t session_id,
437 uint64_t chunk_id)
438 {
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];
443
444 lttng_uuid_to_str(sessiond_uuid, uuid_str);
445
446 key.sessiond_uuid = sessiond_uuid;
447 element = trace_chunk_registry_ht_element_find(sessiond_registry, &key);
448 if (!element) {
449 ERR("Failed to find trace chunk registry of sessiond {%s}", uuid_str);
450 goto end;
451 }
452
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);
456 end:
457 return chunk;
458 }
459
460 int sessiond_trace_chunk_registry_chunk_exists(
461 struct sessiond_trace_chunk_registry *sessiond_registry,
462 const lttng_uuid& sessiond_uuid,
463 uint64_t session_id,
464 uint64_t chunk_id,
465 bool *chunk_exists)
466 {
467 int ret;
468 struct trace_chunk_registry_ht_element *element;
469 struct trace_chunk_registry_ht_key key;
470
471 key.sessiond_uuid = sessiond_uuid;
472 element = trace_chunk_registry_ht_element_find(sessiond_registry, &key);
473 if (!element) {
474 char uuid_str[LTTNG_UUID_STR_LEN];
475
476 lttng_uuid_to_str(sessiond_uuid, uuid_str);
477 /*
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.
483 */
484 ERR("Failed to find trace chunk registry of sessiond {%s}", uuid_str);
485 ret = -1;
486 goto end;
487 }
488
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);
492 end:
493 return ret;
494 }
This page took 0.038691 seconds and 4 git commands to generate.