Metadata cache and multiple metadata channels
authorJulien Desfossez <jdesfossez@efficios.com>
Thu, 6 Jun 2013 20:38:48 +0000 (16:38 -0400)
committerMathieu Desnoyers <mathieu.desnoyers@efficios.com>
Thu, 6 Jun 2013 20:39:46 +0000 (16:39 -0400)
Implement a cache for the kernel metadata and a list of metadata
channels.

When new metadata is appended, all metadata channels are awakened so
they can return from poll and get the newly added metadata
This allows to request the metadata multiple times by creating multiple
metadata channels (useful for snapshots).

With this new feature, the poll and get_subbuf ring buffer operations
are now overridden by lttng-abi for the metadata channels, to check the
cache before doing these operations.

Signed-off-by: Julien Desfossez <jdesfossez@efficios.com>
Signed-off-by: Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
lib/ringbuffer/backend.h
lib/ringbuffer/iterator.h
lib/ringbuffer/ring_buffer_iterator.c
lib/ringbuffer/ring_buffer_mmap.c
lib/ringbuffer/ring_buffer_splice.c
lib/ringbuffer/ring_buffer_vfs.c
lib/ringbuffer/vfs.h
lttng-abi.c
lttng-events.c
lttng-events.h

index 826be933fe61bb45efbe96a7738823e3ae46af9e..ed913325e2ddb70070392226cc9b27b39df18fad 100644 (file)
@@ -263,9 +263,4 @@ unsigned long lib_ring_buffer_get_records_unread(
        return records_unread;
 }
 
-ssize_t lib_ring_buffer_file_splice_read(struct file *in, loff_t *ppos,
-                                        struct pipe_inode_info *pipe,
-                                        size_t len, unsigned int flags);
-loff_t lib_ring_buffer_no_llseek(struct file *file, loff_t offset, int origin);
-
 #endif /* _LIB_RING_BUFFER_BACKEND_H */
index 4bfd816391c70c1cf49c8ed0212bf214437fb253..b0e17644c698e96a55aa9886f2522b00aff5315a 100644 (file)
@@ -28,6 +28,7 @@
 
 #include "../../wrapper/ringbuffer/backend.h"
 #include "../../wrapper/ringbuffer/frontend.h"
+#include "../../wrapper/ringbuffer/vfs.h"
 
 /*
  * lib_ring_buffer_get_next_record advances the buffer read position to the next
index 9d0197c73fbe074df3f2f60df806b76ad601ef12..290466f870110ba45b778266a4ccca9b7967407c 100644 (file)
@@ -796,7 +796,7 @@ const struct file_operations channel_payload_file_operations = {
        .open = channel_file_open,
        .release = channel_file_release,
        .read = channel_file_read,
-       .llseek = lib_ring_buffer_no_llseek,
+       .llseek = vfs_lib_ring_buffer_no_llseek,
 };
 EXPORT_SYMBOL_GPL(channel_payload_file_operations);
 
@@ -805,6 +805,6 @@ const struct file_operations lib_ring_buffer_payload_file_operations = {
        .open = lib_ring_buffer_file_open,
        .release = lib_ring_buffer_file_release,
        .read = lib_ring_buffer_file_read,
-       .llseek = lib_ring_buffer_no_llseek,
+       .llseek = vfs_lib_ring_buffer_no_llseek,
 };
 EXPORT_SYMBOL_GPL(lib_ring_buffer_payload_file_operations);
index ec7d9530fd968544ad4ce0b5a70a1740545621ef..c172fee7cfab9e2632e68adb9c617ab8e8c2fa43 100644 (file)
@@ -106,16 +106,23 @@ static int lib_ring_buffer_mmap_buf(struct lib_ring_buffer *buf,
        return 0;
 }
 
+int lib_ring_buffer_mmap(struct file *filp, struct vm_area_struct *vma,
+               struct lib_ring_buffer *buf)
+{
+       return lib_ring_buffer_mmap_buf(buf, vma);
+}
+EXPORT_SYMBOL_GPL(lib_ring_buffer_mmap);
+
 /**
- *     lib_ring_buffer_mmap - mmap file op
+ *     vfs_lib_ring_buffer_mmap - mmap file op
  *     @filp: the file
  *     @vma: the vma describing what to map
  *
  *     Calls upon lib_ring_buffer_mmap_buf() to map the file into user space.
  */
-int lib_ring_buffer_mmap(struct file *filp, struct vm_area_struct *vma)
+int vfs_lib_ring_buffer_mmap(struct file *filp, struct vm_area_struct *vma)
 {
        struct lib_ring_buffer *buf = filp->private_data;
-       return lib_ring_buffer_mmap_buf(buf, vma);
+       return lib_ring_buffer_mmap(filp, vma, buf);
 }
-EXPORT_SYMBOL_GPL(lib_ring_buffer_mmap);
+EXPORT_SYMBOL_GPL(vfs_lib_ring_buffer_mmap);
index 59a1589bb1301cc32ad5372bce6a7f26d3b09d17..3a7ff0c45cf86f7606d9d0751fe7a5db283daf3b 100644 (file)
 #define printk_dbg(fmt, args...)
 #endif
 
-loff_t lib_ring_buffer_no_llseek(struct file *file, loff_t offset, int origin)
+loff_t vfs_lib_ring_buffer_no_llseek(struct file *file, loff_t offset,
+               int origin)
 {
        return -ESPIPE;
 }
