X-Git-Url: http://git.lttng.org/?a=blobdiff_plain;f=src%2Flttng-abi.c;h=6eb838752111ab207fe94dca18ece53fc85b0161;hb=21f58fb7e62071dae3ddf13817631749bcab4e00;hp=3bc2d1fb7c3df1a979ef1992f14554079dbddff3;hpb=a3fcd49911634cdfe26916b0f5983849debaea0b;p=lttng-modules.git diff --git a/src/lttng-abi.c b/src/lttng-abi.c index 3bc2d1fb..6eb83875 100644 --- a/src/lttng-abi.c +++ b/src/lttng-abi.c @@ -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]", + <tng_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(¬if_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, + <tng_event_notifier_group_notif_fops, + "[lttng_event_notifier_stream]"); + if (ret < 0) + goto fd_error; + + return ret; + +fd_error: + atomic_long_dec(¬if_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]", + <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_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 *