#include <linux/file.h>
#include <linux/uaccess.h>
#include <linux/slab.h>
+#include <linux/err.h>
#include "wrapper/vmalloc.h" /* for wrapper_vmalloc_sync_all() */
#include "wrapper/ringbuffer/vfs.h"
#include "wrapper/ringbuffer/backend.h"
#include "lttng-abi-old.h"
#include "lttng-events.h"
#include "lttng-tracer.h"
+#include "lib/ringbuffer/frontend_types.h"
/*
* This is LTTng's own personal way to create a system call as an external
static const struct file_operations lttng_channel_fops;
static const struct file_operations lttng_metadata_fops;
static const struct file_operations lttng_event_fops;
+static struct file_operations lttng_stream_ring_buffer_file_operations;
/*
* Teardown management: opened file descriptors keep a refcount on the module,
goto fd_error;
}
- tracepoint_list_file = anon_inode_getfile("[lttng_session]",
+ tracepoint_list_file = anon_inode_getfile("[lttng_tracepoint_list]",
<tng_tracepoint_list_fops,
NULL, O_RDWR);
if (IS_ERR(tracepoint_list_file)) {
return ret;
}
+static
+int lttng_abi_syscall_list(void)
+{
+ struct file *syscall_list_file;
+ int file_fd, ret;
+
+ file_fd = get_unused_fd();
+ if (file_fd < 0) {
+ ret = file_fd;
+ goto fd_error;
+ }
+
+ syscall_list_file = anon_inode_getfile("[lttng_syscall_list]",
+ <tng_syscall_list_fops,
+ NULL, O_RDWR);
+ if (IS_ERR(syscall_list_file)) {
+ ret = PTR_ERR(syscall_list_file);
+ goto file_error;
+ }
+ ret = lttng_syscall_list_fops.open(NULL, syscall_list_file);
+ if (ret < 0)
+ goto open_error;
+ fd_install(file_fd, syscall_list_file);
+ if (file_fd < 0) {
+ ret = file_fd;
+ goto fd_error;
+ }
+ return file_fd;
+
+open_error:
+ fput(syscall_list_file);
+file_error:
+ put_unused_fd(file_fd);
+fd_error:
+ return ret;
+}
+
static
void lttng_abi_tracer_version(struct lttng_kernel_tracer_version *v)
{
case LTTNG_KERNEL_OLD_TRACEPOINT_LIST:
case LTTNG_KERNEL_TRACEPOINT_LIST:
return lttng_abi_tracepoint_list();
+ case LTTNG_KERNEL_SYSCALL_LIST:
+ return lttng_abi_syscall_list();
case LTTNG_KERNEL_OLD_WAIT_QUIESCENT:
case LTTNG_KERNEL_WAIT_QUIESCENT:
synchronize_trace();
return mask;
}
-static
-int lttng_metadata_ring_buffer_ioctl_get_next_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;
- int ret;
-
- ret = lttng_metadata_output_channel(stream, chan);
- if (ret > 0) {
- lib_ring_buffer_switch_slow(buf, SWITCH_ACTIVE);
- ret = 0;
- }
- return ret;
-}
-
static
void lttng_metadata_ring_buffer_ioctl_put_next_subbuf(struct file *filp,
unsigned int cmd, unsigned long arg)
switch (cmd) {
case RING_BUFFER_GET_NEXT_SUBBUF:
{
- ret = lttng_metadata_ring_buffer_ioctl_get_next_subbuf(filp,
- cmd, arg);
- if (ret < 0)
+ 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);
+ if (ret > 0) {
+ lib_ring_buffer_switch_slow(buf, SWITCH_ACTIVE);
+ ret = 0;
+ } else if (ret < 0)
goto err;
break;
}
*/
return -ENOSYS;
}
+ 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);
+ if (ret < 0)
+ goto err;
+ break;
+ }
default:
break;
}
switch (cmd) {
case RING_BUFFER_GET_NEXT_SUBBUF:
{
- ret = lttng_metadata_ring_buffer_ioctl_get_next_subbuf(filp,
- cmd, arg);
- if (ret < 0)
+ 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);
+ if (ret > 0) {
+ lib_ring_buffer_switch_slow(buf, SWITCH_ACTIVE);
+ ret = 0;
+ } else if (ret < 0)
goto err;
break;
}
stream_priv = buf;
ret = lttng_abi_create_stream_fd(channel_file, stream_priv,
- &lib_ring_buffer_file_operations);
+ <tng_stream_ring_buffer_file_operations);
if (ret < 0)
goto fd_error;
metadata_stream->priv = buf;
stream_priv = metadata_stream;
metadata_stream->transport = channel->transport;
+ mutex_init(&metadata_stream->lock);
/*
* Since life-time of metadata cache differs from that of
* will stay invariant for the rest of the session.
*/
event = lttng_event_create(channel, event_param, NULL, NULL);
- if (!event) {
- ret = -EINVAL;
+ WARN_ON_ONCE(!event);
+ if (IS_ERR(event)) {
+ ret = PTR_ERR(event);
goto event_error;
}
event_file->private_data = event;
atomic_long_inc(&channel_file->f_count);
break;
case LTTNG_KERNEL_SYSCALL:
- /*
- * Only all-syscall tracing supported for now.
- */
- if (event_param->name[0] != '\0')
- return -EINVAL;
ret = lttng_syscalls_register(channel, NULL);
if (ret)
goto fd_error;
event_fd = 0;
+ if (event_param->u.syscall.enable) {
+ ret = lttng_syscall_filter_enable(channel,
+ event_param->name[0] == '\0' ?
+ NULL : event_param->name);
+ if (ret)
+ goto fd_error;
+
+ } else {
+ ret = lttng_syscall_filter_disable(channel,
+ event_param->name[0] == '\0' ?
+ NULL : event_param->name);
+ if (ret)
+ goto fd_error;
+ }
break;
}
return event_fd;
case LTTNG_KERNEL_OLD_DISABLE:
case LTTNG_KERNEL_DISABLE:
return lttng_channel_disable(channel);
+ case LTTNG_KERNEL_SYSCALL_MASK:
+ return lttng_channel_syscall_mask(channel,
+ (struct lttng_kernel_syscall_mask __user *) arg);
default:
return -ENOIOCTLCMD;
}
#endif
};
+static int put_u64(uint64_t val, unsigned long arg)
+{
+ return put_user(val, (uint64_t __user *) arg);
+}
+
+static long lttng_stream_ring_buffer_ioctl(struct file *filp,
+ unsigned int cmd, unsigned long arg)
+{
+ struct lib_ring_buffer *buf = filp->private_data;
+ struct channel *chan = buf->backend.chan;
+ const struct lib_ring_buffer_config *config = &chan->backend.config;
+ const struct lttng_channel_ops *ops = chan->backend.priv_ops;
+ int ret;
+
+ if (atomic_read(&chan->record_disabled))
+ return -EIO;
+
+ switch (cmd) {
+ case LTTNG_RING_BUFFER_GET_TIMESTAMP_BEGIN:
+ {
+ uint64_t ts;
+
+ ret = ops->timestamp_begin(config, buf, &ts);
+ if (ret < 0)
+ goto error;
+ return put_u64(ts, arg);
+ }
+ case LTTNG_RING_BUFFER_GET_TIMESTAMP_END:
+ {
+ uint64_t ts;
+
+ ret = ops->timestamp_end(config, buf, &ts);
+ if (ret < 0)
+ goto error;
+ return put_u64(ts, arg);
+ }
+ case LTTNG_RING_BUFFER_GET_EVENTS_DISCARDED:
+ {
+ uint64_t ed;
+
+ ret = ops->events_discarded(config, buf, &ed);
+ if (ret < 0)
+ goto error;
+ return put_u64(ed, arg);
+ }
+ case LTTNG_RING_BUFFER_GET_CONTENT_SIZE:
+ {
+ uint64_t cs;
+
+ ret = ops->content_size(config, buf, &cs);
+ if (ret < 0)
+ goto error;
+ return put_u64(cs, arg);
+ }
+ case LTTNG_RING_BUFFER_GET_PACKET_SIZE:
+ {
+ uint64_t ps;
+
+ ret = ops->packet_size(config, buf, &ps);
+ if (ret < 0)
+ goto error;
+ return put_u64(ps, arg);
+ }
+ case LTTNG_RING_BUFFER_GET_STREAM_ID:
+ {
+ uint64_t si;
+
+ ret = ops->stream_id(config, buf, &si);
+ if (ret < 0)
+ goto error;
+ return put_u64(si, arg);
+ }
+ case LTTNG_RING_BUFFER_GET_CURRENT_TIMESTAMP:
+ {
+ uint64_t ts;
+
+ ret = ops->current_timestamp(config, buf, &ts);
+ if (ret < 0)
+ goto error;
+ return put_u64(ts, arg);
+ }
+ default:
+ return lib_ring_buffer_file_operations.unlocked_ioctl(filp,
+ cmd, arg);
+ }
+
+error:
+ return -ENOSYS;
+}
+
+#ifdef CONFIG_COMPAT
+static long lttng_stream_ring_buffer_compat_ioctl(struct file *filp,
+ unsigned int cmd, unsigned long arg)
+{
+ struct lib_ring_buffer *buf = filp->private_data;
+ struct channel *chan = buf->backend.chan;
+ const struct lib_ring_buffer_config *config = &chan->backend.config;
+ const struct lttng_channel_ops *ops = chan->backend.priv_ops;
+ int ret;
+
+ if (atomic_read(&chan->record_disabled))
+ return -EIO;
+
+ switch (cmd) {
+ case LTTNG_RING_BUFFER_COMPAT_GET_TIMESTAMP_BEGIN:
+ {
+ uint64_t ts;
+
+ ret = ops->timestamp_begin(config, buf, &ts);
+ if (ret < 0)
+ goto error;
+ return put_u64(ts, arg);
+ }
+ case LTTNG_RING_BUFFER_COMPAT_GET_TIMESTAMP_END:
+ {
+ uint64_t ts;
+
+ ret = ops->timestamp_end(config, buf, &ts);
+ if (ret < 0)
+ goto error;
+ return put_u64(ts, arg);
+ }
+ case LTTNG_RING_BUFFER_COMPAT_GET_EVENTS_DISCARDED:
+ {
+ uint64_t ed;
+
+ ret = ops->events_discarded(config, buf, &ed);
+ if (ret < 0)
+ goto error;
+ return put_u64(ed, arg);
+ }
+ case LTTNG_RING_BUFFER_COMPAT_GET_CONTENT_SIZE:
+ {
+ uint64_t cs;
+
+ ret = ops->content_size(config, buf, &cs);
+ if (ret < 0)
+ goto error;
+ return put_u64(cs, arg);
+ }
+ case LTTNG_RING_BUFFER_COMPAT_GET_PACKET_SIZE:
+ {
+ uint64_t ps;
+
+ ret = ops->packet_size(config, buf, &ps);
+ if (ret < 0)
+ goto error;
+ return put_u64(ps, arg);
+ }
+ case LTTNG_RING_BUFFER_COMPAT_GET_STREAM_ID:
+ {
+ uint64_t si;
+
+ ret = ops->stream_id(config, buf, &si);
+ if (ret < 0)
+ goto error;
+ return put_u64(si, arg);
+ }
+ case LTTNG_RING_BUFFER_GET_CURRENT_TIMESTAMP:
+ {
+ uint64_t ts;
+
+ ret = ops->current_timestamp(config, buf, &ts);
+ if (ret < 0)
+ goto error;
+ return put_u64(ts, arg);
+ }
+ default:
+ return lib_ring_buffer_file_operations.compat_ioctl(filp,
+ cmd, arg);
+ }
+
+error:
+ return -ENOSYS;
+}
+#endif /* CONFIG_COMPAT */
+
+static void lttng_stream_override_ring_buffer_fops(void)
+{
+ lttng_stream_ring_buffer_file_operations.owner = THIS_MODULE;
+ lttng_stream_ring_buffer_file_operations.open =
+ lib_ring_buffer_file_operations.open;
+ lttng_stream_ring_buffer_file_operations.release =
+ lib_ring_buffer_file_operations.release;
+ lttng_stream_ring_buffer_file_operations.poll =
+ lib_ring_buffer_file_operations.poll;
+ lttng_stream_ring_buffer_file_operations.splice_read =
+ lib_ring_buffer_file_operations.splice_read;
+ lttng_stream_ring_buffer_file_operations.mmap =
+ lib_ring_buffer_file_operations.mmap;
+ lttng_stream_ring_buffer_file_operations.unlocked_ioctl =
+ lttng_stream_ring_buffer_ioctl;
+ lttng_stream_ring_buffer_file_operations.llseek =
+ lib_ring_buffer_file_operations.llseek;
+#ifdef CONFIG_COMPAT
+ lttng_stream_ring_buffer_file_operations.compat_ioctl =
+ lttng_stream_ring_buffer_compat_ioctl;
+#endif
+}
+
int __init lttng_abi_init(void)
{
int ret = 0;
ret = -ENOMEM;
goto error;
}
+ lttng_stream_override_ring_buffer_fops();
+
error:
return ret;
}
-void __exit lttng_abi_exit(void)
+/* No __exit annotation because used by init error path too. */
+void lttng_abi_exit(void)
{
if (lttng_proc_dentry)
remove_proc_entry("lttng", NULL);