X-Git-Url: https://git.lttng.org/?a=blobdiff_plain;f=src%2Fbin%2Flttng-sessiond%2Fkernel.c;h=59b94137f8f30590410912bccb7a7335fdf8e0fb;hb=a0377dfefe40662ba7d68617bce6ff467114136c;hp=1fc36e33e9b45b1c7b941c3261f7c8ad0c07dfe9;hpb=0a398bc4ce5e8f150e71cc7196162e677d571821;p=lttng-tools.git diff --git a/src/bin/lttng-sessiond/kernel.c b/src/bin/lttng-sessiond/kernel.c index 1fc36e33e..59b94137f 100644 --- a/src/bin/lttng-sessiond/kernel.c +++ b/src/bin/lttng-sessiond/kernel.c @@ -15,6 +15,7 @@ #include #include +#include #include #include #include @@ -25,16 +26,28 @@ #include #include +#include +#include +#include +#include +#include +#include +#include + +#include "event-notifier-error-accounting.h" #include "lttng-sessiond.h" #include "lttng-syscall.h" +#include "condition-internal.h" #include "consumer.h" #include "kernel.h" #include "kernel-consumer.h" #include "kern-modules.h" +#include "sessiond-config.h" #include "utils.h" #include "rotate.h" #include "modprobe.h" #include "tracker.h" +#include "notification-thread-commands.h" /* * Key used to reference a channel between the sessiond and the consumer. This @@ -45,9 +58,10 @@ static uint64_t next_kernel_channel_key; static const char *module_proc_lttng = "/proc/lttng"; static int kernel_tracer_fd = -1; +static int kernel_tracer_event_notifier_group_fd = -1; +static int kernel_tracer_event_notifier_group_notification_fd = -1; +static struct cds_lfht *kernel_token_to_event_notifier_rule_ht; -#include -#include /* * Add context on a kernel channel. * @@ -58,8 +72,8 @@ int kernel_add_channel_context(struct ltt_kernel_channel *chan, { int ret; - assert(chan); - assert(ctx); + LTTNG_ASSERT(chan); + LTTNG_ASSERT(ctx); DBG("Adding context to channel %s", chan->channel->name); ret = kernctl_add_context(chan->fd, &ctx->ctx); @@ -101,7 +115,7 @@ int kernel_create_session(struct ltt_session *session) int ret; struct ltt_kernel_session *lks; - assert(session); + LTTNG_ASSERT(session); /* Allocate data structure */ lks = trace_kernel_create_session(); @@ -170,8 +184,8 @@ int kernel_create_channel(struct ltt_kernel_session *session, int ret; struct ltt_kernel_channel *lkc; - assert(session); - assert(chan); + LTTNG_ASSERT(session); + LTTNG_ASSERT(chan); /* Allocate kernel channel */ lkc = trace_kernel_create_channel(chan); @@ -219,6 +233,51 @@ error: return -1; } +/* + * Create a kernel event notifier group, register it to the kernel tracer and + * add it to the kernel session. + */ +static int kernel_create_event_notifier_group(int *event_notifier_group_fd) +{ + int ret; + int local_fd = -1; + + LTTNG_ASSERT(event_notifier_group_fd); + + /* Kernel event notifier group creation. */ + ret = kernctl_create_event_notifier_group(kernel_tracer_fd); + if (ret < 0) { + PERROR("Failed to create kernel event notifier group"); + ret = -1; + goto error; + } + + local_fd = ret; + + /* Prevent fd duplication after execlp(). */ + ret = fcntl(local_fd, F_SETFD, FD_CLOEXEC); + if (ret < 0) { + PERROR("Failed to set FD_CLOEXEC on kernel event notifier group file descriptor: fd = %d", + local_fd); + goto error; + } + + DBG("Created kernel event notifier group: fd = %d", local_fd); + *event_notifier_group_fd = local_fd; + local_fd = -1; + ret = 0; +error: + if (local_fd >= 0) { + ret = close(local_fd); + if (ret) { + PERROR("Failed to close kernel event notifier group file descriptor: fd = %d", + local_fd); + } + } + + return ret; +} + /* * Compute the offset of the instrumentation byte in the binary based on the * function probe location using the ELF lookup method. @@ -238,7 +297,7 @@ int extract_userspace_probe_offset_function_elf( const struct lttng_userspace_probe_location_lookup_method *lookup = NULL; enum lttng_userspace_probe_location_lookup_method_type lookup_method_type; - assert(lttng_userspace_probe_location_get_type(probe_location) == + LTTNG_ASSERT(lttng_userspace_probe_location_get_type(probe_location) == LTTNG_USERSPACE_PROBE_LOCATION_TYPE_FUNCTION); lookup = lttng_userspace_probe_location_get_lookup_method( @@ -251,7 +310,7 @@ int extract_userspace_probe_offset_function_elf( lookup_method_type = lttng_userspace_probe_location_lookup_method_get_type(lookup); - assert(lookup_method_type == + LTTNG_ASSERT(lookup_method_type == LTTNG_USERSPACE_PROBE_LOCATION_LOOKUP_METHOD_TYPE_FUNCTION_ELF); symbol = lttng_userspace_probe_location_function_get_function_name( @@ -300,7 +359,7 @@ int extract_userspace_probe_offset_tracepoint_sdt( int ret = 0; int fd, i; - assert(lttng_userspace_probe_location_get_type(probe_location) == + LTTNG_ASSERT(lttng_userspace_probe_location_get_type(probe_location) == LTTNG_USERSPACE_PROBE_LOCATION_TYPE_TRACEPOINT); lookup = lttng_userspace_probe_location_get_lookup_method(probe_location); @@ -312,7 +371,7 @@ int extract_userspace_probe_offset_tracepoint_sdt( lookup_method_type = lttng_userspace_probe_location_lookup_method_get_type(lookup); - assert(lookup_method_type == + LTTNG_ASSERT(lookup_method_type == LTTNG_USERSPACE_PROBE_LOCATION_LOOKUP_METHOD_TYPE_TRACEPOINT_SDT); @@ -377,7 +436,7 @@ int userspace_probe_add_callsite( switch (type) { case LTTNG_USERSPACE_PROBE_LOCATION_LOOKUP_METHOD_TYPE_FUNCTION_ELF: { - struct lttng_kernel_event_callsite callsite; + struct lttng_kernel_abi_event_callsite callsite; uint64_t offset; ret = extract_userspace_probe_offset_function_elf(location, @@ -401,7 +460,7 @@ int userspace_probe_add_callsite( int i; uint64_t *offsets = NULL; uint32_t offsets_count; - struct lttng_kernel_event_callsite callsite; + struct lttng_kernel_abi_event_callsite callsite; /* * This call allocates the offsets buffer. This buffer must be freed @@ -445,8 +504,8 @@ int userspace_probe_event_add_callsites(struct lttng_event *ev, int ret; const struct lttng_userspace_probe_location *location = NULL; - assert(ev); - assert(ev->type == LTTNG_EVENT_USERSPACE_PROBE); + LTTNG_ASSERT(ev); + LTTNG_ASSERT(ev->type == LTTNG_EVENT_USERSPACE_PROBE); location = lttng_event_get_userspace_probe_location(ev); if (!location) { @@ -465,6 +524,44 @@ end: return ret; } +/* + * Extract the offsets of the instrumentation point for the different look-up + * methods. + */ +static int userspace_probe_event_rule_add_callsites( + const struct lttng_event_rule *rule, + const struct lttng_credentials *creds, + int fd) +{ + int ret; + enum lttng_event_rule_status status; + enum lttng_event_rule_type event_rule_type; + const struct lttng_userspace_probe_location *location = NULL; + + LTTNG_ASSERT(rule); + LTTNG_ASSERT(creds); + + event_rule_type = lttng_event_rule_get_type(rule); + LTTNG_ASSERT(event_rule_type == LTTNG_EVENT_RULE_TYPE_KERNEL_UPROBE); + + status = lttng_event_rule_kernel_uprobe_get_location(rule, &location); + if (status != LTTNG_EVENT_RULE_STATUS_OK || !location) { + ret = -1; + goto end; + } + + ret = userspace_probe_add_callsite(location, + lttng_credentials_get_uid(creds), + lttng_credentials_get_gid(creds), fd); + if (ret) { + WARN("Failed to add callsite to user space probe object: fd = %d", + fd); + } + +end: + return ret; +} + /* * Create a kernel event, enable it to the kernel tracer and add it to the * channel event list of the kernel session. @@ -473,14 +570,14 @@ end: int kernel_create_event(struct lttng_event *ev, struct ltt_kernel_channel *channel, char *filter_expression, - struct lttng_filter_bytecode *filter) + struct lttng_bytecode *filter) { int err, fd; enum lttng_error_code ret; struct ltt_kernel_event *event; - assert(ev); - assert(channel); + LTTNG_ASSERT(ev); + LTTNG_ASSERT(channel); /* We pass ownership of filter_expression and filter */ ret = trace_kernel_create_event(ev, filter_expression, @@ -587,7 +684,7 @@ int kernel_disable_channel(struct ltt_kernel_channel *chan) { int ret; - assert(chan); + LTTNG_ASSERT(chan); ret = kernctl_disable(chan->fd); if (ret < 0) { @@ -612,7 +709,7 @@ int kernel_enable_channel(struct ltt_kernel_channel *chan) { int ret; - assert(chan); + LTTNG_ASSERT(chan); ret = kernctl_enable(chan->fd); if (ret < 0 && ret != -EEXIST) { @@ -637,7 +734,7 @@ int kernel_enable_event(struct ltt_kernel_event *event) { int ret; - assert(event); + LTTNG_ASSERT(event); ret = kernctl_enable(event->fd); if (ret < 0) { @@ -668,18 +765,12 @@ int kernel_disable_event(struct ltt_kernel_event *event) { int ret; - assert(event); + LTTNG_ASSERT(event); ret = kernctl_disable(event->fd); if (ret < 0) { - switch (-ret) { - case EEXIST: - ret = LTTNG_ERR_KERN_EVENT_EXIST; - break; - default: - PERROR("disable kernel event"); - break; - } + PERROR("Failed to disable kernel event: name = '%s', fd = %d", + event->event->name, event->fd); goto error; } @@ -692,6 +783,35 @@ error: return ret; } +/* + * Disable a kernel event notifier. + */ +static +int kernel_disable_event_notifier_rule(struct ltt_kernel_event_notifier_rule *event) +{ + int ret; + + LTTNG_ASSERT(event); + + rcu_read_lock(); + cds_lfht_del(kernel_token_to_event_notifier_rule_ht, &event->ht_node); + rcu_read_unlock(); + + ret = kernctl_disable(event->fd); + if (ret < 0) { + PERROR("Failed to disable kernel event notifier: fd = %d, token = %" PRIu64, + event->fd, event->token); + goto error; + } + + event->enabled = 0; + DBG("Disabled kernel event notifier: fd = %d, token = %" PRIu64, + event->fd, event->token); + +error: + return ret; +} + static struct process_attr_tracker *_kernel_get_process_attr_tracker( struct ltt_kernel_session *session, @@ -1067,7 +1187,7 @@ int kernel_open_metadata(struct ltt_kernel_session *session) int ret; struct ltt_kernel_metadata *lkm = NULL; - assert(session); + LTTNG_ASSERT(session); /* Allocate kernel metadata */ lkm = trace_kernel_create_metadata(); @@ -1108,7 +1228,7 @@ int kernel_start_session(struct ltt_kernel_session *session) { int ret; - assert(session); + LTTNG_ASSERT(session); ret = kernctl_start_session(session->fd); if (ret < 0) { @@ -1166,7 +1286,7 @@ int kernel_flush_buffer(struct ltt_kernel_channel *channel) int ret; struct ltt_kernel_stream *stream; - assert(channel); + LTTNG_ASSERT(channel); DBG("Flush buffer for channel %s", channel->channel->name); @@ -1190,7 +1310,7 @@ int kernel_stop_session(struct ltt_kernel_session *session) { int ret; - assert(session); + LTTNG_ASSERT(session); ret = kernctl_stop_session(session->fd); if (ret < 0) { @@ -1220,7 +1340,7 @@ int kernel_open_channel_stream(struct ltt_kernel_channel *channel) int ret; struct ltt_kernel_stream *lks; - assert(channel); + LTTNG_ASSERT(channel); while ((ret = kernctl_create_stream(channel->fd)) >= 0) { lks = trace_kernel_create_stream(channel->channel->name, @@ -1264,7 +1384,7 @@ int kernel_open_metadata_stream(struct ltt_kernel_session *session) { int ret; - assert(session); + LTTNG_ASSERT(session); ret = kernctl_create_stream(session->metadata->fd); if (ret < 0) { @@ -1297,7 +1417,7 @@ ssize_t kernel_list_events(struct lttng_event **events) FILE *fp; struct lttng_event *elist; - assert(events); + LTTNG_ASSERT(events); fd = kernctl_tracepoint_list(kernel_tracer_fd); if (fd < 0) { @@ -1373,8 +1493,8 @@ error: /* * Get kernel version and validate it. */ -int kernel_validate_version(struct lttng_kernel_tracer_version *version, - struct lttng_kernel_tracer_abi_version *abi_version) +int kernel_validate_version(struct lttng_kernel_abi_tracer_version *version, + struct lttng_kernel_abi_tracer_abi_version *abi_version) { int ret; @@ -1395,10 +1515,10 @@ int kernel_validate_version(struct lttng_kernel_tracer_version *version, ERR("Failed to retrieve lttng-modules ABI version"); goto error; } - if (abi_version->major != LTTNG_MODULES_ABI_MAJOR_VERSION) { + if (abi_version->major != LTTNG_KERNEL_ABI_MAJOR_VERSION) { ERR("Kernel tracer ABI version (%d.%d) does not match the expected ABI major version (%d.*)", abi_version->major, abi_version->minor, - LTTNG_MODULES_ABI_MAJOR_VERSION); + LTTNG_KERNEL_ABI_MAJOR_VERSION); goto error; } DBG2("Kernel tracer version validated (%d.%d, ABI %d.%d)", @@ -1513,8 +1633,8 @@ void kernel_destroy_channel(struct ltt_kernel_channel *kchan) { struct ltt_kernel_session *ksess = NULL; - assert(kchan); - assert(kchan->channel); + LTTNG_ASSERT(kchan); + LTTNG_ASSERT(kchan->channel); DBG3("Kernel destroy channel %s", kchan->channel->name); @@ -1554,9 +1674,9 @@ enum lttng_error_code kernel_snapshot_record( char *trace_path = NULL; size_t consumer_path_offset = 0; - assert(ksess); - assert(ksess->consumer); - assert(output); + LTTNG_ASSERT(ksess); + LTTNG_ASSERT(ksess->consumer); + LTTNG_ASSERT(output); DBG("Kernel snapshot record started"); @@ -1579,7 +1699,7 @@ enum lttng_error_code kernel_snapshot_record( } trace_path = setup_channel_trace_path(ksess->consumer, - DEFAULT_KERNEL_TRACE_DIR, &consumer_path_offset); + "", &consumer_path_offset); if (!trace_path) { status = LTTNG_ERR_INVALID; goto error; @@ -1652,8 +1772,8 @@ error: */ int kernel_syscall_mask(int chan_fd, char **syscall_mask, uint32_t *nr_bits) { - assert(syscall_mask); - assert(nr_bits); + LTTNG_ASSERT(syscall_mask); + LTTNG_ASSERT(nr_bits); return kernctl_syscall_mask(chan_fd, syscall_mask, nr_bits); } @@ -1662,7 +1782,7 @@ static int kernel_tracer_abi_greater_or_equal(unsigned int major, unsigned int minor) { int ret; - struct lttng_kernel_tracer_abi_version abi; + struct lttng_kernel_abi_tracer_abi_version abi; ret = kernctl_tracer_abi_version(kernel_tracer_fd, &abi); if (ret < 0) { @@ -1732,8 +1852,8 @@ enum lttng_error_code kernel_rotate_session(struct ltt_session *session) struct lttng_ht_iter iter; struct ltt_kernel_session *ksess = session->kernel_session; - assert(ksess); - assert(ksess->consumer); + LTTNG_ASSERT(ksess); + LTTNG_ASSERT(ksess->consumer); DBG("Rotate kernel session %s started (session %" PRIu64 ")", session->name, session->id); @@ -1785,7 +1905,7 @@ enum lttng_error_code kernel_create_channel_subdirectories( enum lttng_trace_chunk_status chunk_status; rcu_read_lock(); - assert(ksess->current_trace_chunk); + LTTNG_ASSERT(ksess->current_trace_chunk); /* * Create the index subdirectory which will take care @@ -1826,8 +1946,8 @@ int init_kernel_tracer(void) } /* Validate kernel version */ - ret = kernel_validate_version(&kernel_tracer_version, - &kernel_tracer_abi_version); + ret = kernel_validate_version(&the_kernel_tracer_version, + &the_kernel_tracer_abi_version); if (ret < 0) { goto error_version; } @@ -1841,37 +1961,99 @@ int init_kernel_tracer(void) if (ret < 0) { goto error_modules; } - if (ret < 1) { WARN("Kernel tracer does not support buffer monitoring. " "The monitoring timer of channels in the kernel domain " "will be set to 0 (disabled)."); } - DBG("Kernel tracer fd %d", kernel_tracer_fd); + ret = kernel_supports_event_notifiers(); + if (ret < 0) { + ERR("Failed to check for kernel tracer event notifier support"); + goto error_modules; + } + ret = kernel_create_event_notifier_group(&kernel_tracer_event_notifier_group_fd); + if (ret < 0) { + /* This is not fatal. */ + WARN("Failed to create kernel event notifier group"); + kernel_tracer_event_notifier_group_fd = -1; + } else { + enum event_notifier_error_accounting_status error_accounting_status; + enum lttng_error_code error_code_ret = + kernel_create_event_notifier_group_notification_fd( + &kernel_tracer_event_notifier_group_notification_fd); + + if (error_code_ret != LTTNG_OK) { + goto error_modules; + } + + error_accounting_status = event_notifier_error_accounting_register_kernel( + kernel_tracer_event_notifier_group_fd); + if (error_accounting_status != EVENT_NOTIFIER_ERROR_ACCOUNTING_STATUS_OK) { + ERR("Failed to initialize event notifier error accounting for kernel tracer"); + error_code_ret = LTTNG_ERR_EVENT_NOTIFIER_ERROR_ACCOUNTING; + goto error_modules; + } + + kernel_token_to_event_notifier_rule_ht = cds_lfht_new( + DEFAULT_HT_SIZE, 1, 0, + CDS_LFHT_AUTO_RESIZE | CDS_LFHT_ACCOUNTING, + NULL); + if (!kernel_token_to_event_notifier_rule_ht) { + goto error_token_ht; + } + } + + DBG("Kernel tracer initialized: kernel tracer fd = %d, event notifier group fd = %d, event notifier group notification fd = %d", + kernel_tracer_fd, kernel_tracer_event_notifier_group_fd, + kernel_tracer_event_notifier_group_notification_fd); ret = syscall_init_table(kernel_tracer_fd); if (ret < 0) { ERR("Unable to populate syscall table. Syscall tracing won't " "work for this session daemon."); } + return 0; error_version: modprobe_remove_lttng_control(); ret = close(kernel_tracer_fd); if (ret) { - PERROR("close"); + PERROR("Failed to close kernel tracer file descriptor: fd = %d", + kernel_tracer_fd); } + kernel_tracer_fd = -1; return LTTNG_ERR_KERN_VERSION; + +error_token_ht: + ret = close(kernel_tracer_event_notifier_group_notification_fd); + if (ret) { + PERROR("Failed to close kernel tracer event notifier group notification file descriptor: fd = %d", + kernel_tracer_event_notifier_group_notification_fd); + } + + kernel_tracer_event_notifier_group_notification_fd = -1; + error_modules: + ret = close(kernel_tracer_event_notifier_group_fd); + if (ret) { + PERROR("Failed to close kernel tracer event notifier group file descriptor: fd = %d", + kernel_tracer_event_notifier_group_fd); + } + + kernel_tracer_event_notifier_group_fd = -1; + ret = close(kernel_tracer_fd); if (ret) { - PERROR("close"); + PERROR("Failed to close kernel tracer file descriptor: fd = %d", + kernel_tracer_fd); } + kernel_tracer_fd = -1; + error_open: modprobe_remove_lttng_control(); @@ -1888,18 +2070,54 @@ error: LTTNG_HIDDEN void cleanup_kernel_tracer(void) { - int ret; + DBG2("Closing kernel event notifier group notification file descriptor"); + if (kernel_tracer_event_notifier_group_notification_fd >= 0) { + int ret = notification_thread_command_remove_tracer_event_source( + the_notification_thread_handle, + kernel_tracer_event_notifier_group_notification_fd); + if (ret != LTTNG_OK) { + ERR("Failed to remove kernel event notifier notification from notification thread"); + } + + ret = close(kernel_tracer_event_notifier_group_notification_fd); + if (ret) { + PERROR("Failed to close kernel event notifier group notification file descriptor: fd = %d", + kernel_tracer_event_notifier_group_notification_fd); + } + + kernel_tracer_event_notifier_group_notification_fd = -1; + } + + if (kernel_token_to_event_notifier_rule_ht) { + const int ret = cds_lfht_destroy( + kernel_token_to_event_notifier_rule_ht, NULL); + LTTNG_ASSERT(ret == 0); + } + + DBG2("Closing kernel event notifier group file descriptor"); + if (kernel_tracer_event_notifier_group_fd >= 0) { + const int ret = close(kernel_tracer_event_notifier_group_fd); + + if (ret) { + PERROR("Failed to close kernel event notifier group file descriptor: fd = %d", + kernel_tracer_event_notifier_group_fd); + } + + kernel_tracer_event_notifier_group_fd = -1; + } DBG2("Closing kernel fd"); if (kernel_tracer_fd >= 0) { - ret = close(kernel_tracer_fd); + const int ret = close(kernel_tracer_fd); + if (ret) { - PERROR("close"); + PERROR("Failed to close kernel tracer file descriptor: fd = %d", + kernel_tracer_fd); } + kernel_tracer_fd = -1; } - DBG("Unloading kernel modules"); - modprobe_remove_lttng_all(); + free(syscall_table); } @@ -1922,8 +2140,8 @@ enum lttng_error_code kernel_clear_session(struct ltt_session *session) struct lttng_ht_iter iter; struct ltt_kernel_session *ksess = session->kernel_session; - assert(ksess); - assert(ksess->consumer); + LTTNG_ASSERT(ksess); + LTTNG_ASSERT(ksess->consumer); DBG("Clear kernel session %s (session %" PRIu64 ")", session->name, session->id); @@ -1988,3 +2206,342 @@ end: rcu_read_unlock(); return status; } + +enum lttng_error_code kernel_create_event_notifier_group_notification_fd( + int *event_notifier_group_notification_fd) +{ + int local_fd = -1, ret; + enum lttng_error_code error_code_ret; + + LTTNG_ASSERT(event_notifier_group_notification_fd); + + ret = kernctl_create_event_notifier_group_notification_fd( + kernel_tracer_event_notifier_group_fd); + if (ret < 0) { + PERROR("Failed to create kernel event notifier group notification file descriptor"); + error_code_ret = LTTNG_ERR_EVENT_NOTIFIER_GROUP_NOTIFICATION_FD; + goto error; + } + + local_fd = ret; + + /* Prevent fd duplication after execlp(). */ + ret = fcntl(local_fd, F_SETFD, FD_CLOEXEC); + if (ret < 0) { + PERROR("Failed to set FD_CLOEXEC on kernel event notifier group notification file descriptor: fd = %d", + local_fd); + error_code_ret = LTTNG_ERR_EVENT_NOTIFIER_GROUP_NOTIFICATION_FD; + goto error; + } + + DBG("Created kernel notifier group notification file descriptor: fd = %d", + local_fd); + error_code_ret = LTTNG_OK; + *event_notifier_group_notification_fd = local_fd; + local_fd = -1; + +error: + if (local_fd >= 0) { + ret = close(local_fd); + if (ret) { + PERROR("Failed to close kernel event notifier group notification file descriptor: fd = %d", + local_fd); + } + } + + return error_code_ret; +} + +enum lttng_error_code kernel_destroy_event_notifier_group_notification_fd( + int event_notifier_group_notification_fd) +{ + enum lttng_error_code ret_code = LTTNG_OK; + + DBG("Closing event notifier group notification file descriptor: fd = %d", + event_notifier_group_notification_fd); + if (event_notifier_group_notification_fd >= 0) { + const int ret = close(event_notifier_group_notification_fd); + if (ret) { + PERROR("Failed to close event notifier group notification file descriptor: fd = %d", + event_notifier_group_notification_fd); + } + } + + return ret_code; +} + +static +unsigned long hash_trigger(const struct lttng_trigger *trigger) +{ + const struct lttng_condition *condition = + lttng_trigger_get_const_condition(trigger); + + return lttng_condition_hash(condition); +} + +static +int match_trigger(struct cds_lfht_node *node, const void *key) +{ + const struct ltt_kernel_event_notifier_rule *event_notifier_rule; + const struct lttng_trigger *trigger = key; + + event_notifier_rule = caa_container_of(node, + const struct ltt_kernel_event_notifier_rule, ht_node); + + return lttng_trigger_is_equal(trigger, event_notifier_rule->trigger); +} + +static enum lttng_error_code kernel_create_event_notifier_rule( + struct lttng_trigger *trigger, + const struct lttng_credentials *creds, uint64_t token) +{ + int err, fd, ret = 0; + enum lttng_error_code error_code_ret; + enum lttng_condition_status condition_status; + enum lttng_condition_type condition_type; + enum lttng_event_rule_type event_rule_type; + struct ltt_kernel_event_notifier_rule *event_notifier_rule; + struct lttng_kernel_abi_event_notifier kernel_event_notifier = {}; + unsigned int capture_bytecode_count = 0, i; + const struct lttng_condition *condition = NULL; + const struct lttng_event_rule *event_rule = NULL; + enum lttng_condition_status cond_status; + + LTTNG_ASSERT(trigger); + + condition = lttng_trigger_get_const_condition(trigger); + LTTNG_ASSERT(condition); + + condition_type = lttng_condition_get_type(condition); + LTTNG_ASSERT(condition_type == LTTNG_CONDITION_TYPE_EVENT_RULE_MATCHES); + + /* Does not acquire a reference. */ + condition_status = lttng_condition_event_rule_matches_get_rule( + condition, &event_rule); + LTTNG_ASSERT(condition_status == LTTNG_CONDITION_STATUS_OK); + LTTNG_ASSERT(event_rule); + + event_rule_type = lttng_event_rule_get_type(event_rule); + LTTNG_ASSERT(event_rule_type != LTTNG_EVENT_RULE_TYPE_UNKNOWN); + + error_code_ret = trace_kernel_create_event_notifier_rule(trigger, token, + lttng_condition_event_rule_matches_get_error_counter_index( + condition), + &event_notifier_rule); + if (error_code_ret != LTTNG_OK) { + goto error; + } + + error_code_ret = trace_kernel_init_event_notifier_from_event_rule( + event_rule, &kernel_event_notifier); + if (error_code_ret != LTTNG_OK) { + goto free_event; + } + + kernel_event_notifier.event.token = event_notifier_rule->token; + kernel_event_notifier.error_counter_idx = + lttng_condition_event_rule_matches_get_error_counter_index( + condition); + + fd = kernctl_create_event_notifier( + kernel_tracer_event_notifier_group_fd, + &kernel_event_notifier); + if (fd < 0) { + switch (-fd) { + case EEXIST: + error_code_ret = LTTNG_ERR_KERN_EVENT_EXIST; + break; + case ENOSYS: + WARN("Failed to create kernel event notifier: not notifier type not implemented"); + error_code_ret = LTTNG_ERR_KERN_EVENT_ENOSYS; + break; + case ENOENT: + WARN("Failed to create kernel event notifier: not found: name = '%s'", + kernel_event_notifier.event.name); + error_code_ret = LTTNG_ERR_KERN_ENABLE_FAIL; + break; + default: + PERROR("Failed to create kernel event notifier: error code = %d, name = '%s'", + fd, kernel_event_notifier.event.name); + error_code_ret = LTTNG_ERR_KERN_ENABLE_FAIL; + } + goto free_event; + } + + event_notifier_rule->fd = fd; + /* Prevent fd duplication after execlp(). */ + err = fcntl(event_notifier_rule->fd, F_SETFD, FD_CLOEXEC); + if (err < 0) { + PERROR("Failed to set FD_CLOEXEC on kernel event notifier file descriptor: fd = %d", + fd); + error_code_ret = LTTNG_ERR_FATAL; + goto set_cloexec_error; + } + + if (event_notifier_rule->filter) { + err = kernctl_filter(event_notifier_rule->fd, event_notifier_rule->filter); + if (err < 0) { + switch (-err) { + case ENOMEM: + error_code_ret = LTTNG_ERR_FILTER_NOMEM; + break; + default: + error_code_ret = LTTNG_ERR_FILTER_INVAL; + break; + } + goto filter_error; + } + } + + if (lttng_event_rule_get_type(event_rule) == + LTTNG_EVENT_RULE_TYPE_KERNEL_UPROBE) { + ret = userspace_probe_event_rule_add_callsites( + event_rule, creds, event_notifier_rule->fd); + if (ret) { + error_code_ret = LTTNG_ERR_KERN_ENABLE_FAIL; + goto add_callsite_error; + } + } + + /* Set the capture bytecode if any. */ + cond_status = lttng_condition_event_rule_matches_get_capture_descriptor_count( + condition, &capture_bytecode_count); + LTTNG_ASSERT(cond_status == LTTNG_CONDITION_STATUS_OK); + + for (i = 0; i < capture_bytecode_count; i++) { + const struct lttng_bytecode *capture_bytecode = + lttng_condition_event_rule_matches_get_capture_bytecode_at_index( + condition, i); + + if (capture_bytecode == NULL) { + ERR("Unexpected NULL capture bytecode on condition"); + error_code_ret = LTTNG_ERR_KERN_ENABLE_FAIL; + goto capture_error; + } + + ret = kernctl_capture(event_notifier_rule->fd, capture_bytecode); + if (ret < 0) { + ERR("Failed to set capture bytecode on event notifier rule fd: fd = %d", + event_notifier_rule->fd); + error_code_ret = LTTNG_ERR_KERN_ENABLE_FAIL; + goto capture_error; + } + } + + err = kernctl_enable(event_notifier_rule->fd); + if (err < 0) { + switch (-err) { + case EEXIST: + error_code_ret = LTTNG_ERR_KERN_EVENT_EXIST; + break; + default: + PERROR("enable kernel event notifier"); + error_code_ret = LTTNG_ERR_KERN_ENABLE_FAIL; + break; + } + goto enable_error; + } + + /* Add trigger to kernel token mapping in the hash table. */ + rcu_read_lock(); + cds_lfht_add(kernel_token_to_event_notifier_rule_ht, hash_trigger(trigger), + &event_notifier_rule->ht_node); + rcu_read_unlock(); + + DBG("Created kernel event notifier: name = '%s', fd = %d", + kernel_event_notifier.event.name, + event_notifier_rule->fd); + + return LTTNG_OK; + +capture_error: +add_callsite_error: +enable_error: +set_cloexec_error: +filter_error: + { + const int close_ret = close(event_notifier_rule->fd); + + if (close_ret) { + PERROR("Failed to close kernel event notifier file descriptor: fd = %d", + event_notifier_rule->fd); + } + } +free_event: + free(event_notifier_rule); +error: + return error_code_ret; +} + +enum lttng_error_code kernel_register_event_notifier( + struct lttng_trigger *trigger, + const struct lttng_credentials *cmd_creds) +{ + enum lttng_error_code ret; + enum lttng_condition_status status; + enum lttng_domain_type domain_type; + const struct lttng_event_rule *event_rule; + const struct lttng_condition *const condition = + lttng_trigger_get_const_condition(trigger); + const uint64_t token = lttng_trigger_get_tracer_token(trigger); + + LTTNG_ASSERT(condition); + + /* Does not acquire a reference to the event rule. */ + status = lttng_condition_event_rule_matches_get_rule( + condition, &event_rule); + LTTNG_ASSERT(status == LTTNG_CONDITION_STATUS_OK); + + domain_type = lttng_event_rule_get_domain_type(event_rule); + LTTNG_ASSERT(domain_type == LTTNG_DOMAIN_KERNEL); + + ret = kernel_create_event_notifier_rule(trigger, cmd_creds, token); + if (ret != LTTNG_OK) { + ERR("Failed to create kernel event notifier rule"); + } + + return ret; +} + +enum lttng_error_code kernel_unregister_event_notifier( + const struct lttng_trigger *trigger) +{ + struct ltt_kernel_event_notifier_rule *token_event_rule_element; + struct cds_lfht_node *node; + struct cds_lfht_iter iter; + enum lttng_error_code error_code_ret; + int ret; + + rcu_read_lock(); + + cds_lfht_lookup(kernel_token_to_event_notifier_rule_ht, + hash_trigger(trigger), match_trigger, trigger, &iter); + + node = cds_lfht_iter_get_node(&iter); + if (!node) { + error_code_ret = LTTNG_ERR_TRIGGER_NOT_FOUND; + goto error; + } + + token_event_rule_element = caa_container_of(node, + struct ltt_kernel_event_notifier_rule, ht_node); + + ret = kernel_disable_event_notifier_rule(token_event_rule_element); + if (ret) { + error_code_ret = LTTNG_ERR_FATAL; + goto error; + } + + trace_kernel_destroy_event_notifier_rule(token_event_rule_element); + error_code_ret = LTTNG_OK; + +error: + rcu_read_unlock(); + + return error_code_ret; +} + +int kernel_get_notification_fd(void) +{ + return kernel_tracer_event_notifier_group_notification_fd; +}