Please see comments below, * Tom Zanussi ([EMAIL PROTECTED]) wrote: > The Generic Tracing and Control Interface (GTSC) code. > > Signed-off-by: Tom Zanussi <[EMAIL PROTECTED]> > Signed-off-by: David Wilder <[EMAIL PROTECTED]> > --- > include/linux/gtsc.h | 104 +++++++++ > lib/Kconfig | 10 > lib/Makefile | 2 > lib/gtsc.c | 558 > +++++++++++++++++++++++++++++++++++++++++++++++++++ > 4 files changed, 674 insertions(+) > > diff --git a/include/linux/gtsc.h b/include/linux/gtsc.h > new file mode 100644 > index 0000000..cbb2601 > --- /dev/null > +++ b/include/linux/gtsc.h > @@ -0,0 +1,104 @@ > +/* > + * GTSC defines and function prototypes > + * > + * Copyright (C) 2006 IBM Inc. > + * > + * Tom Zanussi <[EMAIL PROTECTED]> > + * Martin Hunt <[EMAIL PROTECTED]> > + * David Wilder <[EMAIL PROTECTED]> > + * > + * This program is free software; you can redistribute it and/or modify > + * it under the terms of the GNU General Public License version 2 as > + * published by the Free Software Foundation. > + * > + * This program is distributed in the hope that it will be useful, > + * but WITHOUT ANY WARRANTY; without even the implied warranty of > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the > + * GNU General Public License for more details. > + * > + * You should have received a copy of the GNU General Public License > + * along with this program; if not, write to the Free Software > + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA > + * > + */ > +#ifndef _LINUX_GTSC_H > +#define _LINUX_GTSC_H > + > +#include <linux/relay.h> > + > +/* > + * GTSC channel flags > + */ > +#define TRACE_GLOBAL_CHANNEL 0x01 > +#define TRACE_FLIGHT_CHANNEL 0x02 > +#define TRACE_DISABLE_STATE 0x04 > + > +enum trace_state { > + TRACE_SETUP, > + TRACE_RUNNING, > + TRACE_STOPPED, > +}; > + > +#define TRACE_ROOT_NAME_SIZE 64 /* Max root dir identifier */ > +#define TRACE_NAME_SIZE 64 /* Max trace identifier */ > + > +/* > + * Global root user information > + */ > +struct trace_root { > + struct list_head list; > + char name[TRACE_ROOT_NAME_SIZE]; > + struct dentry *root; > + unsigned int users; > +}; > + > +/* > + * Client information > + */ > +struct trace_info { > + enum trace_state state; > + struct dentry *state_file; > + struct rchan *rchan; > + struct dentry *dir; > + struct dentry *dropped_file; > + struct dentry *reset_consumed_file; > + struct dentry *nr_sub_file; > + struct dentry *sub_size_file; > + atomic_t dropped; > + struct trace_root *root; > + void *private_data; > + unsigned int flags; > + unsigned int buf_size; > + unsigned int buf_nr; > +}; > + > +#ifdef CONFIG_GTSC > +static inline int trace_running(struct trace_info *trace) > +{ > + return trace->state == TRACE_RUNNING; > +} > +struct trace_info *trace_setup(const char *root, const char *name, > + u32 buf_size, u32 buf_nr, u32 flags); > +int trace_start(struct trace_info *trace); > +int trace_stop(struct trace_info *trace); > +void trace_cleanup_channel(struct trace_info *gt); > +void trace_cleanup(struct trace_info *gt); > +unsigned long long trace_timestamp(void); > +#else > +static inline struct trace_info *trace_setup(const char *root, > + const char *name, > + u32 buf_size, > + u32 buf_nr, > + u32 flags) > +{ > + return NULL; > +} > +static inline int trace_running(struct trace_info *trace) { return 0; } > +static inline int trace_start(struct trace_info *trace) { return -EINVAL; } > +static inline int trace_stop(struct trace_info *trace) {} > +static inline void trace_cleanup_channel(struct trace_info *trace) {} > +static inline void trace_cleanup(struct trace_info *trace) {} > +static inline unsigned long long trace_timestamp(void) { return 0; } > +#endif > +
I don't see any correct case where functions such as trace_start or trace_stop should be declared empty. Correct dependencies on CONFIG_GTSC should probably be used. > +#endif > diff --git a/lib/Kconfig b/lib/Kconfig > index 2e7ae6b..b3931f3 100644 > --- a/lib/Kconfig > +++ b/lib/Kconfig > @@ -124,4 +124,14 @@ config HAS_DMA > depends on !NO_DMA > default y > > +config GTSC > + bool "Generic Trace Setup and Control" > + select RELAY > + select DEBUG_FS > + help > + This option provides support for the setup, teardown and control > + of tracing channels from kernel code. It also provides trace > + information and control to userspace via a set of debugfs control > + files. If unsure, say N. > + > endmenu > diff --git a/lib/Makefile b/lib/Makefile > index c8c8e20..d9e68fa 100644 > --- a/lib/Makefile > +++ b/lib/Makefile > @@ -62,6 +62,8 @@ obj-$(CONFIG_FAULT_INJECTION) += fault-inject.o > > lib-$(CONFIG_GENERIC_BUG) += bug.o > > +obj-$(CONFIG_GTSC) += gtsc.o > + > hostprogs-y := gen_crc32table > clean-files := crc32table.h > > diff --git a/lib/gtsc.c b/lib/gtsc.c > new file mode 100644 > index 0000000..ecd0ddf > --- /dev/null > +++ b/lib/gtsc.c > @@ -0,0 +1,558 @@ > +/* > + * Based on blktrace code, Copyright (C) 2006 Jens Axboe <[EMAIL PROTECTED]> > + * Moved to utt.c by Tom Zanussi <[EMAIL PROTECTED]>, 2006 > + * Additional contributions by: > + * Martin Hunt <[EMAIL PROTECTED]>, 2007 > + * David Wilder <[EMAIL PROTECTED]>, 2007 > + * Renamed to gtsc <dwilder.ibm.com>, 2007 > + * > + * This program is free software; you can redistribute it and/or modify > + * it under the terms of the GNU General Public License version 2 as > + * published by the Free Software Foundation. > + * > + * This program is distributed in the hope that it will be useful, > + * but WITHOUT ANY WARRANTY; without even the implied warranty of > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the > + * GNU General Public License for more details. > + * > + * You should have received a copy of the GNU General Public License > + * along with this program; if not, write to the Free Software > + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA > + * > + */ > + > +#include <linux/kernel.h> > +#include <linux/module.h> > +#include <linux/init.h> > +#include <linux/mutex.h> > +#include <linux/debugfs.h> > +#include <linux/gtsc.h> > + > +static LIST_HEAD(trace_roots); > +static DEFINE_MUTEX(trace_mutex); > + > +static int state_open(struct inode *inode, struct file *filp) > +{ > + filp->private_data = inode->i_private; > + > + return 0; > +} > + > +static ssize_t state_read(struct file *filp, char __user *buffer, > + size_t count, loff_t *ppos) > +{ > + struct trace_info *trace = filp->private_data; > + char *buf = "trace not started\n"; > + > + if (trace->state == TRACE_STOPPED) > + buf = "stopped\n"; > + else if (trace->state == TRACE_RUNNING) > + buf = "running\n"; > + return simple_read_from_buffer(buffer, count, ppos, buf, strlen(buf)); > +} > + > + > +static ssize_t state_write(struct file *filp, const char __user *buffer, > + size_t count, loff_t *ppos) > +{ > + struct trace_info *trace = filp->private_data; > + char buf[16] = { '\0' }; > + int ret; > + > + if (trace->flags & TRACE_DISABLE_STATE) > + return -EINVAL; > + > + if (count > sizeof(buf) - 1) > + return -EINVAL; > + > + if (copy_from_user(buf, buffer, count)) > + return -EFAULT; > + > + buf[count] = '\0'; > + > + if (strncmp(buf, "start", strlen("start")) == 0 ) { > + ret = trace_start(trace); > + if (ret) > + return ret; > + } else if (strncmp(buffer, "stop", strlen("stop")) == 0) > + trace_stop(trace); > + else > + return -EINVAL; > + > + return count; > +} > + > + > +static struct file_operations state_fops = { > + .owner = THIS_MODULE, > + .open = state_open, > + .read = state_read, > + .write = state_write, > +}; > + > + > +static void remove_root(struct trace_info *trace) > +{ > + if (trace->root->root && simple_empty(trace->root->root)) { > + debugfs_remove(trace->root->root); > + list_del(&trace->root->list); > + kfree(trace->root); > + trace->root = NULL; > + } > +} > + > + > +static void remove_tree(struct trace_info *trace) > +{ > + mutex_lock(&trace_mutex); > + > + debugfs_remove(trace->dir); > + > + if (--trace->root->users == 0) > + remove_root(trace); > + > + mutex_unlock(&trace_mutex); > +} > + > + > +static struct trace_root *lookup_root(const char *root) > +{ > + struct list_head *pos; > + struct trace_root *r; > + > + list_for_each(pos, &trace_roots) { > + r = list_entry(pos, struct trace_root, list); > + if (!strcmp(r->name, root)) > + return r; > + } > + > + r = kzalloc(sizeof(struct trace_root), GFP_KERNEL); > + if (!r) > + return NULL; > + > + strlcpy(r->name, root, sizeof(r->name)); > + > + r->root = debugfs_create_dir(root, NULL); > + if (r->root) > + list_add(&r->list, &trace_roots); > + > + return r; > +} > + > + > +static struct dentry *create_tree(struct trace_info *trace, > + const char *root, > + const char *name) > +{ > + struct dentry *dir = NULL; > + > + if (root == NULL || name == NULL) > + return NULL; > + > + mutex_lock(&trace_mutex); > + > + trace->root = lookup_root(root); > + if (!trace->root) > + goto err; > + > + dir = debugfs_create_dir(name, trace->root->root); > + if (dir) > + trace->root->users++; > + else > + remove_root(trace); > + > +err: > + mutex_unlock(&trace_mutex); > + > + return dir; > +} > + > + > +static int dropped_open(struct inode *inode, struct file *filp) > +{ > + filp->private_data = inode->i_private; > + > + return 0; > +} > + > + > +static ssize_t dropped_read(struct file *filp, char __user *buffer, > + size_t count, loff_t *ppos) > +{ > + struct trace_info *trace = filp->private_data; > + char buf[16]; > + > + snprintf(buf, sizeof(buf), "%u\n", atomic_read(&trace->dropped)); > + > + return simple_read_from_buffer(buffer, count, ppos, buf, strlen(buf)); > +} > + > + > +static struct file_operations dropped_fops = { > + .owner = THIS_MODULE, > + .open = dropped_open, > + .read = dropped_read, > +}; > + > +static int reset_consumed_open(struct inode *inode, struct file *filp) > +{ > + filp->private_data = inode->i_private; > + > + return 0; > +} > + > +static ssize_t reset_consumed_write(struct file *filp, > + const char __user *buffer, > + size_t count, > + loff_t *ppos) > +{ > + struct trace_info *trace = filp->private_data; > + > + relay_reset_consumed(trace->rchan); > + > + return count; > +} > + > +struct file_operations reset_consumed_fops = { > + .owner = THIS_MODULE, > + .open = reset_consumed_open, > + .write = reset_consumed_write > +}; > + > +static int sub_size_open(struct inode *inode, struct file *filp) > +{ > + filp->private_data = inode->i_private; > + > + return 0; > +} > + > +static ssize_t sub_size_read(struct file *filp, char __user *buffer, > + size_t count, loff_t *ppos) > +{ > + struct trace_info *trace = filp->private_data; > + char buf[32]; > + > + snprintf(buf, sizeof(buf), "%u\n", > + (unsigned int)trace->rchan->subbuf_size); > + > + return simple_read_from_buffer(buffer, count, ppos, buf, strlen(buf)); > +} > + > +struct file_operations sub_size_fops = { > + .owner = THIS_MODULE, > + .open = sub_size_open, > + .read = sub_size_read, > +}; > + > +static int nr_sub_open(struct inode *inode, struct file *filp) > +{ > + filp->private_data = inode->i_private; > + return 0; > +} > + > +static ssize_t nr_sub_read(struct file *filp, char __user *buffer, > + size_t count, loff_t *ppos) > +{ > + struct trace_info *trace = filp->private_data; > + char buf[32]; > + > + snprintf(buf, sizeof(buf), "%u\n", > + (unsigned int)trace->rchan->n_subbufs); > + > + return simple_read_from_buffer(buffer, count, ppos, buf, strlen(buf)); > +} > + > +struct file_operations nr_sub_fops = { > + .owner = THIS_MODULE, > + .open = nr_sub_open, > + .read = nr_sub_read, > +}; > + > +/* > + * Keep track of how many times we encountered a full subbuffer, to aid > + * the user space app in telling how many lost events there were. > + */ > +static int subbuf_start_callback(struct rchan_buf *buf, void *subbuf, > + void *prev_subbuf, size_t prev_padding) > +{ > + struct trace_info *trace = buf->chan->private_data; > + > + if (trace->flags & TRACE_FLIGHT_CHANNEL) > + return 1; > + > + if (!relay_buf_full(buf)) > + return 1; > + > + atomic_inc(&trace->dropped); > + > + return 0; > +} > + > + > +static int remove_buf_file_callback(struct dentry *dentry) > +{ > + debugfs_remove(dentry); > + > + return 0; > +} > + > + > +static struct dentry *create_buf_file_callback(const char *filename, > + struct dentry *parent, > + int mode, > + struct rchan_buf *buf, > + int *is_global) > +{ > + return debugfs_create_file(filename, mode, parent, buf, > + &relay_file_operations); > +} > + > + > +static struct dentry *create_global_buf_file_callback(const char *filename, > + struct dentry *parent, > + int mode, > + struct rchan_buf *buf, > + int *is_global) > +{ > + *is_global = 1; > + > + return debugfs_create_file(filename, mode, parent, buf, > + &relay_file_operations); > +} > + > + > +static struct rchan_callbacks relay_callbacks = { > + .subbuf_start = subbuf_start_callback, > + .create_buf_file = create_buf_file_callback, > + .remove_buf_file = remove_buf_file_callback, > +}; > +static struct rchan_callbacks relay_callbacks_global = { > + .subbuf_start = subbuf_start_callback, > + .create_buf_file = create_global_buf_file_callback, > + .remove_buf_file = remove_buf_file_callback, > +}; > + > + > +static void remove_controls(struct trace_info *trace) > +{ > + if (trace->state_file) > + debugfs_remove(trace->state_file); > + if (trace->dropped_file) > + debugfs_remove(trace->dropped_file); > + if (trace->reset_consumed_file) > + debugfs_remove(trace->reset_consumed_file); > + if (trace->nr_sub_file) > + debugfs_remove(trace->nr_sub_file); > + if (trace->sub_size_file) > + debugfs_remove(trace->sub_size_file); > + if (trace->dir) > + remove_tree(trace); > +} > + > +/* > + * Setup controls for tracing. > + */ > +static struct trace_info *setup_controls(const char *root, > + const char *name, u32 flags) > +{ > + struct trace_info *trace = NULL; > + > + trace = kzalloc(sizeof(*trace), GFP_KERNEL); > + if (!trace) > + goto err; > + > + trace->dir = create_tree(trace, root, name); > + if (!trace->dir) > + goto err; > + > + trace->state_file = debugfs_create_file("state", 0444, trace->dir, > + trace, &state_fops); > + if (!trace->state_file) > + goto err; > + > + if (!flags & TRACE_FLIGHT_CHANNEL) { > + trace->dropped_file = debugfs_create_file("dropped", 0444, > + trace->dir, > + trace, > + &dropped_fops); > + if (!trace->dropped_file) > + goto err; > + } > + > + if (flags & TRACE_FLIGHT_CHANNEL) { > + trace->reset_consumed_file = debugfs_create_file("rewind", > + 0444, > + trace->dir, > + trace, > + > &reset_consumed_fops); > + if (!trace->reset_consumed_file) > + goto err; > + } > + > + trace->nr_sub_file = debugfs_create_file("nr_sub", 0444, > + trace->dir, trace, > + &nr_sub_fops); > + if (!trace->nr_sub_file) > + goto err; > + > + trace->sub_size_file = debugfs_create_file("sub_size", 0444, > + trace->dir, trace, > + &sub_size_fops); > + if (!trace->sub_size_file) > + goto err; > + > + return trace; > +err: > + if (trace) { > + remove_controls(trace); > + kfree(trace); > + } > + > + return NULL; > +} > + > + > +int trace_setup_channel(struct trace_info *trace, u32 buf_size, u32 buf_nr, > + u32 flags) > +{ > + if (!buf_size || !buf_nr) > + return -EINVAL; > + > + if (flags & TRACE_GLOBAL_CHANNEL) > + trace->rchan = relay_open("trace", trace->dir, buf_size, > + buf_nr, &relay_callbacks_global, > + trace); > + else > + trace->rchan = relay_open("trace", trace->dir, buf_size, > + buf_nr, &relay_callbacks, trace); > + > + if (!trace->rchan) > + return -ENOMEM; > + > + trace->flags = flags; > + trace->state = TRACE_SETUP; > + > + return 0; > +} > + > + > +/** > + * trace_setup: create a new gtsc trace handle > + * > + * @root: The root directory name in the root of the debugfs > + * to place trace directories. Created as needed. > + * @name: Trace directory name, created in @root > + * @buf_size: size of the relay sub-buffers > + * @buf_nr: number of relay sub-buffers > + * @flags: Option selection (see GTSC channel flags definitions) > + * default values when flags=0 are: use per-CPU buffering, > + * use non-overwrite mode. See Documentation/gtsc.txt for details. > + * > + * returns a gtsc_trace handle or NULL, if setup failed. > + */ > +struct trace_info *trace_setup(const char *root, const char *name, > + u32 buf_size, u32 buf_nr, u32 flags) > +{ > + struct trace_info *trace = NULL; > + > + trace = setup_controls(root, name, flags); > + if (!trace) > + return NULL; > + > + trace->buf_size = buf_size; > + trace->buf_nr = buf_nr; > + trace->flags = flags; > + trace->state = TRACE_SETUP; > + > + return trace; > +} > +EXPORT_SYMBOL_GPL(trace_setup); > + > + > +/** > + * trace_start: start tracing > + * > + * @trace: gtsc trace handle to start or stop. > + * > + * returns 0 if successful. > + */ > +int trace_start(struct trace_info *trace) > +{ > + /* > + * For starting a trace, we can transition from a setup or stopped > + * trace. > + */ > + if (trace->state == TRACE_RUNNING) > + return -EINVAL; > + > + if (trace->state == TRACE_SETUP) { > + int ret; > + > + ret = trace_setup_channel(trace, trace->buf_size, > + trace->buf_nr, trace->flags); > + if (ret) > + return ret; > + } > + > + trace->state = TRACE_RUNNING; > + > + return 0; > +} > +EXPORT_SYMBOL_GPL(trace_start); > + > +/** > + * trace_stop: stop tracing > + * > + * @trace: gtsc trace handle to start or stop. > + */ > +int trace_stop(struct trace_info *trace) > +{ > + int ret = -EINVAL; > + > + /* > + * For stopping a trace, the state must be running > + */ > + if (trace->state == TRACE_RUNNING) { > + trace->state = TRACE_STOPPED; > + relay_flush(trace->rchan); > + ret = 0; > + } > + > + return ret; > +} > +EXPORT_SYMBOL_GPL(trace_stop); > + I am curious: how do you make sure that every users of the relay channel will be stopped and not in the reserve code anymore when the trace_cleanup is called ? > + > +/** > + * trace_cleanup_channel: destroys the gtsc channel only > + * > + * @trace: gtsc trace handle to cleanup > + */ > +void trace_cleanup_channel(struct trace_info *trace) > +{ > + if (trace->rchan) > + relay_close(trace->rchan); > + trace->rchan = NULL; > +} > +EXPORT_SYMBOL_GPL(trace_cleanup_channel); > + > + > +/** > + * trace_cleanup: destroys the gtsc channel, control files and dir > + * > + * @trace: gtsc trace handle to cleanup > + */ > +void trace_cleanup(struct trace_info *trace) > +{ > + trace_cleanup_channel(trace); > + remove_controls(trace); > + kfree(trace); > +} > +EXPORT_SYMBOL_GPL(trace_cleanup); > + > + > +unsigned long long trace_timestamp(void) > +{ > + return sched_clock(); Can be non monotonic on multiple CPUs. > +} > +EXPORT_SYMBOL_GPL(trace_timestamp); > > > > - > To unsubscribe from this list: send the line "unsubscribe linux-kernel" in > the body of a message to [EMAIL PROTECTED] > More majordomo info at http://vger.kernel.org/majordomo-info.html > Please read the FAQ at http://www.tux.org/lkml/ > -- Mathieu Desnoyers Computer Engineering Ph.D. Student, Ecole Polytechnique de Montreal OpenPGP key fingerprint: 8CD5 52C3 8E3C 4140 715F BA06 3F25 A8FE 3BAE 9A68 - To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to [EMAIL PROTECTED] More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/