2 * Copyright (C) 2020 Francis Deslauriers <francis.deslauriers@efficios.com>
4 * SPDX-License-Identifier: GPL-2.0-only
12 #include <urcu/compiler.h>
14 #include <common/error.h>
15 #include <common/hashtable/hashtable.h>
16 #include <common/index-allocator.h>
17 #include <common/kernel-ctl/kernel-ctl.h>
18 #include <lttng/trigger/trigger-internal.h>
20 #include "event-notifier-error-accounting.h"
22 #define ERROR_COUNTER_INDEX_HT_INITIAL_SIZE 16
24 struct index_ht_entry
{
25 struct lttng_ht_node_u64 node
;
26 uint64_t error_counter_index
;
27 struct rcu_head rcu_head
;
30 struct kernel_error_account_entry
{
31 int kernel_event_notifier_error_counter_fd
;
34 static struct kernel_error_account_entry kernel_error_accountant
;
36 /* Hashtable mapping event notifier token to index_ht_entry. */
37 static struct lttng_ht
*error_counter_indexes_ht
;
39 static uint64_t error_counter_size
;
40 static struct lttng_index_allocator
*index_allocator
;
43 const char *error_accounting_status_str(
44 enum event_notifier_error_accounting_status status
)
47 case EVENT_NOTIFIER_ERROR_ACCOUNTING_STATUS_OK
:
49 case EVENT_NOTIFIER_ERROR_ACCOUNTING_STATUS_ERR
:
51 case EVENT_NOTIFIER_ERROR_ACCOUNTING_STATUS_NOT_FOUND
:
53 case EVENT_NOTIFIER_ERROR_ACCOUNTING_STATUS_NOMEM
:
55 case EVENT_NOTIFIER_ERROR_ACCOUNTING_STATUS_NO_INDEX_AVAILABLE
:
56 return "NO_INDEX_AVAILABLE";
62 enum event_notifier_error_accounting_status
63 event_notifier_error_accounting_init(uint64_t nb_bucket
)
65 enum event_notifier_error_accounting_status status
;
67 index_allocator
= lttng_index_allocator_create(nb_bucket
);
68 if (!index_allocator
) {
69 ERR("Failed to allocate event notifier error counter index");
70 status
= EVENT_NOTIFIER_ERROR_ACCOUNTING_STATUS_NOMEM
;
71 goto error_index_allocator
;
74 error_counter_indexes_ht
= lttng_ht_new(
75 ERROR_COUNTER_INDEX_HT_INITIAL_SIZE
, LTTNG_HT_TYPE_U64
);
76 if (!error_counter_indexes_ht
) {
77 status
= EVENT_NOTIFIER_ERROR_ACCOUNTING_STATUS_NOMEM
;
78 goto error_index_allocator
;
81 error_counter_size
= nb_bucket
;
83 status
= EVENT_NOTIFIER_ERROR_ACCOUNTING_STATUS_OK
;
85 error_index_allocator
:
90 enum event_notifier_error_accounting_status
get_error_counter_index_for_token(
91 uint64_t tracer_token
, uint64_t *error_counter_index
)
93 struct lttng_ht_node_u64
*node
;
94 struct lttng_ht_iter iter
;
95 const struct index_ht_entry
*index_entry
;
96 enum event_notifier_error_accounting_status status
;
99 lttng_ht_lookup(error_counter_indexes_ht
, &tracer_token
, &iter
);
100 node
= lttng_ht_iter_get_node_u64(&iter
);
102 index_entry
= caa_container_of(
103 node
, const struct index_ht_entry
, node
);
104 *error_counter_index
= index_entry
->error_counter_index
;
105 status
= EVENT_NOTIFIER_ERROR_ACCOUNTING_STATUS_OK
;
107 status
= EVENT_NOTIFIER_ERROR_ACCOUNTING_STATUS_NOT_FOUND
;
115 enum event_notifier_error_accounting_status
116 event_notifier_error_accounting_kernel_clear(
117 const struct lttng_trigger
*trigger
)
120 uint64_t error_counter_index
;
121 enum event_notifier_error_accounting_status status
;
122 struct lttng_kernel_counter_clear counter_clear
= {};
124 status
= get_error_counter_index_for_token(
125 lttng_trigger_get_tracer_token(trigger
),
126 &error_counter_index
);
127 if (status
!= EVENT_NOTIFIER_ERROR_ACCOUNTING_STATUS_OK
) {
128 uid_t trigger_owner_uid
;
129 const char *trigger_name
;
130 const enum lttng_trigger_status trigger_status
=
131 lttng_trigger_get_owner_uid(
132 trigger
, &trigger_owner_uid
);
134 assert(trigger_status
== LTTNG_TRIGGER_STATUS_OK
);
135 if (lttng_trigger_get_name(trigger
, &trigger_name
) !=
136 LTTNG_TRIGGER_STATUS_OK
) {
137 trigger_name
= "(unnamed)";
140 ERR("Failed to get event notifier error counter index: trigger owner uid = %d, trigger name = '%s'",
141 trigger_owner_uid
, trigger_name
);
145 counter_clear
.index
.number_dimensions
= 1;
146 counter_clear
.index
.dimension_indexes
[0] = error_counter_index
;
148 ret
= kernctl_counter_clear(
149 kernel_error_accountant
.kernel_event_notifier_error_counter_fd
,
152 ERR("Failed to clear event notifier error counter");
153 status
= EVENT_NOTIFIER_ERROR_ACCOUNTING_STATUS_ERR
;
157 status
= EVENT_NOTIFIER_ERROR_ACCOUNTING_STATUS_OK
;
162 enum event_notifier_error_accounting_status
163 event_notifier_error_accounting_register_kernel(
164 int kernel_event_notifier_group_fd
)
166 int error_counter_fd
= -1, ret
;
167 enum event_notifier_error_accounting_status status
;
168 const struct lttng_kernel_counter_conf error_counter_conf
= {
169 .arithmetic
= LTTNG_KERNEL_COUNTER_ARITHMETIC_MODULAR
,
170 .bitness
= sizeof(void *) == sizeof(uint32_t) ?
171 LTTNG_KERNEL_COUNTER_BITNESS_32
:
172 LTTNG_KERNEL_COUNTER_BITNESS_64
,
173 .global_sum_step
= 0,
174 .number_dimensions
= 1,
175 .dimensions
[0].size
= error_counter_size
,
176 .dimensions
[0].has_underflow
= false,
177 .dimensions
[0].has_overflow
= false,
180 ret
= kernctl_create_event_notifier_group_error_counter(
181 kernel_event_notifier_group_fd
, &error_counter_conf
);
183 PERROR("Failed to create event notifier group error counter through kernel ioctl: kernel_event_notifier_group_fd = %d",
184 kernel_event_notifier_group_fd
);
185 status
= EVENT_NOTIFIER_ERROR_ACCOUNTING_STATUS_ERR
;
189 error_counter_fd
= ret
;
191 /* Prevent fd duplication after execlp(). */
192 ret
= fcntl(error_counter_fd
, F_SETFD
, FD_CLOEXEC
);
194 PERROR("Failed to set FD_CLOEXEC flag on event notifier error counter file descriptor: error_counter_fd = %d",
196 status
= EVENT_NOTIFIER_ERROR_ACCOUNTING_STATUS_ERR
;
200 DBG("Created kernel event notifier group error counter: fd = %d",
203 kernel_error_accountant
.kernel_event_notifier_error_counter_fd
=
205 status
= EVENT_NOTIFIER_ERROR_ACCOUNTING_STATUS_OK
;
212 enum event_notifier_error_accounting_status
create_error_counter_index_for_token(
213 uint64_t tracer_token
, uint64_t *error_counter_index
)
215 struct index_ht_entry
*index_entry
;
216 enum lttng_index_allocator_status index_alloc_status
;
217 uint64_t local_error_counter_index
;
218 enum event_notifier_error_accounting_status status
;
220 /* Allocate a new index for that counter. */
221 index_alloc_status
= lttng_index_allocator_alloc(index_allocator
,
222 &local_error_counter_index
);
223 switch (index_alloc_status
) {
224 case LTTNG_INDEX_ALLOCATOR_STATUS_EMPTY
:
225 DBG("No indices left in the configured event notifier error counter: "
226 "number-of-indices = %"PRIu64
,
227 lttng_index_allocator_get_index_count(
229 status
= EVENT_NOTIFIER_ERROR_ACCOUNTING_STATUS_NO_INDEX_AVAILABLE
;
231 case LTTNG_INDEX_ALLOCATOR_STATUS_OK
:
234 status
= EVENT_NOTIFIER_ERROR_ACCOUNTING_STATUS_ERR
;
238 index_entry
= zmalloc(sizeof(*index_entry
));
239 if (index_entry
== NULL
) {
240 PERROR("Failed to allocate event notifier error counter hash table entry");
241 status
= EVENT_NOTIFIER_ERROR_ACCOUNTING_STATUS_NOMEM
;
245 index_entry
->error_counter_index
= local_error_counter_index
;
246 lttng_ht_node_init_u64(&index_entry
->node
, tracer_token
);
247 lttng_ht_add_unique_u64(error_counter_indexes_ht
, &index_entry
->node
);
249 DBG("Allocated error counter index for tracer token: tracer token = %" PRIu64
", index = %" PRIu64
,
250 tracer_token
, local_error_counter_index
);
251 *error_counter_index
= local_error_counter_index
;
252 status
= EVENT_NOTIFIER_ERROR_ACCOUNTING_STATUS_OK
;
257 enum event_notifier_error_accounting_status
258 event_notifier_error_accounting_register_event_notifier(
259 const struct lttng_trigger
*trigger
,
260 uint64_t *error_counter_index
)
262 enum event_notifier_error_accounting_status status
;
263 uint64_t local_error_counter_index
;
266 * Check if this event notifier already has a error counter index
269 status
= get_error_counter_index_for_token(
270 lttng_trigger_get_tracer_token(trigger
),
271 &local_error_counter_index
);
273 case EVENT_NOTIFIER_ERROR_ACCOUNTING_STATUS_NOT_FOUND
:
275 uid_t trigger_owner_uid
;
276 const char *trigger_name
;
277 const enum lttng_trigger_status trigger_status
=
278 lttng_trigger_get_owner_uid(
279 trigger
, &trigger_owner_uid
);
281 assert(trigger_status
== LTTNG_TRIGGER_STATUS_OK
);
282 if (lttng_trigger_get_name(trigger
, &trigger_name
) !=
283 LTTNG_TRIGGER_STATUS_OK
) {
284 trigger_name
= "(unnamed)";
287 DBG("Event notifier error counter index not found for tracer token (allocating a new one): trigger owner = %d, trigger name = '%s', tracer token = %" PRIu64
,
288 trigger_owner_uid
, trigger_name
,
289 lttng_trigger_get_tracer_token(trigger
));
290 status
= create_error_counter_index_for_token(
291 lttng_trigger_get_tracer_token(trigger
),
292 &local_error_counter_index
);
293 if (status
!= EVENT_NOTIFIER_ERROR_ACCOUNTING_STATUS_OK
) {
298 case EVENT_NOTIFIER_ERROR_ACCOUNTING_STATUS_OK
:
299 *error_counter_index
= local_error_counter_index
;
300 status
= EVENT_NOTIFIER_ERROR_ACCOUNTING_STATUS_OK
;
311 enum event_notifier_error_accounting_status
312 event_notifier_error_accounting_kernel_get_count(
313 const struct lttng_trigger
*trigger
, uint64_t *count
)
315 struct lttng_kernel_counter_aggregate counter_aggregate
= {};
316 enum event_notifier_error_accounting_status status
;
317 uint64_t error_counter_index
;
320 status
= get_error_counter_index_for_token(
321 lttng_trigger_get_tracer_token(trigger
), &error_counter_index
);
322 if (status
!= EVENT_NOTIFIER_ERROR_ACCOUNTING_STATUS_OK
) {
326 counter_aggregate
.index
.number_dimensions
= 1;
327 counter_aggregate
.index
.dimension_indexes
[0] = error_counter_index
;
329 assert(kernel_error_accountant
.kernel_event_notifier_error_counter_fd
);
331 ret
= kernctl_counter_get_aggregate_value(
332 kernel_error_accountant
.kernel_event_notifier_error_counter_fd
,
334 if (ret
|| counter_aggregate
.value
.value
< 0) {
335 uid_t trigger_owner_uid
;
336 const char *trigger_name
;
337 const enum lttng_trigger_status trigger_status
=
338 lttng_trigger_get_owner_uid(
339 trigger
, &trigger_owner_uid
);
341 assert(trigger_status
== LTTNG_TRIGGER_STATUS_OK
);
342 if (lttng_trigger_get_name(trigger
, &trigger_name
) !=
343 LTTNG_TRIGGER_STATUS_OK
) {
344 trigger_name
= "(unnamed)";
347 if (counter_aggregate
.value
.value
< 0) {
348 ERR("Invalid negative event notifier error counter value: trigger owner = %d, trigger name = '%s', value = %" PRId64
,
349 trigger_owner_uid
, trigger_name
,
350 counter_aggregate
.value
.value
);
352 ERR("Failed to getting event notifier error count: trigger owner = %d, trigger name = '%s', ret = %d",
353 trigger_owner_uid
, trigger_name
, ret
);
356 status
= EVENT_NOTIFIER_ERROR_ACCOUNTING_STATUS_ERR
;
360 /* Error count can't be negative. */
361 assert(counter_aggregate
.value
.value
>= 0);
362 *count
= (uint64_t) counter_aggregate
.value
.value
;
364 status
= EVENT_NOTIFIER_ERROR_ACCOUNTING_STATUS_OK
;
370 enum event_notifier_error_accounting_status
371 event_notifier_error_accounting_get_count(
372 const struct lttng_trigger
*trigger
, uint64_t *count
)
374 switch (lttng_trigger_get_underlying_domain_type_restriction(trigger
)) {
375 case LTTNG_DOMAIN_KERNEL
:
376 return event_notifier_error_accounting_kernel_get_count(
378 case LTTNG_DOMAIN_UST
:
379 return EVENT_NOTIFIER_ERROR_ACCOUNTING_STATUS_OK
;
386 enum event_notifier_error_accounting_status
387 event_notifier_error_accounting_clear(const struct lttng_trigger
*trigger
)
389 switch (lttng_trigger_get_underlying_domain_type_restriction(trigger
)) {
390 case LTTNG_DOMAIN_KERNEL
:
391 return event_notifier_error_accounting_kernel_clear(trigger
);
392 case LTTNG_DOMAIN_UST
:
393 return EVENT_NOTIFIER_ERROR_ACCOUNTING_STATUS_OK
;
399 static void free_index_ht_entry(struct rcu_head
*head
)
401 struct index_ht_entry
*entry
= caa_container_of(head
,
402 struct index_ht_entry
, rcu_head
);
407 void event_notifier_error_accounting_unregister_event_notifier(
408 const struct lttng_trigger
*trigger
)
410 struct lttng_ht_iter iter
;
411 struct lttng_ht_node_u64
*node
;
412 struct index_ht_entry
*index_entry
;
413 enum event_notifier_error_accounting_status status
;
414 enum lttng_index_allocator_status index_alloc_status
;
415 uint64_t tracer_token
= lttng_trigger_get_tracer_token(trigger
);
417 status
= event_notifier_error_accounting_clear(trigger
);
418 if (status
!= EVENT_NOTIFIER_ERROR_ACCOUNTING_STATUS_OK
) {
419 ERR("Failed to clear event notifier error counter index");
423 lttng_ht_lookup(error_counter_indexes_ht
, &tracer_token
, &iter
);
424 node
= lttng_ht_iter_get_node_u64(&iter
);
426 index_entry
= caa_container_of(node
, struct index_ht_entry
, node
);
427 index_alloc_status
= lttng_index_allocator_release(
429 index_entry
->error_counter_index
);
430 if (index_alloc_status
!= LTTNG_INDEX_ALLOCATOR_STATUS_OK
) {
431 ERR("Failed to release event notifier error counter index: index = %" PRIu64
,
432 index_entry
->error_counter_index
);
435 lttng_ht_del(error_counter_indexes_ht
, &iter
);
436 call_rcu(&index_entry
->rcu_head
, free_index_ht_entry
);
442 void event_notifier_error_accounting_fini(void)
444 lttng_index_allocator_destroy(index_allocator
);
446 if (kernel_error_accountant
.kernel_event_notifier_error_counter_fd
) {
447 const int ret
= close(kernel_error_accountant
.kernel_event_notifier_error_counter_fd
);
450 PERROR("Failed to close kernel event notifier error counter");
455 * Will assert if some error counters were not released (an internal
458 lttng_ht_destroy(error_counter_indexes_ht
);