+EXPORT_SYMBOL_GPL(vfs_lib_ring_buffer_no_llseek);
 
 /*
  * Release pages from the buffer so splice pipe_to_file can move them.
@@ -78,9 +80,9 @@ static int subbuf_splice_actor(struct file *in,
                               loff_t *ppos,
                               struct pipe_inode_info *pipe,
                               size_t len,
-                              unsigned int flags)
+                              unsigned int flags,
+                              struct lib_ring_buffer *buf)
 {
-       struct lib_ring_buffer *buf = in->private_data;
        struct channel *chan = buf->backend.chan;
        const struct lib_ring_buffer_config *config = &chan->backend.config;
        unsigned int poff, subbuf_pages, nr_pages;
@@ -160,9 +162,9 @@ static int subbuf_splice_actor(struct file *in,
 
 ssize_t lib_ring_buffer_splice_read(struct file *in, loff_t *ppos,
                                    struct pipe_inode_info *pipe, size_t len,
-                                   unsigned int flags)
+                                   unsigned int flags,
+                                   struct lib_ring_buffer *buf)
 {
-       struct lib_ring_buffer *buf = in->private_data;
        struct channel *chan = buf->backend.chan;
        const struct lib_ring_buffer_config *config = &chan->backend.config;
        ssize_t spliced;
@@ -189,7 +191,7 @@ ssize_t lib_ring_buffer_splice_read(struct file *in, loff_t *ppos,
        printk_dbg(KERN_DEBUG "SPLICE read len %zu pos %zd\n", len,
                   (ssize_t)*ppos);
        while (len && !spliced) {
-               ret = subbuf_splice_actor(in, ppos, pipe, len, flags);
+               ret = subbuf_splice_actor(in, ppos, pipe, len, flags, buf);
                printk_dbg(KERN_DEBUG "SPLICE read loop ret %d\n", ret);
                if (ret < 0)
                        break;
@@ -213,3 +215,13 @@ ssize_t lib_ring_buffer_splice_read(struct file *in, loff_t *ppos,
        return ret;
 }
 EXPORT_SYMBOL_GPL(lib_ring_buffer_splice_read);
+
+ssize_t vfs_lib_ring_buffer_splice_read(struct file *in, loff_t *ppos,
+                                   struct pipe_inode_info *pipe, size_t len,
+                                   unsigned int flags)
+{
+       struct lib_ring_buffer *buf = in->private_data;
+
+       return lib_ring_buffer_splice_read(in, ppos, pipe, len, flags, buf);
+}
+EXPORT_SYMBOL_GPL(vfs_lib_ring_buffer_splice_read);
index 899af8124f05c10f296b7ea167383a399fdb1e28..33dfeaaaba4c5ce55df34ac97b9b812f78d8f565 100644 (file)
@@ -41,17 +41,9 @@ static int compat_put_ulong(compat_ulong_t val, unsigned long arg)
 }
 #endif
 
-/**
- *     lib_ring_buffer_open - ring buffer open file operation
- *     @inode: opened inode
- *     @file: opened file
- *
- *     Open implementation. Makes sure only one open instance of a buffer is
- *     done at a given moment.
- */
-int lib_ring_buffer_open(struct inode *inode, struct file *file)
+int lib_ring_buffer_open(struct inode *inode, struct file *file,
+               struct lib_ring_buffer *buf)
 {
-       struct lib_ring_buffer *buf = inode->i_private;
        int ret;
 
        if (!buf)
@@ -61,7 +53,6 @@ int lib_ring_buffer_open(struct inode *inode, struct file *file)
        if (ret)
                return ret;
 
-       file->private_data = buf;
        ret = nonseekable_open(inode, file);
        if (ret)
                goto release_read;
@@ -71,34 +62,53 @@ release_read:
        lib_ring_buffer_release_read(buf);
        return ret;
 }
+EXPORT_SYMBOL_GPL(lib_ring_buffer_open);
 
 /**
- *     lib_ring_buffer_release - ring buffer release file operation
+ *     vfs_lib_ring_buffer_open - ring buffer open file operation
  *     @inode: opened inode
  *     @file: opened file
  *
- *     Release implementation.
+ *     Open implementation. Makes sure only one open instance of a buffer is
+ *     done at a given moment.
  */
-int lib_ring_buffer_release(struct inode *inode, struct file *file)
+static
+int vfs_lib_ring_buffer_open(struct inode *inode, struct file *file)
 {
-       struct lib_ring_buffer *buf = file->private_data;
+       struct lib_ring_buffer *buf = inode->i_private;
+
+       file->private_data = buf;
+       return lib_ring_buffer_open(inode, file, buf);
+}
 
+int lib_ring_buffer_release(struct inode *inode, struct file *file,
+               struct lib_ring_buffer *buf)
+{
        lib_ring_buffer_release_read(buf);
 
        return 0;
 }
+EXPORT_SYMBOL_GPL(lib_ring_buffer_release);
 
 /**
- *     lib_ring_buffer_poll - ring buffer poll file operation
- *     @filp: the file
- *     @wait: poll table
+ *     vfs_lib_ring_buffer_release - ring buffer release file operation
+ *     @inode: opened inode
+ *     @file: opened file
  *
- *     Poll implementation.
+ *     Release implementation.
  */
-unsigned int lib_ring_buffer_poll(struct file *filp, poll_table *wait)
+static
+int vfs_lib_ring_buffer_release(struct inode *inode, struct file *file)
+{
+       struct lib_ring_buffer *buf = file->private_data;
+
+       return lib_ring_buffer_release(inode, file, buf);
+}
+
+unsigned int lib_ring_buffer_poll(struct file *filp, poll_table *wait,
+               struct lib_ring_buffer *buf)
 {
        unsigned int mask = 0;
-       struct lib_ring_buffer *buf = filp->private_data;
        struct channel *chan = buf->backend.chan;
        const struct lib_ring_buffer_config *config = &chan->backend.config;
        int finalized, disabled;
@@ -148,34 +158,26 @@ retry:
        }
        return mask;
 }
+EXPORT_SYMBOL_GPL(lib_ring_buffer_poll);
 
 /**
- *     lib_ring_buffer_ioctl - control ring buffer reader synchronization
- *
+ *     vfs_lib_ring_buffer_poll - ring buffer poll file operation
  *     @filp: the file
- *     @cmd: the command
- *     @arg: command arg
+ *     @wait: poll table
  *
- *     This ioctl implements commands necessary for producer/consumer
- *     and flight recorder reader interaction :
- *     RING_BUFFER_GET_NEXT_SUBBUF
- *             Get the next sub-buffer that can be read. It never blocks.
- *     RING_BUFFER_PUT_NEXT_SUBBUF
- *             Release the currently read sub-buffer.
- *     RING_BUFFER_GET_SUBBUF_SIZE
- *             returns the size of the current sub-buffer.
- *     RING_BUFFER_GET_MAX_SUBBUF_SIZE
- *             returns the maximum size for sub-buffers.
- *     RING_BUFFER_GET_NUM_SUBBUF
- *             returns the number of reader-visible sub-buffers in the per cpu
- *              channel (for mmap).
- *      RING_BUFFER_GET_MMAP_READ_OFFSET
- *              returns the offset of the subbuffer belonging to the reader.
- *              Should only be used for mmap clients.
+ *     Poll implementation.
  */
