X-Git-Url: http://git.lttng.org/?p=lttng-modules.git;a=blobdiff_plain;f=lttng-abi.c;h=01eb4d55addea3ea41b5def0ed79ca9efd91fcf1;hp=2b673776b44ce2e2a36c2567668ec297e1d99bd3;hb=cd4486798c2b046ea93b89439cd705e93c40b349;hpb=255e52a4f2d85eee50e5cde6436279ef74e61996 diff --git a/lttng-abi.c b/lttng-abi.c index 2b673776..01eb4d55 100644 --- a/lttng-abi.c +++ b/lttng-abi.c @@ -1,10 +1,11 @@ -/* - * lttng-abi.c +/* SPDX-License-Identifier: (GPL-2.0-only or LGPL-2.1-only) * - * Copyright 2010-2011 (c) - Mathieu Desnoyers + * lttng-abi.c * * LTTng ABI * + * Copyright (C) 2010-2012 Mathieu Desnoyers + * * Mimic system calls for: * - session creation, returns a file descriptor or failure. * - channel creation, returns a file descriptor or failure. @@ -20,8 +21,6 @@ * - Takes an instrumentation source as parameter * - e.g. tracepoints, dynamic_probes... * - Takes instrumentation source specific arguments. - * - * Dual LGPL v2.1/GPL v2 license. */ #include @@ -30,12 +29,21 @@ #include #include #include -#include "wrapper/vmalloc.h" /* for wrapper_vmalloc_sync_all() */ -#include "wrapper/ringbuffer/vfs.h" -#include "wrapper/poll.h" -#include "lttng-abi.h" -#include "lttng-events.h" -#include "lttng-tracer.h" +#include +#include /* for wrapper_vmalloc_sync_mappings() */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include /* * This is LTTng's own personal way to create a system call as an external @@ -43,22 +51,26 @@ */ static struct proc_dir_entry *lttng_proc_dentry; -static const struct file_operations lttng_fops; + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5,6,0)) +static const struct proc_ops lttng_proc_ops; +#else +static const struct file_operations lttng_proc_ops; +#endif + static const struct file_operations lttng_session_fops; static const struct file_operations lttng_channel_fops; static const struct file_operations lttng_metadata_fops; static const struct file_operations lttng_event_fops; +static struct file_operations lttng_stream_ring_buffer_file_operations; + +static int put_u64(uint64_t val, unsigned long arg); /* * Teardown management: opened file descriptors keep a refcount on the module, * 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) { @@ -69,7 +81,7 @@ int lttng_abi_create_session(void) session = lttng_session_create(); if (!session) return -ENOMEM; - session_fd = get_unused_fd(); + session_fd = lttng_get_unused_fd(); if (session_fd < 0) { ret = session_fd; goto fd_error; @@ -98,13 +110,13 @@ int lttng_abi_tracepoint_list(void) struct file *tracepoint_list_file; int file_fd, ret; - file_fd = get_unused_fd(); + file_fd = lttng_get_unused_fd(); if (file_fd < 0) { ret = file_fd; 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)) { @@ -115,49 +127,82 @@ int lttng_abi_tracepoint_list(void) if (ret < 0) goto open_error; fd_install(file_fd, tracepoint_list_file); + return file_fd; + +open_error: + fput(tracepoint_list_file); +file_error: + put_unused_fd(file_fd); +fd_error: + return ret; +} + +#ifndef CONFIG_HAVE_SYSCALL_TRACEPOINTS +static inline +int lttng_abi_syscall_list(void) +{ + return -ENOSYS; +} +#else +static +int lttng_abi_syscall_list(void) +{ + struct file *syscall_list_file; + int file_fd, ret; + + file_fd = lttng_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); return file_fd; open_error: - fput(tracepoint_list_file); + fput(syscall_list_file); file_error: put_unused_fd(file_fd); fd_error: return ret; } +#endif static -long lttng_abi_tracer_version(struct file *file, - struct lttng_kernel_tracer_version __user *uversion_param) +void lttng_abi_tracer_version(struct lttng_kernel_tracer_version *v) { - struct lttng_kernel_tracer_version v; - - v.version = LTTNG_VERSION; - v.patchlevel = LTTNG_PATCHLEVEL; - v.sublevel = LTTNG_SUBLEVEL; + v->major = LTTNG_MODULES_MAJOR_VERSION; + v->minor = LTTNG_MODULES_MINOR_VERSION; + v->patchlevel = LTTNG_MODULES_PATCHLEVEL_VERSION; +} - if (copy_to_user(uversion_param, &v, sizeof(v))) - return -EFAULT; - return 0; +static +void lttng_abi_tracer_abi_version(struct lttng_kernel_tracer_abi_version *v) +{ + v->major = LTTNG_MODULES_ABI_MAJOR_VERSION; + v->minor = LTTNG_MODULES_ABI_MINOR_VERSION; } static long lttng_abi_add_context(struct file *file, - struct lttng_kernel_context __user *ucontext_param, + struct lttng_kernel_context *context_param, struct lttng_ctx **ctx, struct lttng_session *session) { - struct lttng_kernel_context context_param; if (session->been_active) return -EPERM; - if (copy_from_user(&context_param, ucontext_param, sizeof(context_param))) - return -EFAULT; - - switch (context_param.ctx) { + switch (context_param->ctx) { case LTTNG_KERNEL_CONTEXT_PID: return lttng_add_pid_to_ctx(ctx); case LTTNG_KERNEL_CONTEXT_PRIO: @@ -175,13 +220,66 @@ long lttng_abi_add_context(struct file *file, case LTTNG_KERNEL_CONTEXT_VPPID: return lttng_add_vppid_to_ctx(ctx); case LTTNG_KERNEL_CONTEXT_PERF_COUNTER: - context_param.u.perf_counter.name[LTTNG_SYM_NAME_LEN - 1] = '\0'; - return lttng_add_perf_counter_to_ctx(context_param.u.perf_counter.type, - context_param.u.perf_counter.config, - context_param.u.perf_counter.name, + context_param->u.perf_counter.name[LTTNG_KERNEL_SYM_NAME_LEN - 1] = '\0'; + return lttng_add_perf_counter_to_ctx(context_param->u.perf_counter.type, + context_param->u.perf_counter.config, + context_param->u.perf_counter.name, ctx); case LTTNG_KERNEL_CONTEXT_PROCNAME: return lttng_add_procname_to_ctx(ctx); + case LTTNG_KERNEL_CONTEXT_HOSTNAME: + return lttng_add_hostname_to_ctx(ctx); + case LTTNG_KERNEL_CONTEXT_CPU_ID: + return lttng_add_cpu_id_to_ctx(ctx); + case LTTNG_KERNEL_CONTEXT_INTERRUPTIBLE: + return lttng_add_interruptible_to_ctx(ctx); + case LTTNG_KERNEL_CONTEXT_NEED_RESCHEDULE: + return lttng_add_need_reschedule_to_ctx(ctx); + case LTTNG_KERNEL_CONTEXT_PREEMPTIBLE: + return lttng_add_preemptible_to_ctx(ctx); + case LTTNG_KERNEL_CONTEXT_MIGRATABLE: + return lttng_add_migratable_to_ctx(ctx); + case LTTNG_KERNEL_CONTEXT_CALLSTACK_KERNEL: + case LTTNG_KERNEL_CONTEXT_CALLSTACK_USER: + return lttng_add_callstack_to_ctx(ctx, context_param->ctx); + case LTTNG_KERNEL_CONTEXT_CGROUP_NS: + return lttng_add_cgroup_ns_to_ctx(ctx); + case LTTNG_KERNEL_CONTEXT_IPC_NS: + return lttng_add_ipc_ns_to_ctx(ctx); + case LTTNG_KERNEL_CONTEXT_MNT_NS: + return lttng_add_mnt_ns_to_ctx(ctx); + case LTTNG_KERNEL_CONTEXT_NET_NS: + return lttng_add_net_ns_to_ctx(ctx); + case LTTNG_KERNEL_CONTEXT_PID_NS: + return lttng_add_pid_ns_to_ctx(ctx); + case LTTNG_KERNEL_CONTEXT_USER_NS: + return lttng_add_user_ns_to_ctx(ctx); + case LTTNG_KERNEL_CONTEXT_UTS_NS: + return lttng_add_uts_ns_to_ctx(ctx); + case LTTNG_KERNEL_CONTEXT_UID: + return lttng_add_uid_to_ctx(ctx); + case LTTNG_KERNEL_CONTEXT_EUID: + return lttng_add_euid_to_ctx(ctx); + case LTTNG_KERNEL_CONTEXT_SUID: + return lttng_add_suid_to_ctx(ctx); + case LTTNG_KERNEL_CONTEXT_GID: + return lttng_add_gid_to_ctx(ctx); + case LTTNG_KERNEL_CONTEXT_EGID: + return lttng_add_egid_to_ctx(ctx); + case LTTNG_KERNEL_CONTEXT_SGID: + return lttng_add_sgid_to_ctx(ctx); + case LTTNG_KERNEL_CONTEXT_VUID: + return lttng_add_vuid_to_ctx(ctx); + case LTTNG_KERNEL_CONTEXT_VEUID: + return lttng_add_veuid_to_ctx(ctx); + case LTTNG_KERNEL_CONTEXT_VSUID: + return lttng_add_vsuid_to_ctx(ctx); + case LTTNG_KERNEL_CONTEXT_VGID: + return lttng_add_vgid_to_ctx(ctx); + case LTTNG_KERNEL_CONTEXT_VEGID: + return lttng_add_vegid_to_ctx(ctx); + case LTTNG_KERNEL_CONTEXT_VSGID: + return lttng_add_vsgid_to_ctx(ctx); default: return -EINVAL; } @@ -203,6 +301,8 @@ long lttng_abi_add_context(struct file *file, * Returns a file descriptor listing available tracepoints * LTTNG_KERNEL_WAIT_QUIESCENT * Returns after all previously running probes have completed + * LTTNG_KERNEL_TRACER_ABI_VERSION + * Returns the LTTng kernel tracer ABI version * * The returned session will be deleted when its file descriptor is closed. */ @@ -210,16 +310,74 @@ static long lttng_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { switch (cmd) { + case LTTNG_KERNEL_OLD_SESSION: case LTTNG_KERNEL_SESSION: return lttng_abi_create_session(); + case LTTNG_KERNEL_OLD_TRACER_VERSION: + { + struct lttng_kernel_tracer_version v; + struct lttng_kernel_old_tracer_version oldv; + struct lttng_kernel_old_tracer_version *uversion = + (struct lttng_kernel_old_tracer_version __user *) arg; + + lttng_abi_tracer_version(&v); + oldv.major = v.major; + oldv.minor = v.minor; + oldv.patchlevel = v.patchlevel; + + if (copy_to_user(uversion, &oldv, sizeof(oldv))) + return -EFAULT; + return 0; + } case LTTNG_KERNEL_TRACER_VERSION: - return lttng_abi_tracer_version(file, - (struct lttng_kernel_tracer_version __user *) arg); + { + struct lttng_kernel_tracer_version version; + struct lttng_kernel_tracer_version *uversion = + (struct lttng_kernel_tracer_version __user *) arg; + + lttng_abi_tracer_version(&version); + + if (copy_to_user(uversion, &version, sizeof(version))) + return -EFAULT; + return 0; + } + case LTTNG_KERNEL_TRACER_ABI_VERSION: + { + struct lttng_kernel_tracer_abi_version version; + struct lttng_kernel_tracer_abi_version *uversion = + (struct lttng_kernel_tracer_abi_version __user *) arg; + + lttng_abi_tracer_abi_version(&version); + + if (copy_to_user(uversion, &version, sizeof(version))) + return -EFAULT; + return 0; + } + 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 0; + case LTTNG_KERNEL_OLD_CALIBRATE: + { + struct lttng_kernel_old_calibrate __user *ucalibrate = + (struct lttng_kernel_old_calibrate __user *) arg; + struct lttng_kernel_old_calibrate old_calibrate; + struct lttng_kernel_calibrate calibrate; + int ret; + + if (copy_from_user(&old_calibrate, ucalibrate, sizeof(old_calibrate))) + return -EFAULT; + calibrate.type = old_calibrate.type; + ret = lttng_calibrate(&calibrate); + if (copy_to_user(ucalibrate, &old_calibrate, sizeof(old_calibrate))) + return -EFAULT; + return ret; + } case LTTNG_KERNEL_CALIBRATE: { struct lttng_kernel_calibrate __user *ucalibrate = @@ -239,47 +397,26 @@ long lttng_ioctl(struct file *file, unsigned int cmd, unsigned long arg) } } -static const struct file_operations lttng_fops = { +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5,6,0)) +static const struct proc_ops lttng_proc_ops = { + .proc_ioctl = lttng_ioctl, +#ifdef CONFIG_COMPAT + .proc_compat_ioctl = lttng_ioctl, +#endif /* CONFIG_COMPAT */ +}; +#else +static const struct file_operations lttng_proc_ops = { .owner = THIS_MODULE, .unlocked_ioctl = lttng_ioctl, #ifdef CONFIG_COMPAT .compat_ioctl = lttng_ioctl, -#endif +#endif /* CONFIG_COMPAT */ }; - -/* - * We tolerate no failure in this function (if one happens, we print a dmesg - * error, but cannot return any error, because the channel information is - * invariant. - */ -static -void lttng_metadata_create_events(struct file *channel_file) -{ - struct lttng_channel *channel = channel_file->private_data; - static struct lttng_kernel_event metadata_params = { - .instrumentation = LTTNG_KERNEL_TRACEPOINT, - .name = "lttng_metadata", - }; - struct lttng_event *event; - - /* - * We tolerate no failure path after event creation. It will stay - * invariant for the rest of the session. - */ - event = lttng_event_create(channel, &metadata_params, NULL, NULL); - if (!event) { - goto create_error; - } - return; - -create_error: - WARN_ON(1); - return; /* not allowed to return error */ -} +#endif static int lttng_abi_create_channel(struct file *session_file, - struct lttng_kernel_channel __user *uchan_param, + struct lttng_kernel_channel *chan_param, enum channel_type channel_type) { struct lttng_session *session = session_file->private_data; @@ -287,13 +424,10 @@ int lttng_abi_create_channel(struct file *session_file, const char *transport_name; struct lttng_channel *chan; struct file *chan_file; - struct lttng_kernel_channel chan_param; int chan_fd; int ret = 0; - if (copy_from_user(&chan_param, uchan_param, sizeof(chan_param))) - return -EFAULT; - chan_fd = get_unused_fd(); + chan_fd = lttng_get_unused_fd(); if (chan_fd < 0) { ret = chan_fd; goto fd_error; @@ -306,7 +440,7 @@ int lttng_abi_create_channel(struct file *session_file, fops = <tng_metadata_fops; break; } - + chan_file = anon_inode_getfile("[lttng_channel]", fops, NULL, O_RDWR); @@ -316,20 +450,20 @@ int lttng_abi_create_channel(struct file *session_file, } switch (channel_type) { case PER_CPU_CHANNEL: - if (chan_param.output == LTTNG_KERNEL_SPLICE) { - transport_name = chan_param.overwrite ? + if (chan_param->output == LTTNG_KERNEL_SPLICE) { + transport_name = chan_param->overwrite ? "relay-overwrite" : "relay-discard"; - } else if (chan_param.output == LTTNG_KERNEL_MMAP) { - transport_name = chan_param.overwrite ? + } else if (chan_param->output == LTTNG_KERNEL_MMAP) { + transport_name = chan_param->overwrite ? "relay-overwrite-mmap" : "relay-discard-mmap"; } else { return -EINVAL; } break; case METADATA_CHANNEL: - if (chan_param.output == LTTNG_KERNEL_SPLICE) + if (chan_param->output == LTTNG_KERNEL_SPLICE) transport_name = "relay-metadata"; - else if (chan_param.output == LTTNG_KERNEL_MMAP) + else if (chan_param->output == LTTNG_KERNEL_MMAP) transport_name = "relay-metadata-mmap"; else return -EINVAL; @@ -338,15 +472,20 @@ int lttng_abi_create_channel(struct file *session_file, transport_name = ""; break; } + if (!atomic_long_add_unless(&session_file->f_count, 1, LONG_MAX)) { + ret = -EOVERFLOW; + goto refcount_error; + } /* * We tolerate no failure path after channel creation. It will stay * invariant for the rest of the session. */ chan = lttng_channel_create(session, transport_name, NULL, - chan_param.subbuf_size, - chan_param.num_subbuf, - chan_param.switch_timer_interval, - chan_param.read_timer_interval); + chan_param->subbuf_size, + chan_param->num_subbuf, + chan_param->switch_timer_interval, + chan_param->read_timer_interval, + channel_type); if (!chan) { ret = -EINVAL; goto chan_error; @@ -354,17 +493,12 @@ 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; - lttng_metadata_create_events(chan_file); - } - - /* The channel created holds a reference on the session */ - atomic_long_inc(&session_file->f_count); return chan_fd; chan_error: + atomic_long_dec(&session_file->f_count); +refcount_error: fput(chan_file); file_error: put_unused_fd(chan_fd); @@ -372,6 +506,61 @@ fd_error: return ret; } +static +int lttng_abi_session_set_name(struct lttng_session *session, + struct lttng_kernel_session_name *name) +{ + size_t len; + + len = strnlen(name->name, LTTNG_KERNEL_SESSION_NAME_LEN); + + if (len == LTTNG_KERNEL_SESSION_NAME_LEN) { + /* Name is too long/malformed */ + return -EINVAL; + } + + strcpy(session->name, name->name); + return 0; +} + +static +int lttng_abi_session_set_creation_time(struct lttng_session *session, + struct lttng_kernel_session_creation_time *time) +{ + size_t len; + + len = strnlen(time->iso8601, LTTNG_KERNEL_SESSION_CREATION_TIME_ISO8601_LEN); + + if (len == LTTNG_KERNEL_SESSION_CREATION_TIME_ISO8601_LEN) { + /* Time is too long/malformed */ + return -EINVAL; + } + + strcpy(session->creation_time, time->iso8601); + return 0; +} + +static +enum tracker_type get_tracker_type(struct lttng_kernel_tracker_args *tracker) +{ + switch (tracker->type) { + case LTTNG_KERNEL_TRACKER_PID: + return TRACKER_PID; + case LTTNG_KERNEL_TRACKER_VPID: + return TRACKER_VPID; + case LTTNG_KERNEL_TRACKER_UID: + return TRACKER_UID; + case LTTNG_KERNEL_TRACKER_VUID: + return TRACKER_VUID; + case LTTNG_KERNEL_TRACKER_GID: + return TRACKER_GID; + case LTTNG_KERNEL_TRACKER_VGID: + return TRACKER_VGID; + default: + return TRACKER_UNKNOWN; + } +} + /** * lttng_session_ioctl - lttng session fd ioctl * @@ -388,6 +577,14 @@ fd_error: * Disables tracing for a session (strong disable) * LTTNG_KERNEL_METADATA * Returns a LTTng metadata file descriptor + * LTTNG_KERNEL_SESSION_TRACK_PID + * Add PID to session PID tracker + * LTTNG_KERNEL_SESSION_UNTRACK_PID + * Remove PID from session PID tracker + * LTTNG_KERNEL_SESSION_TRACK_ID + * Add ID to tracker + * LTTNG_KERNEL_SESSION_UNTRACK_ID + * Remove ID from tracker * * The returned channel will be deleted when its file descriptor is closed. */ @@ -395,22 +592,143 @@ static long lttng_session_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { struct lttng_session *session = file->private_data; + struct lttng_kernel_channel chan_param; + struct lttng_kernel_old_channel old_chan_param; switch (cmd) { + case LTTNG_KERNEL_OLD_CHANNEL: + { + if (copy_from_user(&old_chan_param, + (struct lttng_kernel_old_channel __user *) arg, + sizeof(struct lttng_kernel_old_channel))) + return -EFAULT; + chan_param.overwrite = old_chan_param.overwrite; + chan_param.subbuf_size = old_chan_param.subbuf_size; + chan_param.num_subbuf = old_chan_param.num_subbuf; + chan_param.switch_timer_interval = old_chan_param.switch_timer_interval; + chan_param.read_timer_interval = old_chan_param.read_timer_interval; + chan_param.output = old_chan_param.output; + + return lttng_abi_create_channel(file, &chan_param, + PER_CPU_CHANNEL); + } case LTTNG_KERNEL_CHANNEL: - return lttng_abi_create_channel(file, + { + if (copy_from_user(&chan_param, (struct lttng_kernel_channel __user *) arg, + sizeof(struct lttng_kernel_channel))) + return -EFAULT; + return lttng_abi_create_channel(file, &chan_param, PER_CPU_CHANNEL); + } + case LTTNG_KERNEL_OLD_SESSION_START: + case LTTNG_KERNEL_OLD_ENABLE: case LTTNG_KERNEL_SESSION_START: case LTTNG_KERNEL_ENABLE: return lttng_session_enable(session); + case LTTNG_KERNEL_OLD_SESSION_STOP: + case LTTNG_KERNEL_OLD_DISABLE: case LTTNG_KERNEL_SESSION_STOP: case LTTNG_KERNEL_DISABLE: return lttng_session_disable(session); + case LTTNG_KERNEL_OLD_METADATA: + { + if (copy_from_user(&old_chan_param, + (struct lttng_kernel_old_channel __user *) arg, + sizeof(struct lttng_kernel_old_channel))) + return -EFAULT; + chan_param.overwrite = old_chan_param.overwrite; + chan_param.subbuf_size = old_chan_param.subbuf_size; + chan_param.num_subbuf = old_chan_param.num_subbuf; + chan_param.switch_timer_interval = old_chan_param.switch_timer_interval; + chan_param.read_timer_interval = old_chan_param.read_timer_interval; + chan_param.output = old_chan_param.output; + + return lttng_abi_create_channel(file, &chan_param, + METADATA_CHANNEL); + } case LTTNG_KERNEL_METADATA: - return lttng_abi_create_channel(file, - (struct lttng_kernel_channel __user *) arg, + { + if (copy_from_user(&chan_param, + (struct lttng_kernel_channel __user *) arg, + sizeof(struct lttng_kernel_channel))) + return -EFAULT; + return lttng_abi_create_channel(file, &chan_param, METADATA_CHANNEL); + } + case LTTNG_KERNEL_SESSION_TRACK_PID: + return lttng_session_track_id(session, TRACKER_PID, (int) arg); + case LTTNG_KERNEL_SESSION_UNTRACK_PID: + return lttng_session_untrack_id(session, TRACKER_PID, (int) arg); + case LTTNG_KERNEL_SESSION_TRACK_ID: + { + struct lttng_kernel_tracker_args tracker; + enum tracker_type tracker_type; + + if (copy_from_user(&tracker, + (struct lttng_kernel_tracker_args __user *) arg, + sizeof(struct lttng_kernel_tracker_args))) + return -EFAULT; + tracker_type = get_tracker_type(&tracker); + if (tracker_type == TRACKER_UNKNOWN) + return -EINVAL; + return lttng_session_track_id(session, tracker_type, tracker.id); + } + case LTTNG_KERNEL_SESSION_UNTRACK_ID: + { + struct lttng_kernel_tracker_args tracker; + enum tracker_type tracker_type; + + if (copy_from_user(&tracker, + (struct lttng_kernel_tracker_args __user *) arg, + sizeof(struct lttng_kernel_tracker_args))) + return -EFAULT; + tracker_type = get_tracker_type(&tracker); + if (tracker_type == TRACKER_UNKNOWN) + return -EINVAL; + return lttng_session_untrack_id(session, tracker_type, + tracker.id); + } + case LTTNG_KERNEL_SESSION_LIST_TRACKER_PIDS: + return lttng_session_list_tracker_ids(session, TRACKER_PID); + case LTTNG_KERNEL_SESSION_LIST_TRACKER_IDS: + { + struct lttng_kernel_tracker_args tracker; + enum tracker_type tracker_type; + + if (copy_from_user(&tracker, + (struct lttng_kernel_tracker_args __user *) arg, + sizeof(struct lttng_kernel_tracker_args))) + return -EFAULT; + tracker_type = get_tracker_type(&tracker); + if (tracker_type == TRACKER_UNKNOWN) + return -EINVAL; + return lttng_session_list_tracker_ids(session, tracker_type); + } + case LTTNG_KERNEL_SESSION_METADATA_REGEN: + return lttng_session_metadata_regenerate(session); + case LTTNG_KERNEL_SESSION_STATEDUMP: + return lttng_session_statedump(session); + case LTTNG_KERNEL_SESSION_SET_NAME: + { + struct lttng_kernel_session_name name; + + if (copy_from_user(&name, + (struct lttng_kernel_session_name __user *) arg, + sizeof(struct lttng_kernel_session_name))) + return -EFAULT; + return lttng_abi_session_set_name(session, &name); + } + case LTTNG_KERNEL_SESSION_SET_CREATION_TIME: + { + struct lttng_kernel_session_creation_time time; + + if (copy_from_user(&time, + (struct lttng_kernel_session_creation_time __user *) arg, + sizeof(struct lttng_kernel_session_creation_time))) + return -EFAULT; + return lttng_abi_session_set_creation_time(session, &time); + } default: return -ENOIOCTLCMD; } @@ -443,129 +761,538 @@ 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_channel *channel = channel_file->private_data; - struct lib_ring_buffer *buf; - int stream_fd, ret; - struct file *stream_file; + struct lttng_metadata_stream *stream = filp->private_data; + struct lib_ring_buffer *buf = stream->priv; + int finalized; + unsigned int mask = 0; - buf = channel->ops->buffer_read_open(channel->chan); - if (!buf) - return -ENOENT; + if (filp->f_mode & FMODE_READ) { + poll_wait_set_exclusive(wait); + poll_wait(filp, &stream->read_wait, wait); - stream_fd = get_unused_fd(); - if (stream_fd < 0) { - ret = stream_fd; - goto fd_error; + 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; + + mutex_lock(&stream->metadata_cache->lock); + if (stream->metadata_cache->metadata_written > + stream->metadata_out) + mask |= POLLIN; + mutex_unlock(&stream->metadata_cache->lock); } - stream_file = anon_inode_getfile("[lttng_stream]", - &lib_ring_buffer_file_operations, - buf, O_RDWR); - if (IS_ERR(stream_file)) { - ret = PTR_ERR(stream_file); - goto file_error; + + return mask; +} + +static +void lttng_metadata_ring_buffer_ioctl_put_next_subbuf(struct file *filp, + unsigned int cmd, unsigned long arg) +{ + struct lttng_metadata_stream *stream = filp->private_data; + + stream->metadata_out = stream->metadata_in; +} + +/* + * Reset the counter of how much metadata has been consumed to 0. That way, + * the consumer receives the content of the metadata cache unchanged. This is + * different from the metadata_regenerate where the offset from epoch is + * resampled, here we want the exact same content as the last time the metadata + * was generated. This command is only possible if all the metadata written + * in the cache has been output to the metadata stream to avoid corrupting the + * metadata file. + * + * Return 0 on success, a negative value on error. + */ +static +int lttng_metadata_cache_dump(struct lttng_metadata_stream *stream) +{ + int ret; + struct lttng_metadata_cache *cache = stream->metadata_cache; + + mutex_lock(&cache->lock); + if (stream->metadata_out != cache->metadata_written) { + ret = -EBUSY; + goto end; } - /* - * OPEN_FMODE, called within anon_inode_getfile/alloc_file, don't honor - * FMODE_LSEEK, FMODE_PREAD nor FMODE_PWRITE. We need to read from this - * file descriptor, so we set FMODE_PREAD here. - */ - stream_file->f_mode |= FMODE_PREAD; - fd_install(stream_fd, stream_file); - /* - * The stream holds a reference to the channel within the generic ring - * buffer library, so no need to hold a refcount on the channel and - * session files here. - */ - return stream_fd; + stream->metadata_out = 0; + stream->metadata_in = 0; + wake_up_interruptible(&stream->read_wait); + ret = 0; -file_error: - put_unused_fd(stream_fd); -fd_error: - channel->ops->buffer_read_close(buf); +end: + mutex_unlock(&cache->lock); return ret; } static -int lttng_abi_create_event(struct file *channel_file, - struct lttng_kernel_event __user *uevent_param) +long lttng_metadata_ring_buffer_ioctl(struct file *filp, + unsigned int cmd, unsigned long arg) { - struct lttng_channel *channel = channel_file->private_data; - struct lttng_event *event; - struct lttng_kernel_event event_param; - int event_fd, ret; - struct file *event_file; + int ret; + struct lttng_metadata_stream *stream = filp->private_data; + struct lib_ring_buffer *buf = stream->priv; - if (copy_from_user(&event_param, uevent_param, sizeof(event_param))) - return -EFAULT; - event_param.name[LTTNG_SYM_NAME_LEN - 1] = '\0'; - switch (event_param.instrumentation) { - case LTTNG_KERNEL_KRETPROBE: - event_param.u.kretprobe.symbol_name[LTTNG_SYM_NAME_LEN - 1] = '\0'; - break; - case LTTNG_KERNEL_KPROBE: - event_param.u.kprobe.symbol_name[LTTNG_SYM_NAME_LEN - 1] = '\0'; - break; - case LTTNG_KERNEL_FUNCTION: - event_param.u.ftrace.symbol_name[LTTNG_SYM_NAME_LEN - 1] = '\0'; - break; - default: + switch (cmd) { + case RING_BUFFER_GET_NEXT_SUBBUF: + { + struct lttng_metadata_stream *stream = filp->private_data; + struct lib_ring_buffer *buf = stream->priv; + struct channel *chan = buf->backend.chan; + + ret = lttng_metadata_output_channel(stream, chan); + if (ret > 0) { + lib_ring_buffer_switch_slow(buf, SWITCH_ACTIVE); + ret = 0; + } else if (ret < 0) + goto err; break; } - switch (event_param.instrumentation) { - default: - event_fd = get_unused_fd(); - if (event_fd < 0) { - ret = event_fd; - goto fd_error; - } - event_file = anon_inode_getfile("[lttng_event]", - <tng_event_fops, - NULL, O_RDWR); - if (IS_ERR(event_file)) { - ret = PTR_ERR(event_file); - goto file_error; - } + case RING_BUFFER_GET_SUBBUF: + { /* - * We tolerate no failure path after event creation. It - * will stay invariant for the rest of the session. + * Random access is not allowed for metadata channel. */ - event = lttng_event_create(channel, &event_param, NULL, NULL); - if (!event) { - ret = -EINVAL; - goto event_error; - } - event_file->private_data = event; - fd_install(event_fd, event_file); - /* The event holds a reference on the channel */ - atomic_long_inc(&channel_file->f_count); - break; - case LTTNG_KERNEL_SYSCALL: + return -ENOSYS; + } + case RING_BUFFER_FLUSH_EMPTY: /* Fall-through. */ + case RING_BUFFER_FLUSH: + { + struct lttng_metadata_stream *stream = filp->private_data; + struct lib_ring_buffer *buf = stream->priv; + struct channel *chan = buf->backend.chan; + /* - * Only all-syscall tracing supported for now. + * Before doing the actual ring buffer flush, write up to one + * packet of metadata in the ring buffer. */ - if (event_param.name[0] != '\0') - return -EINVAL; - ret = lttng_syscalls_register(channel, NULL); - if (ret) - goto fd_error; - event_fd = 0; + ret = lttng_metadata_output_channel(stream, chan); + if (ret < 0) + goto err; break; } - return event_fd; + case RING_BUFFER_GET_METADATA_VERSION: + { + struct lttng_metadata_stream *stream = filp->private_data; -event_error: - fput(event_file); -file_error: - put_unused_fd(event_fd); -fd_error: - return ret; -} + return put_u64(stream->version, arg); + } + case RING_BUFFER_METADATA_CACHE_DUMP: + { + struct lttng_metadata_stream *stream = filp->private_data; -/** - * lttng_channel_ioctl - lttng syscall through ioctl + return lttng_metadata_cache_dump(stream); + } + default: + break; + } + /* PUT_SUBBUF is the one from lib ring buffer, unmodified. */ + + /* Performing lib ring buffer ioctl after our own. */ + ret = lib_ring_buffer_ioctl(filp, cmd, arg, buf); + if (ret < 0) + goto err; + + switch (cmd) { + case RING_BUFFER_PUT_NEXT_SUBBUF: + { + lttng_metadata_ring_buffer_ioctl_put_next_subbuf(filp, + cmd, arg); + break; + } + default: + break; + } +err: + return ret; +} + +#ifdef CONFIG_COMPAT +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_NEXT_SUBBUF: + { + struct lttng_metadata_stream *stream = filp->private_data; + struct lib_ring_buffer *buf = stream->priv; + struct channel *chan = buf->backend.chan; + + ret = lttng_metadata_output_channel(stream, chan); + if (ret > 0) { + lib_ring_buffer_switch_slow(buf, SWITCH_ACTIVE); + ret = 0; + } else if (ret < 0) + goto err; + break; + } + case RING_BUFFER_GET_SUBBUF: + { + /* + * Random access is not allowed for metadata channel. + */ + return -ENOSYS; + } + case RING_BUFFER_FLUSH_EMPTY: /* Fall-through. */ + case RING_BUFFER_FLUSH: + { + struct lttng_metadata_stream *stream = filp->private_data; + struct lib_ring_buffer *buf = stream->priv; + struct channel *chan = buf->backend.chan; + + /* + * Before doing the actual ring buffer flush, write up to one + * packet of metadata in the ring buffer. + */ + ret = lttng_metadata_output_channel(stream, chan); + if (ret < 0) + goto err; + break; + } + case RING_BUFFER_GET_METADATA_VERSION: + { + struct lttng_metadata_stream *stream = filp->private_data; + + return put_u64(stream->version, arg); + } + case RING_BUFFER_METADATA_CACHE_DUMP: + { + struct lttng_metadata_stream *stream = filp->private_data; + + return lttng_metadata_cache_dump(stream); + } + default: + break; + } + /* PUT_SUBBUF is the one from lib ring buffer, unmodified. */ + + /* Performing lib ring buffer ioctl after our own. */ + ret = lib_ring_buffer_compat_ioctl(filp, cmd, arg, buf); + if (ret < 0) + goto err; + + switch (cmd) { + case RING_BUFFER_PUT_NEXT_SUBBUF: + { + lttng_metadata_ring_buffer_ioctl_put_next_subbuf(filp, + cmd, arg); + break; + } + default: + break; + } +err: + return ret; +} +#endif + +/* + * This is not used by anonymous file descriptors. This code is left + * there if we ever want to implement an inode with open() operation. + */ +static +int lttng_metadata_ring_buffer_open(struct inode *inode, struct file *file) +{ + struct lttng_metadata_stream *stream = inode->i_private; + struct lib_ring_buffer *buf = stream->priv; + + file->private_data = buf; + /* + * Since life-time of metadata cache differs from that of + * session, we need to keep our own reference on the transport. + */ + if (!try_module_get(stream->transport->owner)) { + printk(KERN_WARNING "LTT : Can't lock transport module.\n"); + return -EBUSY; + } + return lib_ring_buffer_open(inode, file, buf); +} + +static +int lttng_metadata_ring_buffer_release(struct inode *inode, struct file *file) +{ + struct lttng_metadata_stream *stream = file->private_data; + struct lib_ring_buffer *buf = stream->priv; + + kref_put(&stream->metadata_cache->refcount, metadata_cache_destroy); + module_put(stream->transport->owner); + return lib_ring_buffer_release(inode, file, buf); +} + +static +ssize_t lttng_metadata_ring_buffer_splice_read(struct file *in, loff_t *ppos, + struct pipe_inode_info *pipe, size_t len, + unsigned int flags) +{ + struct lttng_metadata_stream *stream = in->private_data; + struct lib_ring_buffer *buf = stream->priv; + + return lib_ring_buffer_splice_read(in, ppos, pipe, len, + flags, buf); +} + +static +int lttng_metadata_ring_buffer_mmap(struct file *filp, + struct vm_area_struct *vma) +{ + struct lttng_metadata_stream *stream = filp->private_data; + struct lib_ring_buffer *buf = stream->priv; + + return lib_ring_buffer_mmap(filp, vma, buf); +} + +static +const struct file_operations lttng_metadata_ring_buffer_file_operations = { + .owner = THIS_MODULE, + .open = lttng_metadata_ring_buffer_open, + .release = lttng_metadata_ring_buffer_release, + .poll = lttng_metadata_ring_buffer_poll, + .splice_read = lttng_metadata_ring_buffer_splice_read, + .mmap = lttng_metadata_ring_buffer_mmap, + .unlocked_ioctl = lttng_metadata_ring_buffer_ioctl, + .llseek = vfs_lib_ring_buffer_no_llseek, +#ifdef CONFIG_COMPAT + .compat_ioctl = lttng_metadata_ring_buffer_compat_ioctl, +#endif +}; + +static +int lttng_abi_create_stream_fd(struct file *channel_file, void *stream_priv, + const struct file_operations *fops) +{ + int stream_fd, ret; + struct file *stream_file; + + stream_fd = lttng_get_unused_fd(); + if (stream_fd < 0) { + ret = stream_fd; + goto fd_error; + } + stream_file = anon_inode_getfile("[lttng_stream]", fops, + stream_priv, O_RDWR); + if (IS_ERR(stream_file)) { + ret = PTR_ERR(stream_file); + goto file_error; + } + /* + * OPEN_FMODE, called within anon_inode_getfile/alloc_file, don't honor + * FMODE_LSEEK, FMODE_PREAD nor FMODE_PWRITE. We need to read from this + * file descriptor, so we set FMODE_PREAD here. + */ + stream_file->f_mode |= FMODE_PREAD; + fd_install(stream_fd, stream_file); + /* + * The stream holds a reference to the channel within the generic ring + * buffer library, so no need to hold a refcount on the channel and + * session files here. + */ + return stream_fd; + +file_error: + put_unused_fd(stream_fd); +fd_error: + return ret; +} + +static +int lttng_abi_open_stream(struct file *channel_file) +{ + struct lttng_channel *channel = channel_file->private_data; + struct lib_ring_buffer *buf; + int ret; + void *stream_priv; + + buf = channel->ops->buffer_read_open(channel->chan); + if (!buf) + return -ENOENT; + + stream_priv = buf; + ret = lttng_abi_create_stream_fd(channel_file, stream_priv, + <tng_stream_ring_buffer_file_operations); + if (ret < 0) + goto fd_error; + + return ret; + +fd_error: + channel->ops->buffer_read_close(buf); + return ret; +} + +static +int lttng_abi_open_metadata_stream(struct file *channel_file) +{ + struct lttng_channel *channel = channel_file->private_data; + struct lttng_session *session = channel->session; + struct lib_ring_buffer *buf; + int ret; + struct lttng_metadata_stream *metadata_stream; + void *stream_priv; + + buf = channel->ops->buffer_read_open(channel->chan); + if (!buf) + return -ENOENT; + + metadata_stream = kzalloc(sizeof(struct lttng_metadata_stream), + GFP_KERNEL); + if (!metadata_stream) { + ret = -ENOMEM; + goto nomem; + } + metadata_stream->metadata_cache = session->metadata_cache; + init_waitqueue_head(&metadata_stream->read_wait); + metadata_stream->priv = buf; + stream_priv = metadata_stream; + metadata_stream->transport = channel->transport; + + /* + * Since life-time of metadata cache differs from that of + * session, we need to keep our own reference on the transport. + */ + if (!try_module_get(metadata_stream->transport->owner)) { + printk(KERN_WARNING "LTT : Can't lock transport module.\n"); + ret = -EINVAL; + goto notransport; + } + + if (!lttng_kref_get(&session->metadata_cache->refcount)) { + ret = -EOVERFLOW; + goto kref_error; + } + + ret = lttng_abi_create_stream_fd(channel_file, stream_priv, + <tng_metadata_ring_buffer_file_operations); + if (ret < 0) + goto fd_error; + + list_add(&metadata_stream->list, + &session->metadata_cache->metadata_stream); + return ret; + +fd_error: + kref_put(&session->metadata_cache->refcount, metadata_cache_destroy); +kref_error: + module_put(metadata_stream->transport->owner); +notransport: + kfree(metadata_stream); +nomem: + channel->ops->buffer_read_close(buf); + return ret; +} + +static +int lttng_abi_create_event(struct file *channel_file, + struct lttng_kernel_event *event_param) +{ + struct lttng_channel *channel = channel_file->private_data; + int event_fd, ret; + struct file *event_file; + void *priv; + + event_param->name[LTTNG_KERNEL_SYM_NAME_LEN - 1] = '\0'; + switch (event_param->instrumentation) { + case LTTNG_KERNEL_KRETPROBE: + event_param->u.kretprobe.symbol_name[LTTNG_KERNEL_SYM_NAME_LEN - 1] = '\0'; + break; + case LTTNG_KERNEL_KPROBE: + event_param->u.kprobe.symbol_name[LTTNG_KERNEL_SYM_NAME_LEN - 1] = '\0'; + break; + case LTTNG_KERNEL_FUNCTION: + WARN_ON_ONCE(1); + /* Not implemented. */ + break; + default: + break; + } + event_fd = lttng_get_unused_fd(); + if (event_fd < 0) { + ret = event_fd; + goto fd_error; + } + event_file = anon_inode_getfile("[lttng_event]", + <tng_event_fops, + NULL, O_RDWR); + if (IS_ERR(event_file)) { + ret = PTR_ERR(event_file); + goto file_error; + } + /* The event holds a reference on the channel */ + if (!atomic_long_add_unless(&channel_file->f_count, 1, LONG_MAX)) { + ret = -EOVERFLOW; + goto refcount_error; + } + if (event_param->instrumentation == LTTNG_KERNEL_TRACEPOINT + || event_param->instrumentation == LTTNG_KERNEL_SYSCALL) { + struct lttng_enabler *enabler; + + if (strutils_is_star_glob_pattern(event_param->name)) { + /* + * If the event name is a star globbing pattern, + * we create the special star globbing enabler. + */ + enabler = lttng_enabler_create(LTTNG_ENABLER_STAR_GLOB, + event_param, channel); + } else { + enabler = lttng_enabler_create(LTTNG_ENABLER_NAME, + event_param, channel); + } + priv = enabler; + } else { + struct lttng_event *event; + + /* + * We tolerate no failure path after event creation. It + * will stay invariant for the rest of the session. + */ + event = lttng_event_create(channel, event_param, + NULL, NULL, + event_param->instrumentation); + WARN_ON_ONCE(!event); + if (IS_ERR(event)) { + ret = PTR_ERR(event); + goto event_error; + } + priv = event; + } + event_file->private_data = priv; + fd_install(event_fd, event_file); + return event_fd; + +event_error: + atomic_long_dec(&channel_file->f_count); +refcount_error: + fput(event_file); +file_error: + put_unused_fd(event_fd); +fd_error: + return ret; +} + +/** + * lttng_channel_ioctl - lttng syscall through ioctl * * @file: the file * @cmd: the command @@ -592,18 +1319,156 @@ long lttng_channel_ioctl(struct file *file, unsigned int cmd, unsigned long arg) struct lttng_channel *channel = file->private_data; switch (cmd) { + case LTTNG_KERNEL_OLD_STREAM: case LTTNG_KERNEL_STREAM: return lttng_abi_open_stream(file); + case LTTNG_KERNEL_OLD_EVENT: + { + struct lttng_kernel_event *uevent_param; + struct lttng_kernel_old_event *old_uevent_param; + int ret; + + uevent_param = kmalloc(sizeof(struct lttng_kernel_event), + GFP_KERNEL); + if (!uevent_param) { + ret = -ENOMEM; + goto old_event_end; + } + old_uevent_param = kmalloc( + sizeof(struct lttng_kernel_old_event), + GFP_KERNEL); + if (!old_uevent_param) { + ret = -ENOMEM; + goto old_event_error_free_param; + } + if (copy_from_user(old_uevent_param, + (struct lttng_kernel_old_event __user *) arg, + sizeof(struct lttng_kernel_old_event))) { + ret = -EFAULT; + goto old_event_error_free_old_param; + } + + memcpy(uevent_param->name, old_uevent_param->name, + sizeof(uevent_param->name)); + uevent_param->instrumentation = + old_uevent_param->instrumentation; + + switch (old_uevent_param->instrumentation) { + case LTTNG_KERNEL_KPROBE: + uevent_param->u.kprobe.addr = + old_uevent_param->u.kprobe.addr; + uevent_param->u.kprobe.offset = + old_uevent_param->u.kprobe.offset; + memcpy(uevent_param->u.kprobe.symbol_name, + old_uevent_param->u.kprobe.symbol_name, + sizeof(uevent_param->u.kprobe.symbol_name)); + break; + case LTTNG_KERNEL_KRETPROBE: + uevent_param->u.kretprobe.addr = + old_uevent_param->u.kretprobe.addr; + uevent_param->u.kretprobe.offset = + old_uevent_param->u.kretprobe.offset; + memcpy(uevent_param->u.kretprobe.symbol_name, + old_uevent_param->u.kretprobe.symbol_name, + sizeof(uevent_param->u.kretprobe.symbol_name)); + break; + case LTTNG_KERNEL_FUNCTION: + WARN_ON_ONCE(1); + /* Not implemented. */ + break; + default: + break; + } + ret = lttng_abi_create_event(file, uevent_param); + +old_event_error_free_old_param: + kfree(old_uevent_param); +old_event_error_free_param: + kfree(uevent_param); +old_event_end: + return ret; + } case LTTNG_KERNEL_EVENT: - return lttng_abi_create_event(file, (struct lttng_kernel_event __user *) arg); + { + struct lttng_kernel_event uevent_param; + + if (copy_from_user(&uevent_param, + (struct lttng_kernel_event __user *) arg, + sizeof(uevent_param))) + return -EFAULT; + return lttng_abi_create_event(file, &uevent_param); + } + case LTTNG_KERNEL_OLD_CONTEXT: + { + struct lttng_kernel_context *ucontext_param; + struct lttng_kernel_old_context *old_ucontext_param; + int ret; + + ucontext_param = kmalloc(sizeof(struct lttng_kernel_context), + GFP_KERNEL); + if (!ucontext_param) { + ret = -ENOMEM; + goto old_ctx_end; + } + old_ucontext_param = kmalloc(sizeof(struct lttng_kernel_old_context), + GFP_KERNEL); + if (!old_ucontext_param) { + ret = -ENOMEM; + goto old_ctx_error_free_param; + } + + if (copy_from_user(old_ucontext_param, + (struct lttng_kernel_old_context __user *) arg, + sizeof(struct lttng_kernel_old_context))) { + ret = -EFAULT; + goto old_ctx_error_free_old_param; + } + ucontext_param->ctx = old_ucontext_param->ctx; + memcpy(ucontext_param->padding, old_ucontext_param->padding, + sizeof(ucontext_param->padding)); + /* only type that uses the union */ + if (old_ucontext_param->ctx == LTTNG_KERNEL_CONTEXT_PERF_COUNTER) { + ucontext_param->u.perf_counter.type = + old_ucontext_param->u.perf_counter.type; + ucontext_param->u.perf_counter.config = + old_ucontext_param->u.perf_counter.config; + memcpy(ucontext_param->u.perf_counter.name, + old_ucontext_param->u.perf_counter.name, + sizeof(ucontext_param->u.perf_counter.name)); + } + + ret = lttng_abi_add_context(file, + ucontext_param, + &channel->ctx, channel->session); + +old_ctx_error_free_old_param: + kfree(old_ucontext_param); +old_ctx_error_free_param: + kfree(ucontext_param); +old_ctx_end: + return ret; + } case LTTNG_KERNEL_CONTEXT: - return lttng_abi_add_context(file, + { + struct lttng_kernel_context ucontext_param; + + if (copy_from_user(&ucontext_param, (struct lttng_kernel_context __user *) arg, + sizeof(ucontext_param))) + return -EFAULT; + return lttng_abi_add_context(file, + &ucontext_param, &channel->ctx, channel->session); + } + case LTTNG_KERNEL_OLD_ENABLE: case LTTNG_KERNEL_ENABLE: return lttng_channel_enable(channel); + 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; } @@ -626,8 +1491,9 @@ static 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; } @@ -671,6 +1537,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) { + fput(channel->session->file); + lttng_metadata_channel_destroy(channel); + } + + return 0; +} + static const struct file_operations lttng_channel_fops = { .owner = THIS_MODULE, .release = lttng_channel_release, @@ -683,7 +1562,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, @@ -708,17 +1587,73 @@ static const struct file_operations lttng_metadata_fops = { static long lttng_event_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { - struct lttng_event *event = file->private_data; + struct lttng_event *event; + struct lttng_enabler *enabler; + enum lttng_event_type *evtype = file->private_data; switch (cmd) { + case LTTNG_KERNEL_OLD_CONTEXT: + { + /* Not implemented */ + return -ENOSYS; + } case LTTNG_KERNEL_CONTEXT: - return lttng_abi_add_context(file, - (struct lttng_kernel_context __user *) arg, - &event->ctx, event->chan->session); + { + /* Not implemented */ + return -ENOSYS; + } + case LTTNG_KERNEL_OLD_ENABLE: case LTTNG_KERNEL_ENABLE: - return lttng_event_enable(event); + switch (*evtype) { + case LTTNG_TYPE_EVENT: + event = file->private_data; + return lttng_event_enable(event); + case LTTNG_TYPE_ENABLER: + enabler = file->private_data; + return lttng_enabler_enable(enabler); + default: + WARN_ON_ONCE(1); + return -ENOSYS; + } + case LTTNG_KERNEL_OLD_DISABLE: case LTTNG_KERNEL_DISABLE: - return lttng_event_disable(event); + switch (*evtype) { + case LTTNG_TYPE_EVENT: + event = file->private_data; + return lttng_event_disable(event); + case LTTNG_TYPE_ENABLER: + enabler = file->private_data; + return lttng_enabler_disable(enabler); + default: + WARN_ON_ONCE(1); + return -ENOSYS; + } + case LTTNG_KERNEL_FILTER: + switch (*evtype) { + case LTTNG_TYPE_EVENT: + return -EINVAL; + case LTTNG_TYPE_ENABLER: + { + enabler = file->private_data; + return lttng_enabler_attach_bytecode(enabler, + (struct lttng_kernel_filter_bytecode __user *) arg); + } + default: + WARN_ON_ONCE(1); + return -ENOSYS; + } + case LTTNG_KERNEL_ADD_CALLSITE: + switch (*evtype) { + case LTTNG_TYPE_EVENT: + event = file->private_data; + return lttng_event_add_callsite(event, + (struct lttng_kernel_event_callsite __user *) arg); + case LTTNG_TYPE_ENABLER: + return -EINVAL; + default: + WARN_ON_ONCE(1); + return -ENOSYS; + } default: return -ENOIOCTLCMD; } @@ -727,10 +1662,29 @@ long lttng_event_ioctl(struct file *file, unsigned int cmd, unsigned long arg) static int lttng_event_release(struct inode *inode, struct file *file) { - struct lttng_event *event = file->private_data; + struct lttng_event *event; + struct lttng_enabler *enabler; + enum lttng_event_type *evtype = file->private_data; + + if (!evtype) + return 0; + + switch (*evtype) { + case LTTNG_TYPE_EVENT: + event = file->private_data; + if (event) + fput(event->chan->file); + break; + case LTTNG_TYPE_ENABLER: + enabler = file->private_data; + if (enabler) + fput(enabler->chan->file); + break; + default: + WARN_ON_ONCE(1); + break; + } - if (event) - fput(event->chan->file); return 0; } @@ -744,25 +1698,276 @@ static const struct file_operations lttng_event_fops = { #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); + } + case LTTNG_RING_BUFFER_GET_SEQ_NUM: + { + uint64_t seq; + + ret = ops->sequence_number(config, buf, &seq); + if (ret < 0) + goto error; + return put_u64(seq, arg); + } + case LTTNG_RING_BUFFER_INSTANCE_ID: + { + uint64_t id; + + ret = ops->instance_id(config, buf, &id); + if (ret < 0) + goto error; + return put_u64(id, 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); + } + case LTTNG_RING_BUFFER_COMPAT_GET_SEQ_NUM: + { + uint64_t seq; + + ret = ops->sequence_number(config, buf, &seq); + if (ret < 0) + goto error; + return put_u64(seq, arg); + } + case LTTNG_RING_BUFFER_COMPAT_INSTANCE_ID: + { + uint64_t id; + + ret = ops->instance_id(config, buf, &id); + if (ret < 0) + goto error; + return put_u64(id, 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; - wrapper_vmalloc_sync_all(); - lttng_proc_dentry = proc_create_data("lttng", S_IWUSR, NULL, - <tng_fops, NULL); - + wrapper_vmalloc_sync_mappings(); + lttng_clock_ref(); + + ret = lttng_tp_mempool_init(); + if (ret) { + goto error; + } + + lttng_proc_dentry = proc_create_data("lttng", S_IRUSR | S_IWUSR, NULL, + <tng_proc_ops, NULL); + if (!lttng_proc_dentry) { printk(KERN_ERR "Error creating LTTng control file\n"); ret = -ENOMEM; goto error; } + lttng_stream_override_ring_buffer_fops(); + return 0; + error: + lttng_tp_mempool_destroy(); + lttng_clock_unref(); return ret; } -void __exit lttng_abi_exit(void) +/* No __exit annotation because used by init error path too. */ +void lttng_abi_exit(void) { + lttng_tp_mempool_destroy(); + lttng_clock_unref(); if (lttng_proc_dentry) remove_proc_entry("lttng", NULL); }