Implement event notifier send notification
[lttng-modules.git] / src / lttng-abi.c
index 3bc2d1fb7c3df1a979ef1992f14554079dbddff3..6eb838752111ab207fe94dca18ece53fc85b0161 100644 (file)
@@ -59,6 +59,7 @@ static const struct file_operations lttng_proc_ops;
 #endif
 
 static const struct file_operations lttng_session_fops;
+static const struct file_operations lttng_event_notifier_group_fops;
 static const struct file_operations lttng_channel_fops;
 static const struct file_operations lttng_metadata_fops;
 static const struct file_operations lttng_event_fops;
@@ -105,6 +106,52 @@ fd_error:
        return ret;
 }
 
+void event_notifier_send_notification_work_wakeup(struct irq_work *entry)
+{
+       struct lttng_event_notifier_group *event_notifier_group =
+                       container_of(entry, struct lttng_event_notifier_group,
+                                       wakeup_pending);
+       wake_up_interruptible(&event_notifier_group->read_wait);
+}
+
+static
+int lttng_abi_create_event_notifier_group(void)
+{
+       struct lttng_event_notifier_group *event_notifier_group;
+       struct file *event_notifier_group_file;
+       int event_notifier_group_fd, ret;
+
+       event_notifier_group = lttng_event_notifier_group_create();
+       if (!event_notifier_group)
+               return -ENOMEM;
+
+       event_notifier_group_fd = lttng_get_unused_fd();
+       if (event_notifier_group_fd < 0) {
+               ret = event_notifier_group_fd;
+               goto fd_error;
+       }
+       event_notifier_group_file = anon_inode_getfile("[lttng_event_notifier_group]",
+                                         &lttng_event_notifier_group_fops,
+                                         event_notifier_group, O_RDWR);
+       if (IS_ERR(event_notifier_group_file)) {
+               ret = PTR_ERR(event_notifier_group_file);
+               goto file_error;
+       }
+
+       event_notifier_group->file = event_notifier_group_file;
+       init_waitqueue_head(&event_notifier_group->read_wait);
+       init_irq_work(&event_notifier_group->wakeup_pending,
+                     event_notifier_send_notification_work_wakeup);
+       fd_install(event_notifier_group_fd, event_notifier_group_file);
+       return event_notifier_group_fd;
+
+file_error:
+       put_unused_fd(event_notifier_group_fd);
+fd_error:
+       lttng_event_notifier_group_destroy(event_notifier_group);
+       return ret;
+}
+
 static
 int lttng_abi_tracepoint_list(void)
 {
@@ -306,6 +353,8 @@ long lttng_abi_add_context(struct file *file,
  *             Returns after all previously running probes have completed
  *     LTTNG_KERNEL_TRACER_ABI_VERSION
  *             Returns the LTTng kernel tracer ABI version
+ *     LTTNG_KERNEL_EVENT_NOTIFIER_GROUP_CREATE
+ *             Returns a LTTng event notifier group file descriptor
  *
  * The returned session will be deleted when its file descriptor is closed.
  */
@@ -316,6 +365,8 @@ long lttng_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
        case LTTNG_KERNEL_OLD_SESSION:
        case LTTNG_KERNEL_SESSION:
                return lttng_abi_create_session();
+       case LTTNG_KERNEL_EVENT_NOTIFIER_GROUP_CREATE:
+               return lttng_abi_create_event_notifier_group();
        case LTTNG_KERNEL_OLD_TRACER_VERSION:
        {
                struct lttng_kernel_tracer_version v;
@@ -764,6 +815,10 @@ static const struct file_operations lttng_session_fops = {
 #endif
 };
 
+static const struct file_operations lttng_event_notifier_group_notif_fops = {
+       .owner = THIS_MODULE,
+};
+
 /**
  *     lttng_metadata_ring_buffer_poll - LTTng ring buffer poll file operation
  *     @filp: the file
@@ -1267,6 +1322,41 @@ nomem:
        return ret;
 }
 
+static
+int lttng_abi_open_event_notifier_group_stream(struct file *notif_file)
+{
+       struct lttng_event_notifier_group *event_notifier_group = notif_file->private_data;
+       struct channel *chan = event_notifier_group->chan;
+       struct lib_ring_buffer *buf;
+       int ret;
+       void *stream_priv;
+
+       buf = event_notifier_group->ops->buffer_read_open(chan);
+       if (!buf)
+               return -ENOENT;
+
+       /* The event_notifier notification fd holds a reference on the event_notifier group */
+       if (!atomic_long_add_unless(&notif_file->f_count, 1, LONG_MAX)) {
+               ret = -EOVERFLOW;
+               goto refcount_error;
+       }
+       event_notifier_group->buf = buf;
+       stream_priv = event_notifier_group;
+       ret = lttng_abi_create_stream_fd(notif_file, stream_priv,
+                       &lttng_event_notifier_group_notif_fops,
+                       "[lttng_event_notifier_stream]");
+       if (ret < 0)
+               goto fd_error;
+
+       return ret;
+
+fd_error:
+       atomic_long_dec(&notif_file->f_count);
+refcount_error:
+       event_notifier_group->ops->buffer_read_close(buf);
+       return ret;
+}
+
 static
 int lttng_abi_validate_event_param(struct lttng_kernel_event *event_param)
 {
@@ -1398,6 +1488,233 @@ 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_GROUP_NOTIFICATION_FD:
+       {
+               return lttng_abi_open_event_notifier_group_stream(file);
+       }
+       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;
+       }
+       return 0;
+}
+
+static
+int lttng_event_notifier_group_release(struct inode *inode, struct file *file)
+{
+       struct lttng_event_notifier_group *event_notifier_group =
+                       file->private_data;
+
+       if (event_notifier_group)
+               lttng_event_notifier_group_destroy(event_notifier_group);
+       return 0;
+}
+
+static const struct file_operations lttng_event_notifier_group_fops = {
+       .owner = THIS_MODULE,
+       .release = lttng_event_notifier_group_release,
+       .unlocked_ioctl = lttng_event_notifier_group_ioctl,
+#ifdef CONFIG_COMPAT
+       .compat_ioctl = lttng_event_notifier_group_ioctl,
+#endif
+};
+
 /**
  *     lttng_channel_ioctl - lttng syscall through ioctl
  *
This page took 0.030039 seconds and 4 git commands to generate.