+ int ret;
+ struct lttng_metadata_stream *stream = filp->private_data;
+ struct lib_ring_buffer *buf = stream->priv;
+ unsigned int rb_cmd;
+ bool coherent;
+
+ if (cmd == RING_BUFFER_GET_NEXT_SUBBUF_METADATA_CHECK)
+ rb_cmd = RING_BUFFER_GET_NEXT_SUBBUF;
+ else
+ rb_cmd = cmd;
+
+ switch (cmd) {
+ case RING_BUFFER_GET_NEXT_SUBBUF:
+ {
+ struct lttng_metadata_stream *stream = filp->private_data;
+ struct lib_ring_buffer *buf = stream->priv;
+ struct channel *chan = buf->backend.chan;
+
+ ret = lttng_metadata_output_channel(stream, chan, NULL);
+ if (ret > 0) {
+ lib_ring_buffer_switch_slow(buf, SWITCH_ACTIVE);
+ ret = 0;
+ } else if (ret < 0)
+ goto err;
+ break;
+ }
+ case RING_BUFFER_GET_SUBBUF:
+ {
+ /*
+ * Random access is not allowed for metadata channel.
+ */
+ return -ENOSYS;
+ }
+ case RING_BUFFER_FLUSH_EMPTY: /* Fall-through. */
+ case RING_BUFFER_FLUSH:
+ {
+ struct lttng_metadata_stream *stream = filp->private_data;
+ struct lib_ring_buffer *buf = stream->priv;
+ struct channel *chan = buf->backend.chan;
+
+ /*
+ * Before doing the actual ring buffer flush, write up to one
+ * packet of metadata in the ring buffer.
+ */
+ ret = lttng_metadata_output_channel(stream, chan, NULL);
+ if (ret < 0)
+ goto err;
+ break;
+ }
+ case RING_BUFFER_GET_METADATA_VERSION:
+ {
+ struct lttng_metadata_stream *stream = filp->private_data;
+
+ return put_u64(stream->version, arg);
+ }
+ case RING_BUFFER_METADATA_CACHE_DUMP:
+ {
+ struct lttng_metadata_stream *stream = filp->private_data;
+
+ return lttng_metadata_cache_dump(stream);
+ }
+ case RING_BUFFER_GET_NEXT_SUBBUF_METADATA_CHECK:
+ {
+ struct lttng_metadata_stream *stream = filp->private_data;
+ struct lib_ring_buffer *buf = stream->priv;
+ struct channel *chan = buf->backend.chan;
+
+ ret = lttng_metadata_output_channel(stream, chan, &coherent);
+ if (ret > 0) {
+ lib_ring_buffer_switch_slow(buf, SWITCH_ACTIVE);
+ ret = 0;
+ } else if (ret < 0) {
+ goto err;
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ /* PUT_SUBBUF is the one from lib ring buffer, unmodified. */
+
+ /* Performing lib ring buffer ioctl after our own. */
+ ret = lib_ring_buffer_compat_ioctl(filp, rb_cmd, arg, buf);
+ if (ret < 0)
+ goto err;
+
+ switch (cmd) {
+ case RING_BUFFER_PUT_NEXT_SUBBUF:
+ {
+ lttng_metadata_ring_buffer_ioctl_put_next_subbuf(filp,
+ cmd, arg);
+ break;
+ }
+ case RING_BUFFER_GET_NEXT_SUBBUF_METADATA_CHECK:
+ {
+ return put_u32(coherent, arg);
+ }
+ default:
+ break;
+ }
+err:
+ return ret;
+}
+#endif
+
+/*
+ * This is not used by anonymous file descriptors. This code is left
+ * there if we ever want to implement an inode with open() operation.
+ */
+static
+int lttng_metadata_ring_buffer_open(struct inode *inode, struct file *file)
+{
+ struct lttng_metadata_stream *stream = inode->i_private;
+ struct lib_ring_buffer *buf = stream->priv;
+
+ file->private_data = buf;
+ /*
+ * Since life-time of metadata cache differs from that of
+ * session, we need to keep our own reference on the transport.
+ */
+ if (!try_module_get(stream->transport->owner)) {
+ printk(KERN_WARNING "LTT : Can't lock transport module.\n");
+ return -EBUSY;
+ }
+ return lib_ring_buffer_open(inode, file, buf);
+}
+
+static
+int lttng_metadata_ring_buffer_release(struct inode *inode, struct file *file)
+{
+ struct lttng_metadata_stream *stream = file->private_data;
+ struct lib_ring_buffer *buf = stream->priv;
+
+ mutex_lock(&stream->metadata_cache->lock);
+ list_del(&stream->list);
+ mutex_unlock(&stream->metadata_cache->lock);
+ kref_put(&stream->metadata_cache->refcount, metadata_cache_destroy);
+ module_put(stream->transport->owner);
+ kfree(stream);
+ return lib_ring_buffer_release(inode, file, buf);
+}
+
+static
+ssize_t lttng_metadata_ring_buffer_splice_read(struct file *in, loff_t *ppos,
+ struct pipe_inode_info *pipe, size_t len,
+ unsigned int flags)
+{
+ struct lttng_metadata_stream *stream = in->private_data;
+ struct lib_ring_buffer *buf = stream->priv;
+
+ return lib_ring_buffer_splice_read(in, ppos, pipe, len,
+ flags, buf);
+}
+
+static
+int lttng_metadata_ring_buffer_mmap(struct file *filp,
+ struct vm_area_struct *vma)
+{
+ struct lttng_metadata_stream *stream = filp->private_data;
+ struct lib_ring_buffer *buf = stream->priv;
+
+ return lib_ring_buffer_mmap(filp, vma, buf);
+}
+
+static
+const struct file_operations lttng_metadata_ring_buffer_file_operations = {
+ .owner = THIS_MODULE,
+ .open = lttng_metadata_ring_buffer_open,
+ .release = lttng_metadata_ring_buffer_release,
+ .poll = lttng_metadata_ring_buffer_poll,
+ .splice_read = lttng_metadata_ring_buffer_splice_read,
+ .mmap = lttng_metadata_ring_buffer_mmap,
+ .unlocked_ioctl = lttng_metadata_ring_buffer_ioctl,
+ .llseek = vfs_lib_ring_buffer_no_llseek,
+#ifdef CONFIG_COMPAT
+ .compat_ioctl = lttng_metadata_ring_buffer_compat_ioctl,
+#endif
+};
+
+static
+int lttng_abi_create_stream_fd(struct file *channel_file, void *stream_priv,
+ const struct file_operations *fops)
+{
+ int stream_fd, ret;
+ struct file *stream_file;
+
+ stream_fd = lttng_get_unused_fd();
+ if (stream_fd < 0) {
+ ret = stream_fd;
+ goto fd_error;
+ }
+ stream_file = anon_inode_getfile("[lttng_stream]", fops,
+ stream_priv, O_RDWR);
+ if (IS_ERR(stream_file)) {
+ ret = PTR_ERR(stream_file);
+ goto file_error;
+ }
+ /*
+ * OPEN_FMODE, called within anon_inode_getfile/alloc_file, don't honor
+ * FMODE_LSEEK, FMODE_PREAD nor FMODE_PWRITE. We need to read from this
+ * file descriptor, so we set FMODE_PREAD here.
+ */
+ stream_file->f_mode |= FMODE_PREAD;
+ fd_install(stream_fd, stream_file);
+ /*
+ * The stream holds a reference to the channel within the generic ring
+ * buffer library, so no need to hold a refcount on the channel and
+ * session files here.
+ */
+ return stream_fd;
+
+file_error:
+ put_unused_fd(stream_fd);
+fd_error:
+ return ret;
+}
+
+static
+int lttng_abi_open_stream(struct file *channel_file)
+{
+ struct lttng_channel *channel = channel_file->private_data;
+ struct lib_ring_buffer *buf;
+ int ret;
+ void *stream_priv;
+
+ buf = channel->ops->buffer_read_open(channel->chan);
+ if (!buf)
+ return -ENOENT;
+
+ stream_priv = buf;
+ ret = lttng_abi_create_stream_fd(channel_file, stream_priv,
+ <tng_stream_ring_buffer_file_operations);
+ if (ret < 0)
+ goto fd_error;
+
+ return ret;
+
+fd_error:
+ channel->ops->buffer_read_close(buf);
+ return ret;
+}
+
+static
+int lttng_abi_open_metadata_stream(struct file *channel_file)
+{
+ struct lttng_channel *channel = channel_file->private_data;
+ struct lttng_session *session = channel->session;
+ struct lib_ring_buffer *buf;
+ int ret;
+ struct lttng_metadata_stream *metadata_stream;
+ void *stream_priv;
+
+ buf = channel->ops->buffer_read_open(channel->chan);
+ if (!buf)
+ return -ENOENT;
+
+ metadata_stream = kzalloc(sizeof(struct lttng_metadata_stream),
+ GFP_KERNEL);
+ if (!metadata_stream) {
+ ret = -ENOMEM;
+ goto nomem;
+ }
+ metadata_stream->metadata_cache = session->metadata_cache;
+ init_waitqueue_head(&metadata_stream->read_wait);
+ metadata_stream->priv = buf;
+ stream_priv = metadata_stream;
+ metadata_stream->transport = channel->transport;
+ /* Initial state is an empty metadata, considered as incoherent. */
+ metadata_stream->coherent = false;
+
+ /*
+ * Since life-time of metadata cache differs from that of
+ * session, we need to keep our own reference on the transport.
+ */
+ if (!try_module_get(metadata_stream->transport->owner)) {
+ printk(KERN_WARNING "LTT : Can't lock transport module.\n");
+ ret = -EINVAL;
+ goto notransport;
+ }
+
+ if (!lttng_kref_get(&session->metadata_cache->refcount)) {
+ ret = -EOVERFLOW;
+ goto kref_error;
+ }
+
+ ret = lttng_abi_create_stream_fd(channel_file, stream_priv,
+ <tng_metadata_ring_buffer_file_operations);
+ if (ret < 0)
+ goto fd_error;
+
+ mutex_lock(&session->metadata_cache->lock);
+ list_add(&metadata_stream->list,
+ &session->metadata_cache->metadata_stream);
+ mutex_unlock(&session->metadata_cache->lock);
+ return ret;
+
+fd_error:
+ kref_put(&session->metadata_cache->refcount, metadata_cache_destroy);
+kref_error:
+ module_put(metadata_stream->transport->owner);
+notransport:
+ kfree(metadata_stream);
+nomem:
+ channel->ops->buffer_read_close(buf);
+ return ret;
+}
+
+static
+int lttng_abi_validate_event_param(struct lttng_kernel_event *event_param)
+{
+ /* Limit ABI to implemented features. */
+ switch (event_param->instrumentation) {
+ case LTTNG_KERNEL_SYSCALL:
+ switch (event_param->u.syscall.entryexit) {
+ case LTTNG_KERNEL_SYSCALL_ENTRYEXIT:
+ break;
+ default:
+ return -EINVAL;
+ }
+ switch (event_param->u.syscall.abi) {
+ case LTTNG_KERNEL_SYSCALL_ABI_ALL:
+ break;
+ default:
+ return -EINVAL;
+ }
+ switch (event_param->u.syscall.match) {
+ case LTTNG_SYSCALL_MATCH_NAME:
+ break;
+ default:
+ return -EINVAL;
+ }
+ break;
+
+ case LTTNG_KERNEL_TRACEPOINT: /* Fallthrough */
+ case LTTNG_KERNEL_KPROBE: /* Fallthrough */
+ case LTTNG_KERNEL_KRETPROBE: /* Fallthrough */
+ case LTTNG_KERNEL_NOOP: /* Fallthrough */
+ case LTTNG_KERNEL_UPROBE:
+ break;
+
+ case LTTNG_KERNEL_FUNCTION: /* Fallthrough */
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static
+int lttng_abi_create_event(struct file *channel_file,
+ struct lttng_kernel_event *event_param)
+{
+ struct lttng_channel *channel = channel_file->private_data;
+ int event_fd, ret;
+ struct file *event_file;
+ void *priv;
+
+ event_param->name[LTTNG_KERNEL_SYM_NAME_LEN - 1] = '\0';
+ switch (event_param->instrumentation) {
+ case LTTNG_KERNEL_KRETPROBE:
+ event_param->u.kretprobe.symbol_name[LTTNG_KERNEL_SYM_NAME_LEN - 1] = '\0';
+ break;
+ case LTTNG_KERNEL_KPROBE:
+ event_param->u.kprobe.symbol_name[LTTNG_KERNEL_SYM_NAME_LEN - 1] = '\0';
+ break;
+ case LTTNG_KERNEL_FUNCTION:
+ WARN_ON_ONCE(1);
+ /* Not implemented. */
+ break;
+ default:
+ break;
+ }
+ event_fd = lttng_get_unused_fd();
+ if (event_fd < 0) {
+ ret = event_fd;
+ goto fd_error;
+ }
+ event_file = anon_inode_getfile("[lttng_event]",
+ <tng_event_fops,
+ NULL, O_RDWR);
+ if (IS_ERR(event_file)) {
+ ret = PTR_ERR(event_file);
+ goto file_error;
+ }
+ /* The event holds a reference on the channel */
+ if (!atomic_long_add_unless(&channel_file->f_count, 1, LONG_MAX)) {
+ ret = -EOVERFLOW;
+ goto refcount_error;
+ }
+ ret = lttng_abi_validate_event_param(event_param);
+ if (ret)
+ goto event_error;
+ if (event_param->instrumentation == LTTNG_KERNEL_TRACEPOINT
+ || event_param->instrumentation == LTTNG_KERNEL_SYSCALL) {
+ struct lttng_enabler *enabler;
+
+ if (strutils_is_star_glob_pattern(event_param->name)) {
+ /*
+ * If the event name is a star globbing pattern,
+ * we create the special star globbing enabler.
+ */
+ enabler = lttng_enabler_create(LTTNG_ENABLER_STAR_GLOB,
+ event_param, channel);
+ } else {
+ enabler = lttng_enabler_create(LTTNG_ENABLER_NAME,
+ event_param, channel);
+ }
+ priv = enabler;
+ } else {
+ struct lttng_event *event;
+
+ /*
+ * We tolerate no failure path after event creation. It
+ * will stay invariant for the rest of the session.
+ */
+ event = lttng_event_create(channel, event_param,
+ NULL, NULL,
+ event_param->instrumentation);
+ WARN_ON_ONCE(!event);
+ if (IS_ERR(event)) {
+ ret = PTR_ERR(event);
+ goto event_error;
+ }
+ priv = event;
+ }
+ event_file->private_data = priv;
+ fd_install(event_fd, event_file);
+ return event_fd;
+
+event_error:
+ atomic_long_dec(&channel_file->f_count);
+refcount_error:
+ fput(event_file);
+file_error:
+ put_unused_fd(event_fd);
+fd_error:
+ return ret;
+}
+
+/**
+ * lttng_channel_ioctl - lttng syscall through ioctl
+ *
+ * @file: the file
+ * @cmd: the command
+ * @arg: command arg
+ *
+ * This ioctl implements lttng commands:
+ * LTTNG_KERNEL_STREAM
+ * Returns an event stream file descriptor or failure.
+ * (typically, one event stream records events from one CPU)
+ * LTTNG_KERNEL_EVENT
+ * Returns an event file descriptor or failure.
+ * LTTNG_KERNEL_CONTEXT
+ * Prepend a context field to each event in the channel
+ * LTTNG_KERNEL_ENABLE
+ * Enable recording for events in this channel (weak enable)
+ * LTTNG_KERNEL_DISABLE
+ * Disable recording for events in this channel (strong disable)
+ *
+ * Channel and event file descriptors also hold a reference on the session.
+ */
+static
+long lttng_channel_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+ struct lttng_channel *channel = file->private_data;