-long lib_ring_buffer_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
+static
+unsigned int vfs_lib_ring_buffer_poll(struct file *filp, poll_table *wait)
 {
        struct lib_ring_buffer *buf = filp->private_data;
+
+       return lib_ring_buffer_poll(filp, wait, buf);
+}
+
+long lib_ring_buffer_ioctl(struct file *filp, unsigned int cmd,
+               unsigned long arg, struct lib_ring_buffer *buf)
+{
        struct channel *chan = buf->backend.chan;
        const struct lib_ring_buffer_config *config = &chan->backend.config;
 
@@ -267,12 +269,44 @@ long lib_ring_buffer_ioctl(struct file *filp, unsigned int cmd, unsigned long ar
                return -ENOIOCTLCMD;
        }
 }
+EXPORT_SYMBOL_GPL(lib_ring_buffer_ioctl);
+
+/**
+ *     vfs_lib_ring_buffer_ioctl - control ring buffer reader synchronization
+ *
+ *     @filp: the file
+ *     @cmd: the command
+ *     @arg: command arg
+ *
+ *     This ioctl implements commands necessary for producer/consumer
+ *     and flight recorder reader interaction :
+ *     RING_BUFFER_GET_NEXT_SUBBUF
+ *             Get the next sub-buffer that can be read. It never blocks.
+ *     RING_BUFFER_PUT_NEXT_SUBBUF
+ *             Release the currently read sub-buffer.
+ *     RING_BUFFER_GET_SUBBUF_SIZE
+ *             returns the size of the current sub-buffer.
+ *     RING_BUFFER_GET_MAX_SUBBUF_SIZE
+ *             returns the maximum size for sub-buffers.
+ *     RING_BUFFER_GET_NUM_SUBBUF
+ *             returns the number of reader-visible sub-buffers in the per cpu
+ *              channel (for mmap).
+ *      RING_BUFFER_GET_MMAP_READ_OFFSET
+ *              returns the offset of the subbuffer belonging to the reader.
+ *              Should only be used for mmap clients.
+ */
+static
+long vfs_lib_ring_buffer_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
+{
+       struct lib_ring_buffer *buf = filp->private_data;
+
+       return lib_ring_buffer_ioctl(filp, cmd, arg, buf);
+}
 
 #ifdef CONFIG_COMPAT
 long lib_ring_buffer_compat_ioctl(struct file *filp, unsigned int cmd,
-                                 unsigned long arg)
+               unsigned long arg, struct lib_ring_buffer *buf)
 {
-       struct lib_ring_buffer *buf = filp->private_data;
        struct channel *chan = buf->backend.chan;
        const struct lib_ring_buffer_config *config = &chan->backend.config;
 
@@ -380,19 +414,29 @@ long lib_ring_buffer_compat_ioctl(struct file *filp, unsigned int cmd,
                return -ENOIOCTLCMD;
        }
 }
+EXPORT_SYMBOL_GPL(lib_ring_buffer_compat_ioctl);
+
+static
+long vfs_lib_ring_buffer_compat_ioctl(struct file *filp, unsigned int cmd,
+                                 unsigned long arg)
+{
+       struct lib_ring_buffer *buf = filp->private_data;
+
+       return lib_ring_buffer_compat_ioctl(filp, cmd, arg, buf);
+}
 #endif
 
 const struct file_operations lib_ring_buffer_file_operations = {
        .owner = THIS_MODULE,
-       .open = lib_ring_buffer_open,
-       .release = lib_ring_buffer_release,
-       .poll = lib_ring_buffer_poll,
-       .splice_read = lib_ring_buffer_splice_read,
-       .mmap = lib_ring_buffer_mmap,
-       .unlocked_ioctl = lib_ring_buffer_ioctl,
-       .llseek = lib_ring_buffer_no_llseek,
+       .open = vfs_lib_ring_buffer_open,
+       .release = vfs_lib_ring_buffer_release,
+       .poll = vfs_lib_ring_buffer_poll,
+       .splice_read = vfs_lib_ring_buffer_splice_read,
+       .mmap = vfs_lib_ring_buffer_mmap,
+       .unlocked_ioctl = vfs_lib_ring_buffer_ioctl,
+       .llseek = vfs_lib_ring_buffer_no_llseek,
 #ifdef CONFIG_COMPAT
-       .compat_ioctl = lib_ring_buffer_compat_ioctl,
+       .compat_ioctl = vfs_lib_ring_buffer_compat_ioctl,
 #endif
 };
 EXPORT_SYMBOL_GPL(lib_ring_buffer_file_operations);
index 625f9c91d932c0833c2c45ac771b69b16ded3d0e..ea317a1eaa4854642393ad9173ab07c223e0a148 100644 (file)
@@ -37,21 +37,37 @@ extern const struct file_operations lib_ring_buffer_file_operations;
  * Internal file operations.
  */
 
