From: Francis Deslauriers Date: Wed, 5 Feb 2020 19:34:36 +0000 (-0500) Subject: Add event notifier and event notifier enabler X-Git-Tag: v2.13.0-rc1~141 X-Git-Url: http://git.lttng.org/?p=lttng-modules.git;a=commitdiff_plain;h=dffef45d53ab388fd08c19e8865b245b40ca9dff Add event notifier and event notifier enabler Idea ==== The purpose of the event notifiers is to allow the session daemon to react to events in the tracer. For example, the user will be able to start or stop tracing on a session when a specific tracepoint is fired. An event notifier is really similar to a regular event. The main difference is that when the tracepoint is fired the action of the event notifier is to notify the session daemon about it. This mechanism will use a special purpose ring buffer to expose these notifications to userspace. Unlike regular events, there are no claim on the timeliness of such notifications. Implementation ============== This commit adds structures and functions related to event notifiers mimicking what we currently do with regular events. Signed-off-by: Francis Deslauriers Signed-off-by: Mathieu Desnoyers Change-Id: I962e6c7051693d6e4a79f89758f8bf1ebda6c148 --- diff --git a/include/lttng/abi.h b/include/lttng/abi.h index 8ef9c112..03e07ae9 100644 --- a/include/lttng/abi.h +++ b/include/lttng/abi.h @@ -137,6 +137,13 @@ struct lttng_kernel_event { } u; } __attribute__((packed)); +#define LTTNG_KERNEL_EVENT_NOTIFIER_PADDING1 16 +struct lttng_kernel_event_notifier { + struct lttng_kernel_event event; + + char padding[LTTNG_KERNEL_EVENT_NOTIFIER_PADDING1]; +} __attribute__((packed)); + struct lttng_kernel_tracer_version { uint32_t major; uint32_t minor; @@ -307,12 +314,12 @@ struct lttng_kernel_tracker_args { #define LTTNG_KERNEL_CONTEXT \ _IOW(0xF6, 0x71, struct lttng_kernel_context) -/* Event, Channel and Session ioctl */ +/* Event, Event notifier, Channel and Session ioctl */ /* lttng/abi-old.h reserve 0x80 and 0x81. */ #define LTTNG_KERNEL_ENABLE _IO(0xF6, 0x82) #define LTTNG_KERNEL_DISABLE _IO(0xF6, 0x83) -/* Event FD ioctl */ +/* Event and Event notifier FD ioctl */ #define LTTNG_KERNEL_FILTER _IO(0xF6, 0x90) #define LTTNG_KERNEL_ADD_CALLSITE _IO(0xF6, 0x91) @@ -324,6 +331,10 @@ struct lttng_kernel_tracker_args { #define LTTNG_KERNEL_SESSION_UNTRACK_ID \ _IOR(0xF6, 0xA2, struct lttng_kernel_tracker_args) +/* Event notifier group file descriptor ioctl */ +#define LTTNG_KERNEL_EVENT_NOTIFIER_CREATE \ + _IOW(0xF6, 0xB0, struct lttng_kernel_event_notifier) + /* * LTTng-specific ioctls for the lib ringbuffer. * diff --git a/include/lttng/events.h b/include/lttng/events.h index 1e84000d..e32480a9 100644 --- a/include/lttng/events.h +++ b/include/lttng/events.h @@ -327,6 +327,30 @@ struct lttng_event { int has_enablers_without_bytecode; }; +// FIXME: Really similar to lttng_event above. Could those be merged ? +struct lttng_event_notifier { + enum lttng_event_type evtype; /* First field. */ + uint64_t user_token; + int enabled; + int registered; /* has reg'd tracepoint probe */ + const struct lttng_event_desc *desc; + void *filter; + struct list_head list; /* event_notifier list in event_notifier group */ + + enum lttng_kernel_instrumentation instrumentation; + union { + } u; + + /* Backward references: list of lttng_enabler_ref (ref to enablers) */ + struct list_head enablers_ref_head; + struct hlist_node hlist; /* session ht of event_notifiers */ + /* list of struct lttng_bytecode_runtime, sorted by seqnum */ + struct list_head bytecode_runtime_head; + int has_enablers_without_bytecode; + + struct lttng_event_notifier_group *group; /* Weak ref */ +}; + enum lttng_enabler_format_type { LTTNG_ENABLER_FORMAT_STAR_GLOB, LTTNG_ENABLER_FORMAT_NAME, @@ -346,6 +370,8 @@ struct lttng_enabler { struct lttng_kernel_event event_param; unsigned int enabled:1; + + uint64_t user_token; /* User-provided token. */ }; struct lttng_event_enabler { @@ -359,6 +385,12 @@ struct lttng_event_enabler { struct lttng_ctx *ctx; }; +struct lttng_event_notifier_enabler { + struct lttng_enabler base; + struct list_head node; /* List of event_notifier enablers */ + struct lttng_event_notifier_group *group; +}; + static inline struct lttng_enabler *lttng_event_enabler_as_enabler( struct lttng_event_enabler *event_enabler) @@ -366,6 +398,12 @@ struct lttng_enabler *lttng_event_enabler_as_enabler( return &event_enabler->base; } +static inline +struct lttng_enabler *lttng_event_notifier_enabler_as_enabler( + struct lttng_event_notifier_enabler *event_notifier_enabler) +{ + return &event_notifier_enabler->base; +} struct lttng_channel_ops { struct channel *(*channel_create)(const char *name, @@ -446,6 +484,13 @@ struct lttng_event_ht { struct hlist_head table[LTTNG_EVENT_HT_SIZE]; }; +#define LTTNG_EVENT_NOTIFIER_HT_BITS 12 +#define LTTNG_EVENT_NOTIFIER_HT_SIZE (1U << LTTNG_EVENT_NOTIFIER_HT_BITS) + +struct lttng_event_notifier_ht { + struct hlist_head table[LTTNG_EVENT_NOTIFIER_HT_SIZE]; +}; + struct lttng_channel { unsigned int id; struct channel *chan; /* Channel buffers */ @@ -560,6 +605,9 @@ struct lttng_session { struct lttng_event_notifier_group { struct file *file; /* File associated to event notifier group */ struct list_head node; /* event notifier group list */ + struct list_head enablers_head; /* List of enablers */ + struct list_head event_notifiers_head; /* List of event notifier */ + struct lttng_event_notifier_ht event_notifiers_ht; /* Hash table of event notifiers */ struct lttng_ctx *ctx; /* Contexts for filters. */ struct lttng_channel_ops *ops; struct lttng_transport *transport; @@ -591,6 +639,15 @@ struct lttng_event_enabler *lttng_event_enabler_create( int lttng_event_enabler_enable(struct lttng_event_enabler *event_enabler); int lttng_event_enabler_disable(struct lttng_event_enabler *event_enabler); +struct lttng_event_notifier_enabler *lttng_event_notifier_enabler_create( + struct lttng_event_notifier_group *event_notifier_group, + enum lttng_enabler_format_type format_type, + struct lttng_kernel_event_notifier *event_notifier_param); + +int lttng_event_notifier_enabler_enable( + struct lttng_event_notifier_enabler *event_notifier_enabler); +int lttng_event_notifier_enabler_disable( + struct lttng_event_notifier_enabler *event_notifier_enabler); int lttng_fix_pending_events(void); int lttng_session_active(void); @@ -635,6 +692,21 @@ struct lttng_event *lttng_event_compat_old_create(struct lttng_channel *chan, void *filter, const struct lttng_event_desc *internal_desc); +struct lttng_event_notifier *lttng_event_notifier_create( + const struct lttng_event_desc *event_notifier_desc, + uint64_t id, + struct lttng_event_notifier_group *event_notifier_group, + struct lttng_kernel_event_notifier *event_notifier_param, + void *filter, + enum lttng_kernel_instrumentation itype); +struct lttng_event_notifier *_lttng_event_notifier_create( + const struct lttng_event_desc *event_notifier_desc, + uint64_t id, + struct lttng_event_notifier_group *event_notifier_group, + struct lttng_kernel_event_notifier *event_notifier_param, + void *filter, + enum lttng_kernel_instrumentation itype); + int lttng_channel_enable(struct lttng_channel *channel); int lttng_channel_disable(struct lttng_channel *channel); int lttng_event_enable(struct lttng_event *event); @@ -720,11 +792,15 @@ static inline long lttng_channel_syscall_mask(struct lttng_channel *channel, { return -ENOSYS; } + #endif void lttng_filter_sync_state(struct lttng_bytecode_runtime *runtime); int lttng_event_enabler_attach_bytecode(struct lttng_event_enabler *event_enabler, struct lttng_kernel_filter_bytecode __user *bytecode); +int lttng_event_notifier_enabler_attach_bytecode( + struct lttng_event_notifier_enabler *event_notifier_enabler, + struct lttng_kernel_filter_bytecode __user *bytecode); void lttng_enabler_link_bytecode(const struct lttng_event_desc *event_desc, struct lttng_ctx *ctx, diff --git a/src/lttng-abi.c b/src/lttng-abi.c index 00d0a34e..07c520f0 100644 --- a/src/lttng-abi.c +++ b/src/lttng-abi.c @@ -1438,11 +1438,203 @@ fd_error: return ret; } +static +long lttng_event_notifier_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + struct lttng_event_notifier_enabler *event_notifier_enabler; + enum lttng_event_type *evtype = file->private_data; + + switch (cmd) { + case LTTNG_KERNEL_ENABLE: + switch (*evtype) { + case LTTNG_TYPE_EVENT: + return -EINVAL; + case LTTNG_TYPE_ENABLER: + event_notifier_enabler = file->private_data; + return lttng_event_notifier_enabler_enable(event_notifier_enabler); + default: + WARN_ON_ONCE(1); + return -ENOSYS; + } + case LTTNG_KERNEL_DISABLE: + switch (*evtype) { + case LTTNG_TYPE_EVENT: + return -EINVAL; + case LTTNG_TYPE_ENABLER: + event_notifier_enabler = file->private_data; + return lttng_event_notifier_enabler_disable(event_notifier_enabler); + default: + WARN_ON_ONCE(1); + return -ENOSYS; + } + case LTTNG_KERNEL_FILTER: + switch (*evtype) { + case LTTNG_TYPE_EVENT: + return -EINVAL; + case LTTNG_TYPE_ENABLER: + event_notifier_enabler = file->private_data; + return lttng_event_notifier_enabler_attach_bytecode(event_notifier_enabler, + (struct lttng_kernel_filter_bytecode __user *) arg); + default: + WARN_ON_ONCE(1); + return -ENOSYS; + } + default: + return -ENOIOCTLCMD; + } +} + +static +int lttng_event_notifier_release(struct inode *inode, struct file *file) +{ + struct lttng_event_notifier *event_notifier; + struct lttng_event_notifier_enabler *event_notifier_enabler; + enum lttng_event_type *evtype = file->private_data; + + if (!evtype) + return 0; + + switch (*evtype) { + case LTTNG_TYPE_EVENT: + event_notifier = file->private_data; + if (event_notifier) + fput(event_notifier->group->file); + break; + case LTTNG_TYPE_ENABLER: + event_notifier_enabler = file->private_data; + if (event_notifier_enabler) + fput(event_notifier_enabler->group->file); + break; + default: + WARN_ON_ONCE(1); + break; + } + + return 0; +} + +static const struct file_operations lttng_event_notifier_fops = { + .owner = THIS_MODULE, + .release = lttng_event_notifier_release, + .unlocked_ioctl = lttng_event_notifier_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = lttng_event_notifier_ioctl, +#endif +}; + +static +int lttng_abi_create_event_notifier(struct file *event_notifier_group_file, + struct lttng_kernel_event_notifier *event_notifier_param) +{ + struct lttng_event_notifier_group *event_notifier_group = + event_notifier_group_file->private_data; + int event_notifier_fd, ret; + struct file *event_notifier_file; + void *priv; + + switch (event_notifier_param->event.instrumentation) { + case LTTNG_KERNEL_TRACEPOINT: + case LTTNG_KERNEL_KPROBE: + case LTTNG_KERNEL_UPROBE: + case LTTNG_KERNEL_KRETPROBE: + case LTTNG_KERNEL_FUNCTION: + case LTTNG_KERNEL_NOOP: + case LTTNG_KERNEL_SYSCALL: + default: + ret = -EINVAL; + goto inval_instr; + } + + event_notifier_param->event.name[LTTNG_KERNEL_SYM_NAME_LEN - 1] = '\0'; + + event_notifier_fd = lttng_get_unused_fd(); + if (event_notifier_fd < 0) { + ret = event_notifier_fd; + goto fd_error; + } + + event_notifier_file = anon_inode_getfile("[lttng_event_notifier]", + <tng_event_notifier_fops, + NULL, O_RDWR); + if (IS_ERR(event_notifier_file)) { + ret = PTR_ERR(event_notifier_file); + goto file_error; + } + + /* The event notifier holds a reference on the event notifier group. */ + if (!atomic_long_add_unless(&event_notifier_group_file->f_count, 1, LONG_MAX)) { + ret = -EOVERFLOW; + goto refcount_error; + } + + if (event_notifier_param->event.instrumentation == LTTNG_KERNEL_TRACEPOINT + || event_notifier_param->event.instrumentation == LTTNG_KERNEL_SYSCALL) { + struct lttng_event_notifier_enabler *enabler; + + if (strutils_is_star_glob_pattern(event_notifier_param->event.name)) { + /* + * If the event name is a star globbing pattern, + * we create the special star globbing enabler. + */ + enabler = lttng_event_notifier_enabler_create( + event_notifier_group, + LTTNG_ENABLER_FORMAT_STAR_GLOB, + event_notifier_param); + } else { + enabler = lttng_event_notifier_enabler_create( + event_notifier_group, + LTTNG_ENABLER_FORMAT_NAME, + event_notifier_param); + } + priv = enabler; + } else { + struct lttng_event_notifier *event_notifier; + + /* + * We tolerate no failure path after event notifier creation. + * It will stay invariant for the rest of the session. + */ + event_notifier = lttng_event_notifier_create(NULL, + event_notifier_param->event.token, event_notifier_group, + event_notifier_param, NULL, + event_notifier_param->event.instrumentation); + WARN_ON_ONCE(!event_notifier); + if (IS_ERR(event_notifier)) { + ret = PTR_ERR(event_notifier); + goto event_notifier_error; + } + priv = event_notifier; + } + event_notifier_file->private_data = priv; + fd_install(event_notifier_fd, event_notifier_file); + return event_notifier_fd; + +event_notifier_error: + atomic_long_dec(&event_notifier_group_file->f_count); +refcount_error: + fput(event_notifier_file); +file_error: + put_unused_fd(event_notifier_fd); +fd_error: +inval_instr: + return ret; +} + static long lttng_event_notifier_group_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { switch (cmd) { + case LTTNG_KERNEL_EVENT_NOTIFIER_CREATE: + { + struct lttng_kernel_event_notifier uevent_notifier_param; + + if (copy_from_user(&uevent_notifier_param, + (struct lttng_kernel_event_notifier __user *) arg, + sizeof(uevent_notifier_param))) + return -EFAULT; + return lttng_abi_create_event_notifier(file, &uevent_notifier_param); + } default: return -ENOIOCTLCMD; } diff --git a/src/lttng-events.c b/src/lttng-events.c index 246c7102..092cbb88 100644 --- a/src/lttng-events.c +++ b/src/lttng-events.c @@ -55,14 +55,18 @@ static LIST_HEAD(lttng_transport_list); */ static DEFINE_MUTEX(sessions_mutex); static struct kmem_cache *event_cache; +static struct kmem_cache *event_notifier_cache; static void lttng_session_lazy_sync_event_enablers(struct lttng_session *session); static void lttng_session_sync_event_enablers(struct lttng_session *session); static void lttng_event_enabler_destroy(struct lttng_event_enabler *event_enabler); +static void lttng_event_notifier_enabler_destroy(struct lttng_event_notifier_enabler *event_notifier_enabler); static void _lttng_event_destroy(struct lttng_event *event); +static void _lttng_event_notifier_destroy(struct lttng_event_notifier *event_notifier); static void _lttng_channel_destroy(struct lttng_channel *chan); static int _lttng_event_unregister(struct lttng_event *event); +static int _lttng_event_notifier_unregister(struct lttng_event_notifier *event_notifier); static int _lttng_event_metadata_statedump(struct lttng_session *session, struct lttng_channel *chan, @@ -199,6 +203,7 @@ struct lttng_event_notifier_group *lttng_event_notifier_group_create(void) size_t num_subbuf = 16; //TODO unsigned int switch_timer_interval = 0; unsigned int read_timer_interval = 0; + int i; mutex_lock(&sessions_mutex); @@ -232,6 +237,12 @@ struct lttng_event_notifier_group *lttng_event_notifier_group_create(void) goto create_error; event_notifier_group->transport = transport; + + INIT_LIST_HEAD(&event_notifier_group->enablers_head); + INIT_LIST_HEAD(&event_notifier_group->event_notifiers_head); + for (i = 0; i < LTTNG_EVENT_NOTIFIER_HT_SIZE; i++) + INIT_HLIST_HEAD(&event_notifier_group->event_notifiers_ht.table[i]); + list_add(&event_notifier_group->node, &event_notifier_groups); mutex_unlock(&sessions_mutex); @@ -304,15 +315,36 @@ void lttng_session_destroy(struct lttng_session *session) lttng_kvfree(session); } -void lttng_event_notifier_group_destroy(struct lttng_event_notifier_group *event_notifier_group) +void lttng_event_notifier_group_destroy( + struct lttng_event_notifier_group *event_notifier_group) { + struct lttng_event_notifier_enabler *event_notifier_enabler, *tmp_event_notifier_enabler; + struct lttng_event_notifier *event_notifier, *tmpevent_notifier; + int ret; + if (!event_notifier_group) return; mutex_lock(&sessions_mutex); + + list_for_each_entry_safe(event_notifier, tmpevent_notifier, + &event_notifier_group->event_notifiers_head, list) { + ret = _lttng_event_notifier_unregister(event_notifier); + WARN_ON(ret); + } + + list_for_each_entry_safe(event_notifier_enabler, tmp_event_notifier_enabler, + &event_notifier_group->enablers_head, node) + lttng_event_notifier_enabler_destroy(event_notifier_enabler); + + list_for_each_entry_safe(event_notifier, tmpevent_notifier, + &event_notifier_group->event_notifiers_head, list) + _lttng_event_notifier_destroy(event_notifier); + event_notifier_group->ops->channel_destroy(event_notifier_group->chan); module_put(event_notifier_group->transport->owner); list_del(&event_notifier_group->node); + mutex_unlock(&sessions_mutex); lttng_kvfree(event_notifier_group); } @@ -562,6 +594,58 @@ end: return ret; } +int lttng_event_notifier_enable(struct lttng_event_notifier *event_notifier) +{ + int ret = 0; + + mutex_lock(&sessions_mutex); + if (event_notifier->enabled) { + ret = -EEXIST; + goto end; + } + switch (event_notifier->instrumentation) { + case LTTNG_KERNEL_TRACEPOINT: + case LTTNG_KERNEL_SYSCALL: + case LTTNG_KERNEL_KPROBE: + case LTTNG_KERNEL_FUNCTION: + case LTTNG_KERNEL_UPROBE: + case LTTNG_KERNEL_NOOP: + case LTTNG_KERNEL_KRETPROBE: + default: + WARN_ON_ONCE(1); + ret = -EINVAL; + } +end: + mutex_unlock(&sessions_mutex); + return ret; +} + +int lttng_event_notifier_disable(struct lttng_event_notifier *event_notifier) +{ + int ret = 0; + + mutex_lock(&sessions_mutex); + if (!event_notifier->enabled) { + ret = -EEXIST; + goto end; + } + switch (event_notifier->instrumentation) { + case LTTNG_KERNEL_TRACEPOINT: + case LTTNG_KERNEL_SYSCALL: + case LTTNG_KERNEL_KPROBE: + case LTTNG_KERNEL_FUNCTION: + case LTTNG_KERNEL_UPROBE: + case LTTNG_KERNEL_NOOP: + case LTTNG_KERNEL_KRETPROBE: + default: + WARN_ON_ONCE(1); + ret = -EINVAL; + } +end: + mutex_unlock(&sessions_mutex); + return ret; +} + struct lttng_channel *lttng_channel_create(struct lttng_session *session, const char *transport_name, void *buf_addr, @@ -893,6 +977,94 @@ full: return ERR_PTR(ret); } +struct lttng_event_notifier *_lttng_event_notifier_create( + const struct lttng_event_desc *event_desc, + uint64_t token, struct lttng_event_notifier_group *event_notifier_group, + struct lttng_kernel_event_notifier *event_notifier_param, + void *filter, enum lttng_kernel_instrumentation itype) +{ + struct lttng_event_notifier *event_notifier; + const char *event_name; + struct hlist_head *head; + int ret; + + switch (itype) { + case LTTNG_KERNEL_TRACEPOINT: + case LTTNG_KERNEL_KPROBE: + case LTTNG_KERNEL_UPROBE: + case LTTNG_KERNEL_KRETPROBE: + case LTTNG_KERNEL_FUNCTION: + case LTTNG_KERNEL_NOOP: + case LTTNG_KERNEL_SYSCALL: + default: + WARN_ON_ONCE(1); + ret = -EINVAL; + goto type_error; + } + + head = utils_borrow_hash_table_bucket(event_notifier_group->event_notifiers_ht.table, + LTTNG_EVENT_NOTIFIER_HT_SIZE, event_name); + lttng_hlist_for_each_entry(event_notifier, head, hlist) { + WARN_ON_ONCE(!event_notifier->desc); + if (!strncmp(event_notifier->desc->name, event_name, + LTTNG_KERNEL_SYM_NAME_LEN - 1) + && event_notifier_group == event_notifier->group + && token == event_notifier->user_token) { + ret = -EEXIST; + goto exist; + } + } + + event_notifier = kmem_cache_zalloc(event_notifier_cache, GFP_KERNEL); + if (!event_notifier) { + ret = -ENOMEM; + goto cache_error; + } + event_notifier->group = event_notifier_group; + event_notifier->user_token = token; + event_notifier->filter = filter; + event_notifier->instrumentation = itype; + event_notifier->evtype = LTTNG_TYPE_EVENT; + INIT_LIST_HEAD(&event_notifier->bytecode_runtime_head); + INIT_LIST_HEAD(&event_notifier->enablers_ref_head); + + switch (itype) { + case LTTNG_KERNEL_TRACEPOINT: + /* Event will be enabled by enabler sync. */ + event_notifier->enabled = 0; + event_notifier->registered = 0; + event_notifier->desc = lttng_event_desc_get(event_name); + if (!event_notifier->desc) { + ret = -ENOENT; + goto register_error; + } + /* Populate lttng_event_notifier structure before event registration. */ + smp_wmb(); + break; + case LTTNG_KERNEL_KPROBE: + case LTTNG_KERNEL_UPROBE: + case LTTNG_KERNEL_KRETPROBE: + case LTTNG_KERNEL_FUNCTION: + case LTTNG_KERNEL_NOOP: + case LTTNG_KERNEL_SYSCALL: + default: + WARN_ON_ONCE(1); + ret = -EINVAL; + goto register_error; + } + + list_add(&event_notifier->list, &event_notifier_group->event_notifiers_head); + hlist_add_head(&event_notifier->hlist, head); + return event_notifier; + +register_error: + kmem_cache_free(event_notifier_cache, event_notifier); +cache_error: +exist: +type_error: + return ERR_PTR(ret); +} + struct lttng_event *lttng_event_create(struct lttng_channel *chan, struct lttng_kernel_event *event_param, void *filter, @@ -908,6 +1080,21 @@ struct lttng_event *lttng_event_create(struct lttng_channel *chan, return event; } +struct lttng_event_notifier *lttng_event_notifier_create( + const struct lttng_event_desc *event_desc, + uint64_t id, struct lttng_event_notifier_group *event_notifier_group, + struct lttng_kernel_event_notifier *event_notifier_param, + void *filter, enum lttng_kernel_instrumentation itype) +{ + struct lttng_event_notifier *event_notifier; + + mutex_lock(&sessions_mutex); + event_notifier = _lttng_event_notifier_create(event_desc, id, + event_notifier_group, event_notifier_param, filter, itype); + mutex_unlock(&sessions_mutex); + return event_notifier; +} + /* Only used for tracepoints for now. */ static void register_event(struct lttng_event *event) @@ -987,6 +1174,60 @@ int _lttng_event_unregister(struct lttng_event *event) return ret; } +/* Only used for tracepoints for now. */ +static +void __always_unused register_event_notifier( + struct lttng_event_notifier *event_notifier) +{ + const struct lttng_event_desc *desc; + int ret = -EINVAL; + + if (event_notifier->registered) + return; + + desc = event_notifier->desc; + switch (event_notifier->instrumentation) { + case LTTNG_KERNEL_TRACEPOINT: + case LTTNG_KERNEL_SYSCALL: + case LTTNG_KERNEL_KPROBE: + case LTTNG_KERNEL_UPROBE: + case LTTNG_KERNEL_KRETPROBE: + case LTTNG_KERNEL_FUNCTION: + case LTTNG_KERNEL_NOOP: + default: + WARN_ON_ONCE(1); + } + if (!ret) + event_notifier->registered = 1; +} + +static +int _lttng_event_notifier_unregister( + struct lttng_event_notifier *event_notifier) +{ + const struct lttng_event_desc *desc; + int ret = -EINVAL; + + if (!event_notifier->registered) + return 0; + + desc = event_notifier->desc; + switch (event_notifier->instrumentation) { + case LTTNG_KERNEL_TRACEPOINT: + case LTTNG_KERNEL_KPROBE: + case LTTNG_KERNEL_KRETPROBE: + case LTTNG_KERNEL_FUNCTION: + case LTTNG_KERNEL_SYSCALL: + case LTTNG_KERNEL_NOOP: + case LTTNG_KERNEL_UPROBE: + default: + WARN_ON_ONCE(1); + } + if (!ret) + event_notifier->registered = 0; + return ret; +} + /* * Only used internally at session destruction. */ @@ -1021,6 +1262,27 @@ void _lttng_event_destroy(struct lttng_event *event) kmem_cache_free(event_cache, event); } +/* + * Only used internally at session destruction. + */ +static +void _lttng_event_notifier_destroy(struct lttng_event_notifier *event_notifier) +{ + switch (event_notifier->instrumentation) { + case LTTNG_KERNEL_TRACEPOINT: + case LTTNG_KERNEL_KPROBE: + case LTTNG_KERNEL_KRETPROBE: + case LTTNG_KERNEL_FUNCTION: + case LTTNG_KERNEL_NOOP: + case LTTNG_KERNEL_SYSCALL: + case LTTNG_KERNEL_UPROBE: + default: + WARN_ON_ONCE(1); + } + list_del(&event_notifier->list); + kmem_cache_free(event_notifier_cache, event_notifier); +} + struct lttng_id_tracker *get_tracker(struct lttng_session *session, enum tracker_type tracker_type) { @@ -1707,6 +1969,94 @@ void lttng_event_enabler_destroy(struct lttng_event_enabler *event_enabler) kfree(event_enabler); } +struct lttng_event_notifier_enabler *lttng_event_notifier_enabler_create( + struct lttng_event_notifier_group *event_notifier_group, + enum lttng_enabler_format_type format_type, + struct lttng_kernel_event_notifier *event_notifier_param) +{ + struct lttng_event_notifier_enabler *event_notifier_enabler; + + event_notifier_enabler = kzalloc(sizeof(*event_notifier_enabler), GFP_KERNEL); + if (!event_notifier_enabler) + return NULL; + + event_notifier_enabler->base.format_type = format_type; + INIT_LIST_HEAD(&event_notifier_enabler->base.filter_bytecode_head); + + memcpy(&event_notifier_enabler->base.event_param.name, event_notifier_param->event.name, + sizeof(event_notifier_enabler->base.event_param.name)); + event_notifier_enabler->base.event_param.instrumentation = event_notifier_param->event.instrumentation; + event_notifier_enabler->base.evtype = LTTNG_TYPE_ENABLER; + + event_notifier_enabler->base.enabled = 0; + event_notifier_enabler->base.user_token = event_notifier_param->event.token; + event_notifier_enabler->group = event_notifier_group; + + mutex_lock(&sessions_mutex); + list_add(&event_notifier_enabler->node, &event_notifier_enabler->group->enablers_head); + + mutex_unlock(&sessions_mutex); + + return event_notifier_enabler; +} + +int lttng_event_notifier_enabler_enable( + struct lttng_event_notifier_enabler *event_notifier_enabler) +{ + mutex_lock(&sessions_mutex); + lttng_event_notifier_enabler_as_enabler(event_notifier_enabler)->enabled = 1; + mutex_unlock(&sessions_mutex); + return 0; +} + +int lttng_event_notifier_enabler_disable( + struct lttng_event_notifier_enabler *event_notifier_enabler) +{ + mutex_lock(&sessions_mutex); + lttng_event_notifier_enabler_as_enabler(event_notifier_enabler)->enabled = 0; + mutex_unlock(&sessions_mutex); + return 0; +} + +int lttng_event_notifier_enabler_attach_bytecode( + struct lttng_event_notifier_enabler *event_notifier_enabler, + struct lttng_kernel_filter_bytecode __user *bytecode) +{ + int ret; + + ret = lttng_enabler_attach_bytecode( + lttng_event_notifier_enabler_as_enabler(event_notifier_enabler), + bytecode); + if (ret) + goto error; + + return 0; + +error: + return ret; +} + +int lttng_event_notifier_enabler_attach_context( + struct lttng_event_notifier_enabler *event_notifier_enabler, + struct lttng_kernel_context *context_param) +{ + return -ENOSYS; +} + +static +void lttng_event_notifier_enabler_destroy( + struct lttng_event_notifier_enabler *event_notifier_enabler) +{ + if (!event_notifier_enabler) { + return; + } + + list_del(&event_notifier_enabler->node); + + lttng_enabler_destroy(lttng_event_notifier_enabler_as_enabler(event_notifier_enabler)); + kfree(event_notifier_enabler); +} + /* * lttng_session_sync_event_enablers should be called just before starting a * session. @@ -3202,7 +3552,12 @@ static int __init lttng_events_init(void) event_cache = KMEM_CACHE(lttng_event, 0); if (!event_cache) { ret = -ENOMEM; - goto error_kmem; + goto error_kmem_event; + } + event_notifier_cache = KMEM_CACHE(lttng_event_notifier, 0); + if (!event_notifier_cache) { + ret = -ENOMEM; + goto error_kmem_event_notifier; } ret = lttng_abi_init(); if (ret) @@ -3236,8 +3591,10 @@ error_hotplug: error_logger: lttng_abi_exit(); error_abi: + kmem_cache_destroy(event_notifier_cache); +error_kmem_event_notifier: kmem_cache_destroy(event_cache); -error_kmem: +error_kmem_event: lttng_tracepoint_exit(); error_tp: lttng_context_exit(); @@ -3272,6 +3629,7 @@ static void __exit lttng_events_exit(void) list_for_each_entry_safe(session, tmpsession, &sessions, list) lttng_session_destroy(session); kmem_cache_destroy(event_cache); + kmem_cache_destroy(event_notifier_cache); lttng_tracepoint_exit(); lttng_context_exit(); printk(KERN_NOTICE "LTTng: Unloaded modules v%s.%s.%s%s (%s)%s%s\n",