Add event notifier and event notifier enabler
authorFrancis Deslauriers <francis.deslauriers@efficios.com>
Wed, 5 Feb 2020 19:34:36 +0000 (14:34 -0500)
committerMathieu Desnoyers <mathieu.desnoyers@efficios.com>
Wed, 18 Nov 2020 18:10:11 +0000 (13:10 -0500)
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 <francis.deslauriers@efficios.com>
Signed-off-by: Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
Change-Id: I962e6c7051693d6e4a79f89758f8bf1ebda6c148

include/lttng/abi.h
include/lttng/events.h
src/lttng-abi.c
src/lttng-events.c

index 8ef9c11261eecd6c0078c24ed922f8a972d20d9c..03e07ae937b50e06709629f8564416cf5fde2b0e 100644 (file)
@@ -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.
  *
index 1e84000d45f090589f7e82a3d447f88c806f26d8..e32480a98791286c5ad7f46d59f0b646746e8941 100644 (file)
@@ -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,
index 00d0a34eb7251fc6be3fd25d7d71f8b6604adf21..07c520f00a0c8d2604e0d170186621b35f46e2ae 100644 (file)
@@ -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]",
+                                       &lttng_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;
        }
index 246c71029dcc23d9f4dd602a645a5b069b0abcd1..092cbb887a97899e4a6fd4be4c50ede12c65d97d 100644 (file)
@@ -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",
This page took 0.036378 seconds and 4 git commands to generate.