Fix: destroy chunk registry on element creation failure
[lttng-tools.git] / src / bin / lttng-relayd / sessiond-trace-chunks.c
1 /*
2 * Copyright (C) 2019 - Jérémie Galarneau <jeremie.galarneau@efficios.com>
3 *
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.
7 *
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
11 * more details.
12 *
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.
16 */
17
18 #include "sessiond-trace-chunks.h"
19 #include <urcu.h>
20 #include <urcu/rculfhash.h>
21 #include <urcu/ref.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>
29 #include <stdio.h>
30 #include <inttypes.h>
31
32 /*
33 * Lifetime of trace chunks within the relay daemon.
34 *
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).
39 *
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.
44 *
45 * When all sessions associated with a given sessiond instance are
46 * destroyed, its registry is destroyed.
47 *
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.
54 *
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.
58 */
59
60 /*
61 * It is assumed that the sessiond_trace_chunk_registry is created and
62 * destroyed by the same thread.
63 */
64 struct sessiond_trace_chunk_registry {
65 /* Maps an lttng_uuid to an lttng_trace_chunk_registry. */
66 struct cds_lfht *ht;
67 };
68
69 struct trace_chunk_registry_ht_key {
70 lttng_uuid sessiond_uuid;
71 };
72
73 struct trace_chunk_registry_ht_element {
74 struct trace_chunk_registry_ht_key key;
75 struct urcu_ref ref;
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;
82 };
83
84 static
85 unsigned long trace_chunk_registry_ht_key_hash(
86 const struct trace_chunk_registry_ht_key *key)
87 {
88 uint64_t uuid_h1 = ((uint64_t *) key->sessiond_uuid)[0];
89 uint64_t uuid_h2 = ((uint64_t *) key->sessiond_uuid)[1];
90
91 return hash_key_u64(&uuid_h1, lttng_ht_seed) ^
92 hash_key_u64(&uuid_h2, lttng_ht_seed);
93 }
94
95 /* cds_lfht match function */
96 static
97 int trace_chunk_registry_ht_key_match(struct cds_lfht_node *node,
98 const void *_key)
99 {
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;
103
104 registry = container_of(node, typeof(*registry), ht_node);
105 return lttng_uuid_is_equal(key->sessiond_uuid,
106 registry->key.sessiond_uuid);
107 }
108
109 static
110 void trace_chunk_registry_ht_element_free(struct rcu_head *node)
111 {
112 struct trace_chunk_registry_ht_element *element =
113 container_of(node, typeof(*element), rcu_node);
114
115 free(element);
116 }
117
118 static
119 void trace_chunk_registry_ht_element_release(struct urcu_ref *ref)
120 {
121 struct trace_chunk_registry_ht_element *element =
122 container_of(ref, typeof(*element), ref);
123 char uuid_str[UUID_STR_LEN];
124
125 lttng_uuid_to_str(element->key.sessiond_uuid, uuid_str);
126
127 DBG("Destroying trace chunk registry associated to sessiond {%s}",
128 uuid_str);
129 if (element->sessiond_trace_chunk_registry) {
130 /* Unpublish. */
131 rcu_read_lock();
132 cds_lfht_del(element->sessiond_trace_chunk_registry->ht,
133 &element->ht_node);
134 rcu_read_unlock();
135 element->sessiond_trace_chunk_registry = NULL;
136 }
137
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);
141 }
142
143 static
144 bool trace_chunk_registry_ht_element_get(
145 struct trace_chunk_registry_ht_element *element)
146 {
147 return urcu_ref_get_unless_zero(&element->ref);
148 }
149
150 static
151 void trace_chunk_registry_ht_element_put(
152 struct trace_chunk_registry_ht_element *element)
153 {
154 urcu_ref_put(&element->ref, trace_chunk_registry_ht_element_release);
155 }
156
157 /* Acquires a reference to the returned element on behalf of the caller. */
158 static
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)
162 {
163 struct trace_chunk_registry_ht_element *element = NULL;
164 struct cds_lfht_node *node;
165 struct cds_lfht_iter iter;
166
167 rcu_read_lock();
168 cds_lfht_lookup(sessiond_registry->ht,
169 trace_chunk_registry_ht_key_hash(key),
170 trace_chunk_registry_ht_key_match,
171 key,
172 &iter);
173 node = cds_lfht_iter_get_node(&iter);
174 if (node) {
175 element = container_of(node, typeof(*element), ht_node);
176 /*
177 * Only consider the look-up as successful if a reference
178 * could be acquired.
179 */
180 if (!trace_chunk_registry_ht_element_get(element)) {
181 element = NULL;
182 }
183 }
184 rcu_read_unlock();
185 return element;
186 }
187
188 static
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)
192 {
193 int ret = 0;
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];
197
198 lttng_uuid_to_str(key->sessiond_uuid, uuid_str);
199
200 trace_chunk_registry = lttng_trace_chunk_registry_create();
201 if (!trace_chunk_registry) {
202 ret = -1;
203 goto end;
204 }
205
206 new_element = zmalloc(sizeof(*new_element));
207 if (!new_element) {
208 ret = -1;
209 goto end;
210 }
211
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;
216 trace_chunk_registry = NULL;
217
218 /* Attempt to publish the new element. */
219 rcu_read_lock();
220 while (1) {
221 struct cds_lfht_node *published_node;
222 struct trace_chunk_registry_ht_element *published_element;
223
224 published_node = cds_lfht_add_unique(sessiond_registry->ht,
225 trace_chunk_registry_ht_key_hash(&new_element->key),
226 trace_chunk_registry_ht_key_match,
227 &new_element->key,
228 &new_element->ht_node);
229 if (published_node == &new_element->ht_node) {
230 /* New element published successfully. */
231 DBG("Created trace chunk registry for sessiond {%s}",
232 uuid_str);
233 new_element->sessiond_trace_chunk_registry =
234 sessiond_registry;
235 break;
236 }
237
238 /*
239 * An equivalent element was published during the creation of
240 * this element. Attempt to acquire a reference to the one that
241 * was already published and release the reference to the copy
242 * we created if successful.
243 */
244 published_element = container_of(published_node,
245 typeof(*published_element), ht_node);
246 if (trace_chunk_registry_ht_element_get(published_element)) {
247 DBG("Acquired reference to trace chunk registry of sessiond {%s}",
248 uuid_str);
249 trace_chunk_registry_ht_element_put(new_element);
250 new_element = NULL;
251 break;
252 }
253 /*
254 * A reference to the previously published element could not
255 * be acquired. Hence, retry to publish our copy of the
256 * element.
257 */
258 }
259 rcu_read_unlock();
260 end:
261 if (ret < 0) {
262 ERR("Failed to create trace chunk registry for session daemon {%s}",
263 uuid_str);
264 }
265 lttng_trace_chunk_registry_destroy(trace_chunk_registry);
266 return ret;
267 }
268
269 struct sessiond_trace_chunk_registry *sessiond_trace_chunk_registry_create(void)
270 {
271 struct sessiond_trace_chunk_registry *sessiond_registry =
272 zmalloc(sizeof(*sessiond_registry));
273
274 if (!sessiond_registry) {
275 goto end;
276 }
277
278 sessiond_registry->ht = cds_lfht_new(DEFAULT_HT_SIZE,
279 1, 0, CDS_LFHT_AUTO_RESIZE | CDS_LFHT_ACCOUNTING, NULL);
280 if (!sessiond_registry->ht) {
281 goto error;
282 }
283
284 end:
285 return sessiond_registry;
286 error:
287 sessiond_trace_chunk_registry_destroy(sessiond_registry);
288 return NULL;
289 }
290
291 void sessiond_trace_chunk_registry_destroy(
292 struct sessiond_trace_chunk_registry *sessiond_registry)
293 {
294 int ret = cds_lfht_destroy(sessiond_registry->ht, NULL);
295
296 assert(!ret);
297 free(sessiond_registry);
298 }
299
300 int sessiond_trace_chunk_registry_session_created(
301 struct sessiond_trace_chunk_registry *sessiond_registry,
302 const lttng_uuid sessiond_uuid)
303 {
304 int ret = 0;
305 struct trace_chunk_registry_ht_key key;
306 struct trace_chunk_registry_ht_element *element;
307
308 lttng_uuid_copy(key.sessiond_uuid, sessiond_uuid);
309
310 element = trace_chunk_registry_ht_element_find(sessiond_registry, &key);
311 if (element) {
312 char uuid_str[UUID_STR_LEN];
313
314 lttng_uuid_to_str(sessiond_uuid, uuid_str);
315 DBG("Acquired reference to trace chunk registry of sessiond {%s}",
316 uuid_str);
317 goto end;
318 } else {
319 ret = trace_chunk_registry_ht_element_create(
320 sessiond_registry, &key);
321 }
322 end:
323 return ret;
324 }
325
326 int sessiond_trace_chunk_registry_session_destroyed(
327 struct sessiond_trace_chunk_registry *sessiond_registry,
328 const lttng_uuid sessiond_uuid)
329 {
330 int ret = 0;
331 struct trace_chunk_registry_ht_key key;
332 struct trace_chunk_registry_ht_element *element;
333 char uuid_str[UUID_STR_LEN];
334
335 lttng_uuid_to_str(sessiond_uuid, uuid_str);
336 lttng_uuid_copy(key.sessiond_uuid, sessiond_uuid);
337
338 element = trace_chunk_registry_ht_element_find(sessiond_registry, &key);
339 if (element) {
340 DBG("Releasing reference to trace chunk registry of sessiond {%s}",
341 uuid_str);
342 /*
343 * Release the reference held by the session and the reference
344 * acquired through the "find" operation.
345 */
346 trace_chunk_registry_ht_element_put(element);
347 trace_chunk_registry_ht_element_put(element);
348 } else {
349 ERR("Failed to find trace chunk registry of sessiond {%s}",
350 uuid_str);
351 ret = -1;
352 }
353 return ret;
354 }
355
356 struct lttng_trace_chunk *sessiond_trace_chunk_registry_publish_chunk(
357 struct sessiond_trace_chunk_registry *sessiond_registry,
358 const lttng_uuid sessiond_uuid, uint64_t session_id,
359 struct lttng_trace_chunk *new_chunk)
360 {
361 enum lttng_trace_chunk_status status;
362 uint64_t chunk_id;
363 bool is_anonymous_chunk;
364 struct trace_chunk_registry_ht_key key;
365 struct trace_chunk_registry_ht_element *element = NULL;
366 char uuid_str[UUID_STR_LEN];
367 char chunk_id_str[MAX_INT_DEC_LEN(typeof(chunk_id))] = "-1";
368 struct lttng_trace_chunk *published_chunk = NULL;
369
370 lttng_uuid_to_str(sessiond_uuid, uuid_str);
371 lttng_uuid_copy(key.sessiond_uuid, sessiond_uuid);
372
373 status = lttng_trace_chunk_get_id(new_chunk, &chunk_id);
374 if (status == LTTNG_TRACE_CHUNK_STATUS_OK) {
375 int ret;
376
377 ret = snprintf(chunk_id_str, sizeof(chunk_id_str), "%" PRIu64,
378 chunk_id);
379 if (ret < 0) {
380 lttng_strncpy(chunk_id_str, "-1", sizeof(chunk_id_str));
381 WARN("Failed to format trace chunk id");
382 }
383 is_anonymous_chunk = false;
384 } else if (status == LTTNG_TRACE_CHUNK_STATUS_NONE) {
385 is_anonymous_chunk = true;
386 } else {
387 ERR("Failed to get trace chunk id");
388 goto end;
389 }
390
391 DBG("Attempting to publish trace chunk: sessiond {%s}, session_id = "
392 "%" PRIu64 ", chunk_id = %s",
393 uuid_str, session_id,
394 is_anonymous_chunk ? "anonymous" : chunk_id_str);
395
396 element = trace_chunk_registry_ht_element_find(sessiond_registry, &key);
397 if (!element) {
398 ERR("Failed to find registry of sessiond {%s}", uuid_str);
399 goto end;
400 }
401
402 published_chunk = lttng_trace_chunk_registry_publish_chunk(
403 element->trace_chunk_registry, session_id, new_chunk);
404 /*
405 * At this point, two references to the published chunks exist. One
406 * is taken by the registry while the other is being returned to the
407 * caller. In the use case of the relay daemon, the reference held
408 * by the registry itself is undesirable.
409 *
410 * We want the trace chunk to be removed from the registry as soon
411 * as it is not being used by the relay daemon (through a session
412 * or a stream). This differs from the behaviour of the consumer
413 * daemon which relies on an explicit command from the session
414 * daemon to release the registry's reference.
415 */
416 lttng_trace_chunk_put(published_chunk);
417 end:
418 trace_chunk_registry_ht_element_put(element);
419 return published_chunk;
420 }
421
422 struct lttng_trace_chunk *
423 sessiond_trace_chunk_registry_get_anonymous_chunk(
424 struct sessiond_trace_chunk_registry *sessiond_registry,
425 const lttng_uuid sessiond_uuid,
426 uint64_t session_id)
427 {
428 struct lttng_trace_chunk *chunk = NULL;
429 struct trace_chunk_registry_ht_element *element;
430 struct trace_chunk_registry_ht_key key;
431 char uuid_str[UUID_STR_LEN];
432
433 lttng_uuid_to_str(sessiond_uuid, uuid_str);
434
435 lttng_uuid_copy(key.sessiond_uuid, sessiond_uuid);
436 element = trace_chunk_registry_ht_element_find(sessiond_registry, &key);
437 if (!element) {
438 ERR("Failed to find trace chunk registry of sessiond {%s}",
439 uuid_str);
440 goto end;
441 }
442
443 chunk = lttng_trace_chunk_registry_find_anonymous_chunk(
444 element->trace_chunk_registry,
445 session_id);
446 trace_chunk_registry_ht_element_put(element);
447 end:
448 return chunk;
449 }
450
451 struct lttng_trace_chunk *
452 sessiond_trace_chunk_registry_get_chunk(
453 struct sessiond_trace_chunk_registry *sessiond_registry,
454 const lttng_uuid sessiond_uuid,
455 uint64_t session_id, uint64_t chunk_id)
456 {
457 struct lttng_trace_chunk *chunk = NULL;
458 struct trace_chunk_registry_ht_element *element;
459 struct trace_chunk_registry_ht_key key;
460 char uuid_str[UUID_STR_LEN];
461
462 lttng_uuid_to_str(sessiond_uuid, uuid_str);
463
464 lttng_uuid_copy(key.sessiond_uuid, sessiond_uuid);
465 element = trace_chunk_registry_ht_element_find(sessiond_registry, &key);
466 if (!element) {
467 ERR("Failed to find trace chunk registry of sessiond {%s}",
468 uuid_str);
469 goto end;
470 }
471
472 chunk = lttng_trace_chunk_registry_find_chunk(
473 element->trace_chunk_registry,
474 session_id, chunk_id);
475 trace_chunk_registry_ht_element_put(element);
476 end:
477 return chunk;
478 }
This page took 0.060236 seconds and 4 git commands to generate.