-int lib_ring_buffer_open(struct inode *inode, struct file *file);
-int lib_ring_buffer_release(struct inode *inode, struct file *file);
-unsigned int lib_ring_buffer_poll(struct file *filp, poll_table *wait);
+struct lib_ring_buffer;
+
+int lib_ring_buffer_open(struct inode *inode, struct file *file,
+               struct lib_ring_buffer *buf);
+int lib_ring_buffer_release(struct inode *inode, struct file *file,
+               struct lib_ring_buffer *buf);
+unsigned int lib_ring_buffer_poll(struct file *filp, poll_table *wait,
+               struct lib_ring_buffer *buf);
 ssize_t lib_ring_buffer_splice_read(struct file *in, loff_t *ppos,
-                                   struct pipe_inode_info *pipe, size_t len,
-                                   unsigned int flags);
-int lib_ring_buffer_mmap(struct file *filp, struct vm_area_struct *vma);
+               struct pipe_inode_info *pipe, size_t len,
+               unsigned int flags, struct lib_ring_buffer *buf);
+int lib_ring_buffer_mmap(struct file *filp, struct vm_area_struct *vma,
+               struct lib_ring_buffer *buf);
 
 /* Ring Buffer ioctl() and ioctl numbers */
-long lib_ring_buffer_ioctl(struct file *filp, unsigned int cmd, unsigned long arg);
+long lib_ring_buffer_ioctl(struct file *filp, unsigned int cmd,
+               unsigned long arg, struct lib_ring_buffer *buf);
 #ifdef CONFIG_COMPAT
 long lib_ring_buffer_compat_ioctl(struct file *filp, unsigned int cmd,
-                                 unsigned long arg);
+               unsigned long arg, struct lib_ring_buffer *buf);
 #endif
 
+ssize_t vfs_lib_ring_buffer_file_splice_read(struct file *in, loff_t *ppos,
+               struct pipe_inode_info *pipe, size_t len, unsigned int flags);
+loff_t vfs_lib_ring_buffer_no_llseek(struct file *file, loff_t offset,
+               int origin);
+int vfs_lib_ring_buffer_mmap(struct file *filp, struct vm_area_struct *vma);
+ssize_t vfs_lib_ring_buffer_splice_read(struct file *in, loff_t *ppos,
+               struct pipe_inode_info *pipe, size_t len,
+               unsigned int flags);
+
 /*
  * Use RING_BUFFER_GET_NEXT_SUBBUF / RING_BUFFER_PUT_NEXT_SUBBUF to read and
  * consume sub-buffers sequentially.
index 2709a855da8bb8d1aef90b01f15e5d570b57efe8..bde20864d526b40220d42db1b79fff8f3f1df35a 100644 (file)
@@ -45,6 +45,8 @@
 #include <linux/slab.h>
 #include "wrapper/vmalloc.h"   /* for wrapper_vmalloc_sync_all() */
 #include "wrapper/ringbuffer/vfs.h"
+#include "wrapper/ringbuffer/backend.h"
+#include "wrapper/ringbuffer/frontend.h"
 #include "wrapper/poll.h"
 #include "lttng-abi.h"
 #include "lttng-abi-old.h"
@@ -68,11 +70,6 @@ static const struct file_operations lttng_event_fops;
  * so it can only exit when all file descriptors are closed.
  */
 
