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 */
#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
.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);
.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);
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);
#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.
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;
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;
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;
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);
}
#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)
if (ret)
return ret;
- file->private_data = buf;
ret = nonseekable_open(inode, file);
if (ret)
goto 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;
}
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;
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;
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);
* 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.
#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"
* 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)
{
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;
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;
#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;
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,
+ <tng_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;
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;
}
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,
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,
#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;
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)
{
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);
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);
{
int old;
- if (channel == channel->session->metadata)
+ if (channel->channel_type == METADATA_CHANNEL)
return -EPERM;
old = xchg(&channel->enabled, 1);
if (old)
{
int old;
- if (channel == channel->session->metadata)
+ if (channel->channel_type == METADATA_CHANNEL)
return -EPERM;
old = xchg(&channel->enabled, 0);
if (!old)
{
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)
{
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)
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) {
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;
}
/*
- * 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)
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.
*/
}
/*
+ * 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));
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)
return ret;
}
+/*
+ * Must be called with sessions_mutex held.
+ */
static
int _lttng_event_metadata_statedump(struct lttng_session *session,
struct lttng_channel *chan,
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,
}
+/*
+ * Must be called with sessions_mutex held.
+ */
static
int _lttng_channel_metadata_statedump(struct lttng_session *session,
struct lttng_channel *chan)
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);
return ret;
}
+/*
+ * Must be called with sessions_mutex held.
+ */
static
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)
/*
* 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)
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",
#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"
struct lttng_channel;
struct lttng_session;
+struct lttng_metadata_cache;
struct lib_ring_buffer_ctx;
struct perf_event;
struct perf_event_attr;
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;
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,
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);