Replace explicit rcu_read_lock/unlock with lttng::urcu::read_lock_guard
[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 #include <common/urcu.hpp>
18
19 #include <inttypes.h>
20 #include <stdio.h>
21 #include <urcu.h>
22 #include <urcu/rculfhash.h>
23 #include <urcu/ref.h>
24
25 /*
26 * Lifetime of trace chunks within the relay daemon.
27 *
28 * Trace chunks are shared accross connections initiated from a given
29 * session daemon. When a session is created by a consumer daemon, the
30 * UUID of its associated session daemon is transmitted (in the case of
31 * 2.11+ consumer daemons).
32 *
33 * The sessiond_trace_chunk_registry_new_session() and
34 * sessiond_trace_chunk_registry_session_closed() methods create and
35 * manage the reference count of lttng_trace_chunk_registry objects
36 * associated to the various sessiond instances served by the relay daemon.
37 *
38 * When all sessions associated with a given sessiond instance are
39 * destroyed, its registry is destroyed.
40 *
41 * lttng_trace_chunk objects are uniquely identified by the
42 * (sessiond_uuid, sessiond_session_id, chunk_id) tuple. If a trace chunk
43 * matching that tuple already exists, a new reference to the trace chunk
44 * is acquired and it is returned to the caller. Otherwise, a new trace
45 * chunk is created. This is how trace chunks are de-duplicated across
46 * multiple consumer daemons managed by the same session daemon.
47 *
48 * Note that trace chunks are always added to their matching
49 * lttng_trace_chunk_registry. They are automatically removed from the
50 * trace chunk registry when their reference count reaches zero.
51 */
52
53 /*
54 * It is assumed that the sessiond_trace_chunk_registry is created and
55 * destroyed by the same thread.
56 */
57 struct sessiond_trace_chunk_registry {
58 /* Maps an lttng_uuid to an lttng_trace_chunk_registry. */
59 struct cds_lfht *ht;
60 };
61
62 namespace {
63 struct trace_chunk_registry_ht_key {
64 lttng_uuid sessiond_uuid;
65 };
66
67 struct trace_chunk_registry_ht_element {
68 struct trace_chunk_registry_ht_key key;
69 struct urcu_ref ref;
70 /* Node into the sessiond_trace_chunk_registry's hash table. */
71 struct cds_lfht_node ht_node;
72 /* Used for defered call_rcu reclaim. */
73 struct rcu_head rcu_node;
74 struct lttng_trace_chunk_registry *trace_chunk_registry;
75 struct sessiond_trace_chunk_registry *sessiond_trace_chunk_registry;
76 };
77 } /* namespace */
78
79 static unsigned long trace_chunk_registry_ht_key_hash(const struct trace_chunk_registry_ht_key *key)
80 {
81 const uint64_t uuid_h1 = *reinterpret_cast<const uint64_t *>(&key->sessiond_uuid[0]);
82 const uint64_t uuid_h2 = *reinterpret_cast<const uint64_t *>(&key->sessiond_uuid[1]);
83
84 return hash_key_u64(&uuid_h1, lttng_ht_seed) ^ hash_key_u64(&uuid_h2, lttng_ht_seed);
85 }
86
87 /* cds_lfht match function */
88 static int trace_chunk_registry_ht_key_match(struct cds_lfht_node *node, const void *_key)
89 {
90 const struct trace_chunk_registry_ht_key *key = (struct trace_chunk_registry_ht_key *) _key;
91 struct trace_chunk_registry_ht_element *registry;
92
93 registry = lttng::utils::container_of(node, &trace_chunk_registry_ht_element::ht_node);
94 return key->sessiond_uuid == registry->key.sessiond_uuid;
95 }
96
97 static void trace_chunk_registry_ht_element_free(struct rcu_head *node)
98 {
99 struct trace_chunk_registry_ht_element *element =
100 lttng::utils::container_of(node, &trace_chunk_registry_ht_element::rcu_node);
101
102 free(element);
103 }
104
105 static void trace_chunk_registry_ht_element_release(struct urcu_ref *ref)
106 {
107 struct trace_chunk_registry_ht_element *element =
108 lttng::utils::container_of(ref, &trace_chunk_registry_ht_element::ref);
109 char uuid_str[LTTNG_UUID_STR_LEN];
110
111 lttng_uuid_to_str(element->key.sessiond_uuid, uuid_str);
112
113 DBG("Destroying trace chunk registry associated to sessiond {%s}", uuid_str);
114 if (element->sessiond_trace_chunk_registry) {
115 /* Unpublish. */
116 lttng::urcu::read_lock_guard read_lock;
117 cds_lfht_del(element->sessiond_trace_chunk_registry->ht, &element->ht_node);
118 element->sessiond_trace_chunk_registry = nullptr;
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 = nullptr;
146 struct cds_lfht_node *node;
147 struct cds_lfht_iter iter;
148
149 lttng::urcu::read_lock_guard 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 = nullptr;
165 }
166 }
167 return element;
168 }
169
170 static int
171 trace_chunk_registry_ht_element_create(struct sessiond_trace_chunk_registry *sessiond_registry,
172 const struct trace_chunk_registry_ht_key *key)
173 {
174 int ret = 0;
175 struct trace_chunk_registry_ht_element *new_element;
176 struct lttng_trace_chunk_registry *trace_chunk_registry;
177 char uuid_str[LTTNG_UUID_STR_LEN];
178
179 lttng_uuid_to_str(key->sessiond_uuid, uuid_str);
180
181 trace_chunk_registry = lttng_trace_chunk_registry_create();
182 if (!trace_chunk_registry) {
183 ret = -1;
184 goto end;
185 }
186
187 new_element = zmalloc<trace_chunk_registry_ht_element>();
188 if (!new_element) {
189 ret = -1;
190 goto end;
191 }
192
193 memcpy(&new_element->key, key, sizeof(new_element->key));
194 urcu_ref_init(&new_element->ref);
195 cds_lfht_node_init(&new_element->ht_node);
196 new_element->trace_chunk_registry = trace_chunk_registry;
197 trace_chunk_registry = nullptr;
198
199 /* Attempt to publish the new element. */
200 {
201 /*
202 * Keep the rcu read lock is held accross all attempts
203 * purely for efficiency reasons.
204 */
205 lttng::urcu::read_lock_guard read_lock;
206 while (true) {
207 struct cds_lfht_node *published_node;
208 struct trace_chunk_registry_ht_element *published_element;
209
210 published_node = cds_lfht_add_unique(
211 sessiond_registry->ht,
212 trace_chunk_registry_ht_key_hash(&new_element->key),
213 trace_chunk_registry_ht_key_match,
214 &new_element->key,
215 &new_element->ht_node);
216 if (published_node == &new_element->ht_node) {
217 /* New element published successfully. */
218 DBG("Created trace chunk registry for sessiond {%s}", uuid_str);
219 new_element->sessiond_trace_chunk_registry = sessiond_registry;
220 break;
221 }
222
223 /*
224 * An equivalent element was published during the creation of
225 * this element. Attempt to acquire a reference to the one that
226 * was already published and release the reference to the copy
227 * we created if successful.
228 */
229 published_element = lttng::utils::container_of(
230 published_node, &trace_chunk_registry_ht_element::ht_node);
231 if (trace_chunk_registry_ht_element_get(published_element)) {
232 DBG("Acquired reference to trace chunk registry of sessiond {%s}",
233 uuid_str);
234 trace_chunk_registry_ht_element_put(new_element);
235 new_element = nullptr;
236 break;
237 }
238 /*
239 * A reference to the previously published element could not
240 * be acquired. Hence, retry to publish our copy of the
241 * element.
242 */
243 }
244 }
245 end:
246 if (ret < 0) {
247 ERR("Failed to create trace chunk registry for session daemon {%s}", uuid_str);
248 }
249 lttng_trace_chunk_registry_destroy(trace_chunk_registry);
250 return ret;
251 }
252
253 struct sessiond_trace_chunk_registry *sessiond_trace_chunk_registry_create()
254 {
255 struct sessiond_trace_chunk_registry *sessiond_registry =
256 zmalloc<sessiond_trace_chunk_registry>();
257
258 if (!sessiond_registry) {
259 goto end;
260 }
261
262 sessiond_registry->ht = cds_lfht_new(
263 DEFAULT_HT_SIZE, 1, 0, CDS_LFHT_AUTO_RESIZE | CDS_LFHT_ACCOUNTING, nullptr);
264 if (!sessiond_registry->ht) {
265 goto error;
266 }
267
268 end:
269 return sessiond_registry;
270 error:
271 sessiond_trace_chunk_registry_destroy(sessiond_registry);
272 return nullptr;
273 }
274
275 void sessiond_trace_chunk_registry_destroy(struct sessiond_trace_chunk_registry *sessiond_registry)
276 {
277 int ret = cds_lfht_destroy(sessiond_registry->ht, nullptr);
278
279 LTTNG_ASSERT(!ret);
280 free(sessiond_registry);
281 }
282
283 int sessiond_trace_chunk_registry_session_created(
284 struct sessiond_trace_chunk_registry *sessiond_registry, const lttng_uuid& sessiond_uuid)
285 {
286 int ret = 0;
287 struct trace_chunk_registry_ht_key key;
288 struct trace_chunk_registry_ht_element *element;
289
290 key.sessiond_uuid = sessiond_uuid;
291
292 element = trace_chunk_registry_ht_element_find(sessiond_registry, &key);
293 if (element) {
294 char uuid_str[LTTNG_UUID_STR_LEN];
295
296 lttng_uuid_to_str(sessiond_uuid, uuid_str);
297 DBG("Acquired reference to trace chunk registry of sessiond {%s}", uuid_str);
298 goto end;
299 } else {
300 ret = trace_chunk_registry_ht_element_create(sessiond_registry, &key);
301 }
302 end:
303 return ret;
304 }
305
306 int sessiond_trace_chunk_registry_session_destroyed(
307 struct sessiond_trace_chunk_registry *sessiond_registry, const lttng_uuid& sessiond_uuid)
308 {
309 int ret = 0;
310 struct trace_chunk_registry_ht_key key;
311 struct trace_chunk_registry_ht_element *element;
312 char uuid_str[LTTNG_UUID_STR_LEN];
313
314 lttng_uuid_to_str(sessiond_uuid, uuid_str);
315 key.sessiond_uuid = sessiond_uuid;
316
317 element = trace_chunk_registry_ht_element_find(sessiond_registry, &key);
318 if (element) {
319 DBG("Releasing reference to trace chunk registry of sessiond {%s}", uuid_str);
320 /*
321 * Release the reference held by the session and the reference
322 * acquired through the "find" operation.
323 */
324 trace_chunk_registry_ht_element_put(element);
325 trace_chunk_registry_ht_element_put(element);
326 } else {
327 ERR("Failed to find trace chunk registry of sessiond {%s}", uuid_str);
328 ret = -1;
329 }
330 return ret;
331 }
332
333 struct lttng_trace_chunk *
334 sessiond_trace_chunk_registry_publish_chunk(struct sessiond_trace_chunk_registry *sessiond_registry,
335 const lttng_uuid& sessiond_uuid,
336 uint64_t session_id,
337 struct lttng_trace_chunk *new_chunk)
338 {
339 enum lttng_trace_chunk_status status;
340 uint64_t chunk_id;
341 bool is_anonymous_chunk;
342 struct trace_chunk_registry_ht_key key;
343 struct trace_chunk_registry_ht_element *element = nullptr;
344 char uuid_str[LTTNG_UUID_STR_LEN];
345 char chunk_id_str[MAX_INT_DEC_LEN(typeof(chunk_id))] = "-1";
346 struct lttng_trace_chunk *published_chunk = nullptr;
347 bool trace_chunk_already_published;
348
349 lttng_uuid_to_str(sessiond_uuid, uuid_str);
350 key.sessiond_uuid = sessiond_uuid;
351
352 status = lttng_trace_chunk_get_id(new_chunk, &chunk_id);
353 if (status == LTTNG_TRACE_CHUNK_STATUS_OK) {
354 int ret;
355
356 ret = snprintf(chunk_id_str, sizeof(chunk_id_str), "%" PRIu64, chunk_id);
357 if (ret < 0) {
358 lttng_strncpy(chunk_id_str, "-1", sizeof(chunk_id_str));
359 WARN("Failed to format trace chunk id");
360 }
361 is_anonymous_chunk = false;
362 } else if (status == LTTNG_TRACE_CHUNK_STATUS_NONE) {
363 is_anonymous_chunk = true;
364 } else {
365 ERR("Failed to get trace chunk id");
366 goto end;
367 }
368
369 DBG("Attempting to publish trace chunk: sessiond {%s}, session_id = "
370 "%" PRIu64 ", chunk_id = %s",
371 uuid_str,
372 session_id,
373 is_anonymous_chunk ? "anonymous" : chunk_id_str);
374
375 element = trace_chunk_registry_ht_element_find(sessiond_registry, &key);
376 if (!element) {
377 ERR("Failed to find registry of sessiond {%s}", uuid_str);
378 goto end;
379 }
380
381 published_chunk = lttng_trace_chunk_registry_publish_chunk(element->trace_chunk_registry,
382 session_id,
383 new_chunk,
384 &trace_chunk_already_published);
385 /*
386 * When the trace chunk is first published, two references to the
387 * published chunks exist. One is taken by the registry while the other
388 * is being returned to the caller. In the use case of the relay daemon,
389 * the reference held by the registry itself is undesirable.
390 *
391 * We want the trace chunk to be removed from the registry as soon
392 * as it is not being used by the relay daemon (through a session
393 * or a stream). This differs from the behaviour of the consumer
394 * daemon which relies on an explicit command from the session
395 * daemon to release the registry's reference.
396 *
397 * In cases where the trace chunk had already been published,
398 * the reference belonging to the sessiond trace chunk
399 * registry instance has already been 'put'. We simply return
400 * the published trace chunk with a reference taken on behalf of the
401 * caller.
402 */
403 if (!trace_chunk_already_published) {
404 lttng_trace_chunk_put(published_chunk);
405 }
406 end:
407 trace_chunk_registry_ht_element_put(element);
408 return published_chunk;
409 }
410
411 struct lttng_trace_chunk *sessiond_trace_chunk_registry_get_anonymous_chunk(
412 struct sessiond_trace_chunk_registry *sessiond_registry,
413 const lttng_uuid& sessiond_uuid,
414 uint64_t session_id)
415 {
416 struct lttng_trace_chunk *chunk = nullptr;
417 struct trace_chunk_registry_ht_element *element;
418 struct trace_chunk_registry_ht_key key;
419 char uuid_str[LTTNG_UUID_STR_LEN];
420
421 lttng_uuid_to_str(sessiond_uuid, uuid_str);
422
423 key.sessiond_uuid = sessiond_uuid;
424 element = trace_chunk_registry_ht_element_find(sessiond_registry, &key);
425 if (!element) {
426 ERR("Failed to find trace chunk registry of sessiond {%s}", uuid_str);
427 goto end;
428 }
429
430 chunk = lttng_trace_chunk_registry_find_anonymous_chunk(element->trace_chunk_registry,
431 session_id);
432 trace_chunk_registry_ht_element_put(element);
433 end:
434 return chunk;
435 }
436
437 struct lttng_trace_chunk *
438 sessiond_trace_chunk_registry_get_chunk(struct sessiond_trace_chunk_registry *sessiond_registry,
439 const lttng_uuid& sessiond_uuid,
440 uint64_t session_id,
441 uint64_t chunk_id)
442 {
443 struct lttng_trace_chunk *chunk = nullptr;
444 struct trace_chunk_registry_ht_element *element;
445 struct trace_chunk_registry_ht_key key;
446 char uuid_str[LTTNG_UUID_STR_LEN];
447
448 lttng_uuid_to_str(sessiond_uuid, uuid_str);
449
450 key.sessiond_uuid = sessiond_uuid;
451 element = trace_chunk_registry_ht_element_find(sessiond_registry, &key);
452 if (!element) {
453 ERR("Failed to find trace chunk registry of sessiond {%s}", uuid_str);
454 goto end;
455 }
456
457 chunk = lttng_trace_chunk_registry_find_chunk(
458 element->trace_chunk_registry, session_id, chunk_id);
459 trace_chunk_registry_ht_element_put(element);
460 end:
461 return chunk;
462 }
463
464 int sessiond_trace_chunk_registry_chunk_exists(
465 struct sessiond_trace_chunk_registry *sessiond_registry,
466 const lttng_uuid& sessiond_uuid,
467 uint64_t session_id,
468 uint64_t chunk_id,
469 bool *chunk_exists)
470 {
471 int ret;
472 struct trace_chunk_registry_ht_element *element;
473 struct trace_chunk_registry_ht_key key;
474
475 key.sessiond_uuid = sessiond_uuid;
476 element = trace_chunk_registry_ht_element_find(sessiond_registry, &key);
477 if (!element) {
478 char uuid_str[LTTNG_UUID_STR_LEN];
479
480 lttng_uuid_to_str(sessiond_uuid, uuid_str);
481 /*
482 * While this certainly means that the chunk does not exist,
483 * it is unexpected for a chunk existence query to target a
484 * session daemon that does not have an active
485 * connection/registry. This would indicate a protocol
486 * (or internal) error.
487 */
488 ERR("Failed to find trace chunk registry of sessiond {%s}", uuid_str);
489 ret = -1;
490 goto end;
491 }
492
493 ret = lttng_trace_chunk_registry_chunk_exists(
494 element->trace_chunk_registry, session_id, chunk_id, chunk_exists);
495 trace_chunk_registry_ht_element_put(element);
496 end:
497 return ret;
498 }
This page took 0.045674 seconds and 5 git commands to generate.