-enum channel_type {
-       PER_CPU_CHANNEL,
-       METADATA_CHANNEL,
-};
-
 static
 int lttng_abi_create_session(void)
 {
@@ -362,7 +359,8 @@ int lttng_abi_create_channel(struct file *session_file,
                                  chan_param->subbuf_size,
                                  chan_param->num_subbuf,
                                  chan_param->switch_timer_interval,
-                                 chan_param->read_timer_interval);
+                                 chan_param->read_timer_interval,
+                                 channel_type);
        if (!chan) {
                ret = -EINVAL;
                goto chan_error;
@@ -370,11 +368,6 @@ int lttng_abi_create_channel(struct file *session_file,
        chan->file = chan_file;
        chan_file->private_data = chan;
        fd_install(chan_fd, chan_file);
-       if (channel_type == METADATA_CHANNEL) {
-               session->metadata = chan;
-       }
-
-       /* The channel created holds a reference on the session */
        atomic_long_inc(&session_file->f_count);
 
        return chan_fd;
@@ -514,26 +507,191 @@ static const struct file_operations lttng_session_fops = {
 #endif
 };
 
+/**
+ *     lttng_metadata_ring_buffer_poll - LTTng ring buffer poll file operation
+ *     @filp: the file
+ *     @wait: poll table
+ *
+ *     Handles the poll operations for the metadata channels.
+ */
 static
-int lttng_abi_open_stream(struct file *channel_file)
+unsigned int lttng_metadata_ring_buffer_poll(struct file *filp,
+               poll_table *wait)
+{
+       struct lttng_metadata_stream *stream = filp->private_data;
+       struct lib_ring_buffer *buf = stream->priv;
+       int finalized;
+       unsigned int mask = 0;
+
+       if (filp->f_mode & FMODE_READ) {
+               poll_wait_set_exclusive(wait);
+               poll_wait(filp, &stream->read_wait, wait);
+
+               finalized = stream->finalized;
+
+               /*
+                * lib_ring_buffer_is_finalized() contains a smp_rmb()
+                * ordering finalized load before offsets loads.
+                */
+               WARN_ON(atomic_long_read(&buf->active_readers) != 1);
+
+               if (finalized)
+                       mask |= POLLHUP;
+
+               if (stream->metadata_cache->metadata_written >
+                               stream->metadata_cache_read)
+                       mask |= POLLIN;
+       }
+
+       return mask;
+}
+
+static
+int lttng_metadata_ring_buffer_ioctl_get_subbuf(struct file *filp,
+               unsigned int cmd, unsigned long arg)
+{
+       struct lttng_metadata_stream *stream = filp->private_data;
+       struct lib_ring_buffer *buf = stream->priv;
+       struct channel *chan = buf->backend.chan;
+       struct lttng_channel *lttng_chan = channel_get_private(chan);
+       int ret;
+
+       ret = lttng_metadata_output_channel(lttng_chan, stream);
+       if (ret > 0) {
+               lib_ring_buffer_switch_slow(buf, SWITCH_ACTIVE);
+               ret = 0;
+       }
+       return ret;
+}
+
+static
+long lttng_metadata_ring_buffer_ioctl(struct file *filp,
+               unsigned int cmd, unsigned long arg)
+{
+       int ret;
+       struct lttng_metadata_stream *stream = filp->private_data;
+       struct lib_ring_buffer *buf = stream->priv;
+
+       switch (cmd) {
+       case RING_BUFFER_GET_SUBBUF:
+       case RING_BUFFER_GET_NEXT_SUBBUF:
+       {
+               ret = lttng_metadata_ring_buffer_ioctl_get_subbuf(filp,
+                               cmd, arg);
+               if (ret < 0)
+                       goto err;
+               break;
+       }
+       default:
+               break;
+       }
+       /* Performing lib ring buffer ioctl after our own. */
+       return lib_ring_buffer_ioctl(filp, cmd, arg, buf);
+
+err:
+       return ret;
+}
+
+static
+long lttng_metadata_ring_buffer_compat_ioctl(struct file *filp,
+               unsigned int cmd, unsigned long arg)
+{
+       int ret;
+       struct lttng_metadata_stream *stream = filp->private_data;
+       struct lib_ring_buffer *buf = stream->priv;
+
+       switch (cmd) {
+       case RING_BUFFER_GET_SUBBUF:
+       case RING_BUFFER_GET_NEXT_SUBBUF:
+       {
+               ret = lttng_metadata_ring_buffer_ioctl_get_subbuf(filp,
+                               cmd, arg);
+               if (ret < 0)
+                       goto err;
+               break;
+       }
+       default:
+               break;
+       }
+       /* Performing lib ring buffer ioctl after our own. */
+       return lib_ring_buffer_compat_ioctl(filp, cmd, arg, buf);
+
+err:
+       return ret;
+}
+
+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;
+       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;
+
+       kref_put(&stream->metadata_cache->refcount, metadata_cache_destroy);
+
+       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)
 {
-       struct lttng_channel *channel = channel_file->private_data;
-       struct lib_ring_buffer *buf;
        int stream_fd, ret;
        struct file *stream_file;
 
-       buf = channel->ops->buffer_read_open(channel->chan);
-       if (!buf)
-               return -ENOENT;
-
        stream_fd = get_unused_fd();
        if (stream_fd < 0) {
                ret = stream_fd;
                goto fd_error;
        }
-       stream_file = anon_inode_getfile("[lttng_stream]",
-                                        &lib_ring_buffer_file_operations,
-                                        buf, O_RDWR);
+       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;
@@ -554,6 +712,67 @@ int lttng_abi_open_stream(struct file *channel_file)
 
 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,
+                       &lib_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)
+               return -ENOMEM;
+       metadata_stream->metadata_cache = session->metadata_cache;
+       init_waitqueue_head(&metadata_stream->read_wait);
+       metadata_stream->priv = buf;
+       stream_priv = metadata_stream;
+       ret = lttng_abi_create_stream_fd(channel_file, stream_priv,
+                       &lttng_metadata_ring_buffer_file_operations);
+       if (ret < 0)
+               goto fd_error;
+
+       kref_get(&session->metadata_cache->refcount);
+       list_add(&metadata_stream->list,
+               &session->metadata_cache->metadata_stream);
+       return ret;
+
 fd_error:
        channel->ops->buffer_read_close(buf);
        return ret;
@@ -833,7 +1052,7 @@ long lttng_metadata_ioctl(struct file *file, unsigned int cmd, unsigned long arg
        switch (cmd) {
        case LTTNG_KERNEL_OLD_STREAM:
        case LTTNG_KERNEL_STREAM:
-               return lttng_abi_open_stream(file);
+               return lttng_abi_open_metadata_stream(file);
        default:
                return -ENOIOCTLCMD;
        }
@@ -877,6 +1096,19 @@ int lttng_channel_release(struct inode *inode, struct file *file)
        return 0;
 }
 
+static
+int lttng_metadata_channel_release(struct inode *inode, struct file *file)
+{
+       struct lttng_channel *channel = file->private_data;
+
+       if (channel) {
+               lttng_metadata_channel_destroy(channel);
+               fput(channel->session->file);
+       }
+
+       return 0;
+}
+
 static const struct file_operations lttng_channel_fops = {
        .owner = THIS_MODULE,
        .release = lttng_channel_release,
@@ -889,7 +1121,7 @@ static const struct file_operations lttng_channel_fops = {
 
 static const struct file_operations lttng_metadata_fops = {
        .owner = THIS_MODULE,
-       .release = lttng_channel_release,
+       .release = lttng_metadata_channel_release,
        .unlocked_ioctl = lttng_metadata_ioctl,
 #ifdef CONFIG_COMPAT
        .compat_ioctl = lttng_metadata_ioctl,
index 4f30904dc0c922d7c865369387a42c4d860a45c9..faf3e1f0f8f76785ebcc8b9d0baa170f113817e9 100644 (file)
 #include "lttng-tracer.h"
 #include "lttng-abi-old.h"
 
+#define METADATA_CACHE_DEFAULT_SIZE 4096
+
 static LIST_HEAD(sessions);
 static LIST_HEAD(lttng_transport_list);
+/*
+ * Protect the sessions and metadata caches.
+ */
 static DEFINE_MUTEX(sessions_mutex);
 static struct kmem_cache *event_cache;
 
@@ -49,6 +54,8 @@ int _lttng_event_metadata_statedump(struct lttng_session *session,
                                  struct lttng_event *event);
 static
 int _lttng_session_metadata_statedump(struct lttng_session *session);
+static
+void _lttng_metadata_channel_hangup(struct lttng_metadata_stream *stream);
 
 void synchronize_trace(void)
 {
@@ -61,23 +68,54 @@ void synchronize_trace(void)
 struct lttng_session *lttng_session_create(void)
 {
        struct lttng_session *session;
+       struct lttng_metadata_cache *metadata_cache;
 
        mutex_lock(&sessions_mutex);
        session = kzalloc(sizeof(struct lttng_session), GFP_KERNEL);
        if (!session)
-               return NULL;
+               goto err;
        INIT_LIST_HEAD(&session->chan);
        INIT_LIST_HEAD(&session->events);
        uuid_le_gen(&session->uuid);
+
+       metadata_cache = kzalloc(sizeof(struct lttng_metadata_cache),
+                       GFP_KERNEL);
+       if (!metadata_cache)
+               goto err_free_session;
+       metadata_cache->data = kzalloc(METADATA_CACHE_DEFAULT_SIZE,
+                       GFP_KERNEL);
+       if (!metadata_cache->data)
+               goto err_free_cache;
+       metadata_cache->cache_alloc = METADATA_CACHE_DEFAULT_SIZE;
+       kref_init(&metadata_cache->refcount);
+       session->metadata_cache = metadata_cache;
+       INIT_LIST_HEAD(&metadata_cache->metadata_stream);
        list_add(&session->list, &sessions);
        mutex_unlock(&sessions_mutex);
        return session;
+
+err_free_cache:
+       kfree(metadata_cache);
+err_free_session:
+       kfree(session);
+err:
+       mutex_unlock(&sessions_mutex);
+       return NULL;
+}
+
+void metadata_cache_destroy(struct kref *kref)
+{
+       struct lttng_metadata_cache *cache =
+               container_of(kref, struct lttng_metadata_cache, refcount);
+       kfree(cache->data);
+       kfree(cache);
 }
 
 void lttng_session_destroy(struct lttng_session *session)
 {
        struct lttng_channel *chan, *tmpchan;
        struct lttng_event *event, *tmpevent;
+       struct lttng_metadata_stream *metadata_stream;
        int ret;
 
        mutex_lock(&sessions_mutex);
@@ -93,8 +131,13 @@ void lttng_session_destroy(struct lttng_session *session)
        synchronize_trace();    /* Wait for in-flight events to complete */
        list_for_each_entry_safe(event, tmpevent, &session->events, list)
                _lttng_event_destroy(event);
-       list_for_each_entry_safe(chan, tmpchan, &session->chan, list)
+       list_for_each_entry_safe(chan, tmpchan, &session->chan, list) {
+               BUG_ON(chan->channel_type == METADATA_CHANNEL);
                _lttng_channel_destroy(chan);
+       }
+       list_for_each_entry(metadata_stream, &session->metadata_cache->metadata_stream, list)
+               _lttng_metadata_channel_hangup(metadata_stream);
+       kref_put(&session->metadata_cache->refcount, metadata_cache_destroy);
        list_del(&session->list);
        mutex_unlock(&sessions_mutex);
        kfree(session);
@@ -158,7 +201,7 @@ int lttng_channel_enable(struct lttng_channel *channel)
 {
        int old;
 
-       if (channel == channel->session->metadata)
+       if (channel->channel_type == METADATA_CHANNEL)
                return -EPERM;
        old = xchg(&channel->enabled, 1);
        if (old)
@@ -170,7 +213,7 @@ int lttng_channel_disable(struct lttng_channel *channel)
 {
        int old;
 
-       if (channel == channel->session->metadata)
+       if (channel->channel_type == METADATA_CHANNEL)
                return -EPERM;
        old = xchg(&channel->enabled, 0);
        if (!old)
@@ -182,7 +225,7 @@ int lttng_event_enable(struct lttng_event *event)
 {
        int old;
 
-       if (event->chan == event->chan->session->metadata)
+       if (event->chan->channel_type == METADATA_CHANNEL)
                return -EPERM;
        old = xchg(&event->enabled, 1);
        if (old)
@@ -194,7 +237,7 @@ int lttng_event_disable(struct lttng_event *event)
 {
        int old;
 
-       if (event->chan == event->chan->session->metadata)
+       if (event->chan->channel_type == METADATA_CHANNEL)
                return -EPERM;
        old = xchg(&event->enabled, 0);
        if (!old)
@@ -218,13 +261,14 @@ struct lttng_channel *lttng_channel_create(struct lttng_session *session,
                                       void *buf_addr,
                                       size_t subbuf_size, size_t num_subbuf,
                                       unsigned int switch_timer_interval,
-                                      unsigned int read_timer_interval)
+                                      unsigned int read_timer_interval,
+                                      enum channel_type channel_type)
 {
        struct lttng_channel *chan;
        struct lttng_transport *transport = NULL;
 
        mutex_lock(&sessions_mutex);
-       if (session->been_active)
+       if (session->been_active && channel_type != METADATA_CHANNEL)
                goto active;    /* Refuse to add channel to active session */
        transport = lttng_transport_find(transport_name);
        if (!transport) {
@@ -254,6 +298,7 @@ struct lttng_channel *lttng_channel_create(struct lttng_session *session,
        chan->enabled = 1;
        chan->ops = &transport->ops;
        chan->transport = transport;
+       chan->channel_type = channel_type;
        list_add(&chan->list, &session->chan);
        mutex_unlock(&sessions_mutex);
        return chan;
@@ -270,7 +315,9 @@ active:
 }
 
 /*
- * Only used internally at session destruction.
+ * Only used internally at session destruction for per-cpu channels, and
+ * when metadata channel is released.
+ * Needs to be called with sessions mutex held.
  */
 static
 void _lttng_channel_destroy(struct lttng_channel *chan)
@@ -282,6 +329,24 @@ void _lttng_channel_destroy(struct lttng_channel *chan)
        kfree(chan);
 }
 
+void lttng_metadata_channel_destroy(struct lttng_channel *chan)
+{
+       BUG_ON(chan->channel_type != METADATA_CHANNEL);
+
+       /* Protect the metadata cache with the sessions_mutex. */
+       mutex_lock(&sessions_mutex);
+       _lttng_channel_destroy(chan);
+       mutex_unlock(&sessions_mutex);
+}
+EXPORT_SYMBOL_GPL(lttng_metadata_channel_destroy);
+
+static
+void _lttng_metadata_channel_hangup(struct lttng_metadata_stream *stream)
+{
+       stream->finalized = 1;
+       wake_up_interruptible(&stream->read_wait);
+}
+
 /*
  * Supports event creation while tracing session is active.
  */
@@ -482,20 +547,57 @@ void _lttng_event_destroy(struct lttng_event *event)
 }
 
 /*
+ * Serialize at most one packet worth of metadata into a metadata
+ * channel.
  * We have exclusive access to our metadata buffer (protected by the
  * sessions_mutex), so we can do racy operations such as looking for
  * remaining space left in packet and write, since mutual exclusion
  * protects us from concurrent writes.
  */
+int lttng_metadata_output_channel(struct lttng_channel *chan,
+               struct lttng_metadata_stream *stream)
+{
+       struct lib_ring_buffer_ctx ctx;
+       int ret = 0;
+       size_t len, reserve_len;
+
+       len = stream->metadata_cache->metadata_written -
+               stream->metadata_cache_read;
+       reserve_len = min_t(size_t,
+                       chan->ops->packet_avail_size(chan->chan),
+                       len);
+       lib_ring_buffer_ctx_init(&ctx, chan->chan, NULL, reserve_len,
+                       sizeof(char), -1);
+       /*
+        * If reservation failed, return an error to the caller.
+        */
+       ret = chan->ops->event_reserve(&ctx, 0);
+       if (ret != 0) {
+               printk(KERN_WARNING "LTTng: Metadata event reservation failed\n");
+               goto end;
+       }
+       chan->ops->event_write(&ctx,
+                       stream->metadata_cache->data + stream->metadata_cache_read,
+                       reserve_len);
+       chan->ops->event_commit(&ctx);
+       stream->metadata_cache_read += reserve_len;
+       ret = reserve_len;
+
+end:
+       return ret;
+}
+
+/*
+ * Write the metadata to the metadata cache.
+ * Must be called with sessions_mutex held.
+ */
 int lttng_metadata_printf(struct lttng_session *session,
                          const char *fmt, ...)
 {
-       struct lib_ring_buffer_ctx ctx;
-       struct lttng_channel *chan = session->metadata;
        char *str;
-       int ret = 0, waitret;
-       size_t len, reserve_len, pos;
+       size_t len;
        va_list ap;
+       struct lttng_metadata_stream *stream;
 
        WARN_ON_ONCE(!ACCESS_ONCE(session->active));
 
@@ -506,42 +608,40 @@ int lttng_metadata_printf(struct lttng_session *session,
                return -ENOMEM;
 
        len = strlen(str);
-       pos = 0;
-
-       for (pos = 0; pos < len; pos += reserve_len) {
-               reserve_len = min_t(size_t,
-                               chan->ops->packet_avail_size(chan->chan),
-                               len - pos);
-               lib_ring_buffer_ctx_init(&ctx, chan->chan, NULL, reserve_len,
-                                        sizeof(char), -1);
-               /*
-                * We don't care about metadata buffer's records lost
-                * count, because we always retry here. Report error if
-                * we need to bail out after timeout or being
-                * interrupted.
-                */
-               waitret = wait_event_interruptible_timeout(*chan->ops->get_writer_buf_wait_queue(chan->chan, -1),
-                       ({
-                               ret = chan->ops->event_reserve(&ctx, 0);
-                               ret != -ENOBUFS || !ret;
-                       }),
-                       msecs_to_jiffies(LTTNG_METADATA_TIMEOUT_MSEC));
-               if (!waitret || waitret == -ERESTARTSYS || ret) {
-                       printk(KERN_WARNING "LTTng: Failure to write metadata to buffers (%s)\n",
-                               waitret == -ERESTARTSYS ? "interrupted" :
-                                       (ret == -ENOBUFS ? "timeout" : "I/O error"));
-                       if (waitret == -ERESTARTSYS)
-                               ret = waitret;
-                       goto end;
-               }
-               chan->ops->event_write(&ctx, &str[pos], reserve_len);
-               chan->ops->event_commit(&ctx);
+       if (session->metadata_cache->metadata_written + len >
+                       session->metadata_cache->cache_alloc) {
+               char *tmp_cache_realloc;
+               unsigned int tmp_cache_alloc_size;
+
+               tmp_cache_alloc_size = max_t(unsigned int,
+                               session->metadata_cache->cache_alloc + len,
+                               session->metadata_cache->cache_alloc << 1);
+               tmp_cache_realloc = krealloc(session->metadata_cache->data,
+                               tmp_cache_alloc_size, GFP_KERNEL);
+               if (!tmp_cache_realloc)
+                       goto err;
+               session->metadata_cache->cache_alloc = tmp_cache_alloc_size;
+               session->metadata_cache->data = tmp_cache_realloc;
        }
-end:
+       memcpy(session->metadata_cache->data +
+                       session->metadata_cache->metadata_written,
+                       str, len);
+       session->metadata_cache->metadata_written += len;
        kfree(str);
-       return ret;
+
+       list_for_each_entry(stream, &session->metadata_cache->metadata_stream, list)
+               wake_up_interruptible(&stream->read_wait);
+
+       return 0;
+
+err:
+       kfree(str);
+       return -ENOMEM;
 }
 
+/*
+ * Must be called with sessions_mutex held.
+ */
 static
 int _lttng_field_statedump(struct lttng_session *session,
                         const struct lttng_event_field *field)
@@ -698,6 +798,9 @@ int _lttng_fields_metadata_statedump(struct lttng_session *session,
        return ret;
 }
 
+/*
+ * Must be called with sessions_mutex held.
+ */
 static
 int _lttng_event_metadata_statedump(struct lttng_session *session,
                                  struct lttng_channel *chan,
@@ -707,7 +810,7 @@ int _lttng_event_metadata_statedump(struct lttng_session *session,
 
        if (event->metadata_dumped || !ACCESS_ONCE(session->active))
                return 0;
-       if (chan == session->metadata)
+       if (chan->channel_type == METADATA_CHANNEL)
                return 0;
 
        ret = lttng_metadata_printf(session,
@@ -763,6 +866,9 @@ end:
 
 }
 
+/*
+ * Must be called with sessions_mutex held.
+ */
 static
 int _lttng_channel_metadata_statedump(struct lttng_session *session,
                                    struct lttng_channel *chan)
@@ -771,7 +877,8 @@ int _lttng_channel_metadata_statedump(struct lttng_session *session,
 
        if (chan->metadata_dumped || !ACCESS_ONCE(session->active))
                return 0;
-       if (chan == session->metadata)
+
+       if (chan->channel_type == METADATA_CHANNEL)
                return 0;
 
        WARN_ON_ONCE(!chan->header_type);
@@ -810,6 +917,9 @@ end:
        return ret;
 }
 
+/*
+ * Must be called with sessions_mutex held.
+ */
 static
 int _lttng_stream_packet_context_declare(struct lttng_session *session)
 {
@@ -833,6 +943,8 @@ int _lttng_stream_packet_context_declare(struct lttng_session *session)
  * Large header:
  * id: range: 0 - 65534.
  * id 65535 is reserved to indicate an extended header.
+ *
+ * Must be called with sessions_mutex held.
  */
 static
 int _lttng_event_header_declare(struct lttng_session *session)
@@ -897,6 +1009,7 @@ uint64_t measure_clock_offset(void)
 
 /*
  * Output metadata into this session's metadata buffers.
+ * Must be called with sessions_mutex held.
  */
 static
 int _lttng_session_metadata_statedump(struct lttng_session *session)
@@ -911,10 +1024,6 @@ int _lttng_session_metadata_statedump(struct lttng_session *session)
                return 0;
        if (session->metadata_dumped)
                goto skip_session;
-       if (!session->metadata) {
-               printk(KERN_WARNING "LTTng: attempt to start tracing, but metadata channel is not found. Operation abort.\n");
-               return -EPERM;
-       }
 
        snprintf(uuid_s, sizeof(uuid_s),
                "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x",
index 37a5db71e5bc1f7543a1a3f2efb3ae0b412c3413..46d80bcdb0edcd1a6d3599e7f630f240ecb28b0f 100644 (file)
@@ -26,6 +26,7 @@
 #include <linux/version.h>
 #include <linux/list.h>
 #include <linux/kprobes.h>
+#include <linux/kref.h>
 #include "wrapper/uuid.h"
 #include "lttng-abi.h"
 #include "lttng-abi-old.h"
@@ -34,6 +35,7 @@
 
 struct lttng_channel;
 struct lttng_session;
+struct lttng_metadata_cache;
 struct lib_ring_buffer_ctx;
 struct perf_event;
 struct perf_event_attr;
@@ -58,6 +60,11 @@ enum lttng_string_encodings {
        NR_STRING_ENCODINGS,
 };
 
+enum channel_type {
+       PER_CPU_CHANNEL,
+       METADATA_CHANNEL,
+};
+
 struct lttng_enum_entry {
        unsigned long long start, end;  /* start and end are inclusive */
        const char *string;
@@ -264,39 +271,60 @@ struct lttng_channel {
        struct lttng_event *sc_compat_unknown;
        struct lttng_event *sc_exit;    /* for syscall exit */
        int header_type;                /* 0: unset, 1: compact, 2: large */
+       enum channel_type channel_type;
        unsigned int metadata_dumped:1;
 };
 
+struct lttng_metadata_stream {
+       void *priv;                     /* Ring buffer private data */
+       struct lttng_metadata_cache *metadata_cache;
+       unsigned int metadata_cache_read; /* Bytes read from the cache */
+       int finalized;                  /* Has channel been finalized */
+       wait_queue_head_t read_wait;    /* Reader buffer-level wait queue */
+       struct list_head list;          /* Stream list */
+};
+
 struct lttng_session {
        int active;                     /* Is trace session active ? */
        int been_active;                /* Has trace session been active ? */
        struct file *file;              /* File associated to session */
-       struct lttng_channel *metadata; /* Metadata channel */
        struct list_head chan;          /* Channel list head */
        struct list_head events;        /* Event list head */
        struct list_head list;          /* Session list */
        unsigned int free_chan_id;      /* Next chan ID to allocate */
        uuid_le uuid;                   /* Trace session unique ID */
+       struct lttng_metadata_cache *metadata_cache;
        unsigned int metadata_dumped:1;
 };
 
+struct lttng_metadata_cache {
+       char *data;                     /* Metadata cache */
+       unsigned int cache_alloc;       /* Metadata allocated size (bytes) */
+       unsigned int metadata_written;  /* Number of bytes written in metadata cache */
+       struct kref refcount;           /* Metadata cache usage */
+       struct list_head metadata_stream;       /* Metadata stream list */
+};
+
 struct lttng_session *lttng_session_create(void);
 int lttng_session_enable(struct lttng_session *session);
 int lttng_session_disable(struct lttng_session *session);
 void lttng_session_destroy(struct lttng_session *session);
+void metadata_cache_destroy(struct kref *kref);
 
 struct lttng_channel *lttng_channel_create(struct lttng_session *session,
                                       const char *transport_name,
                                       void *buf_addr,
                                       size_t subbuf_size, size_t num_subbuf,
                                       unsigned int switch_timer_interval,
-                                      unsigned int read_timer_interval);
+                                      unsigned int read_timer_interval,
+                                      enum channel_type channel_type);
 struct lttng_channel *lttng_global_channel_create(struct lttng_session *session,
                                       int overwrite, void *buf_addr,
                                       size_t subbuf_size, size_t num_subbuf,
                                       unsigned int switch_timer_interval,
                                       unsigned int read_timer_interval);
 
+void lttng_metadata_channel_destroy(struct lttng_channel *chan);
 struct lttng_event *lttng_event_create(struct lttng_channel *chan,
                                   struct lttng_kernel_event *event_param,
                                   void *filter,
@@ -327,6 +355,9 @@ void lttng_event_put(const struct lttng_event_desc *desc);
 int lttng_probes_init(void);
 void lttng_probes_exit(void);
 
+int lttng_metadata_output_channel(struct lttng_channel *chan,
+               struct lttng_metadata_stream *stream);
+
 #if defined(CONFIG_HAVE_SYSCALL_TRACEPOINTS)
 int lttng_syscalls_register(struct lttng_channel *chan, void *filter);
 int lttng_syscalls_unregister(struct lttng_channel *chan);
This page took 0.042682 seconds and 4 git commands to generate.