Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1758674AbYHDEIy (ORCPT ); Mon, 4 Aug 2008 00:08:54 -0400 Received: (majordomo@vger.kernel.org) by vger.kernel.org id S1751092AbYHDEIo (ORCPT ); Mon, 4 Aug 2008 00:08:44 -0400 Received: from e28smtp06.in.ibm.com ([59.145.155.6]:41337 "EHLO e28esmtp06.in.ibm.com" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S1751175AbYHDEIm (ORCPT ); Mon, 4 Aug 2008 00:08:42 -0400 Date: Mon, 4 Aug 2008 09:38:09 +0530 From: "K.Prasad" To: akpm@linux-foundation.org Cc: linux-kernel@vger.kernel.org, dwilder@us.ibm.com, hch@infradead.org Subject: [Patch 2/2] Renaming lib/trace.[ch] files to kernel/relay_debugfs.[ch] and enhancements Message-ID: <20080804040809.GC6415@in.ibm.com> Reply-To: prasad@linux.vnet.ibm.com References: <20080804040439.GA6415@in.ibm.com> MIME-Version: 1.0 Content-Type: text/plain; charset=us-ascii Content-Disposition: inline In-Reply-To: <20080804040439.GA6415@in.ibm.com> User-Agent: Mutt/1.5.18 (2008-05-17) Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Length: 57978 Lines: 2151 This patch renames the lib/trace.[ch] files to kernel/relay_debugfs.[ch]. Also present are the changes to rename the "trace_*" functions to "relay_*". Two new functions which provide an easy-to-use interface for relay called relay_printk() and relay_dump() are also introduced (earlier called as trace_printk() and trace_dump()). Signed-off-by: K.Prasad --- Documentation/filesystems/relay.txt | 22 + include/linux/relay_debugfs.h | 155 +++++++ include/linux/trace.h | 99 ---- init/Kconfig | 9 kernel/Makefile | 1 kernel/relay_debugfs.c | 760 ++++++++++++++++++++++++++++++++++++ lib/Kconfig | 9 lib/Makefile | 2 lib/trace.c | 563 -------------------------- samples/Kconfig | 8 samples/Makefile | 2 samples/relay/Makefile | 4 samples/relay/fork_trace.c | 132 ++++++ samples/trace/Makefile | 4 samples/trace/fork_new_trace.c | 99 ++++ samples/trace/fork_trace.c | 132 ------ 16 files changed, 1187 insertions(+), 814 deletions(-) Index: linux-trace-relay-2.6.27-rc1-mm1/include/linux/relay_debugfs.h =================================================================== --- /dev/null +++ linux-trace-relay-2.6.27-rc1-mm1/include/linux/relay_debugfs.h @@ -0,0 +1,155 @@ +/* + * RELAY DEBUGFS defines and function prototypes + * + * Copyright (C) 2007 IBM Inc. + * + * Tom Zanussi + * Martin Hunt + * David Wilder + * + * 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_RELAY_DEBUGFS_H +#define _LINUX_RELAY_DEBUGFS_H + +#include + +/* + * RELAY DEBUGFS channel flags + */ +#define RELAY_GLOBAL_CHANNEL 0x01 +#define RELAY_FLIGHT_CHANNEL 0x02 +#define RELAY_DISABLE_STATE 0x04 + +enum relay_state { + RELAY_SETUP, + RELAY_RUNNING, + RELAY_STOPPED, +}; + +enum relay_dir_state { + RELAY_PARENT_DIR_ABSENT, + RELAY_PARENT_DIR_EXISTS, + RELAY_DIR_EXISTS +}; + +#define RELAY_ROOT_NAME_SIZE 64 /* Max root dir identifier */ +#define RELAY_NAME_SIZE 64 /* Max relay identifier */ + +/* + * Buffers for use by relay_printk + */ +#define DEFAULT_RELAY_BUF_SIZE 4096 +#define DEFAULT_RELAY_SUB_BUF_NR 40 +/* + * Global root user information + */ +struct relay_root { + struct list_head list; + char name[RELAY_ROOT_NAME_SIZE]; + struct dentry *root; + unsigned int users; +}; + +/* + * Client information + */ +struct relay_info { + struct mutex state_mutex; /* Used to protect state changes */ + enum relay_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 relay_root *root; + void *private_data; + unsigned int flags; + unsigned int buf_size; + unsigned int buf_nr; + spinlock_t relay_lock; +}; + +struct relay_printk_data { + char *parent_dir; + char *dir; + int exists; + int buf_size; + int sub_buf_size; + unsigned long flags; + struct relay_info *ti; +}; + +/* + * Information about every 'relay debugfs' directory + */ +struct relay_dir { + struct list_head relay_dir_list; + char relay_dir_name[RELAY_NAME_SIZE]; + struct dentry *relay_root; + struct dentry *relay_dir; + struct relay_info *ti; +}; + +#ifdef CONFIG_RELAY_DEBUGFS +static inline int relay_running(struct relay_info *relay) +{ + return relay->state == RELAY_RUNNING; +} +struct relay_info *relay_setup(const char *root, const char *name, + u32 buf_size, u32 buf_nr, u32 flags); +int relay_start(struct relay_info *relay); +int relay_stop(struct relay_info *relay); +void relay_cleanup(struct relay_info *relay); +int relay_exists(const char *parent_dir, const char *dir, + struct relay_info **ti); +void relay_cleanup_all(const char *parent_dir); +int relay_printk(struct relay_printk_data *dpk, char *format, ...); +int relay_dump(struct relay_printk_data *dpk, const void *output, + const int output_len); +#else +static inline struct relay_info *relay_setup(const char *root, + const char *name, u32 buf_size, + u32 buf_nr, u32 flags) +{ + return NULL; +} +static inline int relay_start(struct relay_info *relay) { return -EINVAL; } +static inline int relay_stop(struct relay_info *relay) { return -EINVAL; } +static inline int relay_running(struct relay_info *relay) { return 0; } +static inline void relay_cleanup(struct relay_info *relay) {} +static inline int relay_exists(const char *parent_dir, const char *dir, + struct relay_info **ti) +{ + return -EINVAL; +} +static inline void relay_cleanup_all(const char *parent_dir) {} +static inline int relay_printk(struct relay_printk_data *dpk, char *format, + ...) +{ + return -EINVAL; +} +int relay_dump(struct relay_printk_data *dpk, const void *output, + const int output_len) +{ + return -EINVAL; +} + +#endif + +#endif /* ifdef CONFIG_RELAY_DEBUGFS */ Index: linux-trace-relay-2.6.27-rc1-mm1/include/linux/trace.h =================================================================== --- linux-trace-relay-2.6.27-rc1-mm1.orig/include/linux/trace.h +++ /dev/null @@ -1,99 +0,0 @@ -/* - * TRACE defines and function prototypes - * - * Copyright (C) 2007 IBM Inc. - * - * Tom Zanussi - * Martin Hunt - * David Wilder - * - * 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_TRACE_H -#define _LINUX_TRACE_H - -#include - -/* - * TRACE 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 { - struct mutex state_mutex; /* Used to protect state changes */ - 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_TRACE -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(struct trace_info *trace); -#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_start(struct trace_info *trace) { return -EINVAL; } -static inline int trace_stop(struct trace_info *trace) { return -EINVAL; } -static inline int trace_running(struct trace_info *trace) { return 0; } -static inline void trace_cleanup(struct trace_info *trace) {} -#endif - -#endif Index: linux-trace-relay-2.6.27-rc1-mm1/kernel/relay_debugfs.c =================================================================== --- /dev/null +++ linux-trace-relay-2.6.27-rc1-mm1/kernel/relay_debugfs.c @@ -0,0 +1,760 @@ +/* + * Based on blktrace code, Copyright (C) 2006 Jens Axboe + * Moved to utt.c by Tom Zanussi , 2006 + * Additional contributions by: + * Martin Hunt , 2007 + * David Wilder , 2007 + * Renamed to trace , 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 +#include +#include +#include +#include +#include + +static LIST_HEAD(relay_roots); +static LIST_HEAD(relay_dirs); +static DEFINE_MUTEX(relay_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 relay_info *relay = filp->private_data; + char *buf = "relay not started\n"; + + if (relay->state == RELAY_STOPPED) + buf = "stopped\n"; + else if (relay->state == RELAY_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 relay_info *relay = filp->private_data; + char buf[16]; + int ret; + + if (relay->flags & RELAY_DISABLE_STATE) + return -EINVAL; + + if (count > sizeof(buf) - 1) + return -EINVAL; + + if (copy_from_user(buf, buffer, count)) + return -EFAULT; + + buf[count-1] = '\0'; + + if (strcmp(buf, "start") == 0) { + ret = relay_start(relay); + if (ret) + return ret; + } else if (strcmp(buf, "stop") == 0) + relay_stop(relay); + 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 relay_info *relay) +{ + if (relay->root->root && simple_empty(relay->root->root)) { + debugfs_remove(relay->root->root); + list_del(&relay->root->list); + kfree(relay->root); + relay->root = NULL; + } +} + +static void remove_tree(struct relay_info *relay) +{ + struct list_head *pos, *temp; + struct relay_dir *dr = NULL; + + mutex_lock(&relay_mutex); + debugfs_remove(relay->dir); + + list_for_each_safe(pos, temp, &relay_dirs) { + dr = list_entry(pos, struct relay_dir, relay_dir_list); + if (dr->ti == relay) { + list_del(pos); + kfree(dr); + } + } + + if (relay->root) { + if (--relay->root->users == 0) + remove_root(relay); + } + + mutex_unlock(&relay_mutex); +} + +/* + * Creates the relay_root if it's not found. + */ +static struct relay_root *lookup_root(const char *root) +{ + struct list_head *pos; + struct relay_root *r; + + list_for_each(pos, &relay_roots) { + r = list_entry(pos, struct relay_root, list); + if (!strcmp(r->name, root)) + return r; + } + + r = kzalloc(sizeof(struct relay_root), GFP_KERNEL); + if (!r) + return ERR_PTR(-ENOMEM); + + strlcpy(r->name, root, sizeof(r->name)); + + r->root = debugfs_create_dir(root, NULL); + if (IS_ERR(r->root)) + r->root = NULL; + else + list_add(&r->list, &relay_roots); + + return r; +} + +static struct dentry *create_tree(struct relay_info *relay, const char *root, + const char *name) +{ + struct relay_dir *temp; + + if (root == NULL || name == NULL) + return ERR_PTR(-EINVAL); + + temp = kzalloc(sizeof(struct relay_dir), GFP_KERNEL); + if ((temp == NULL) || (strlen(name) > RELAY_NAME_SIZE)) + return ERR_PTR(-ENOMEM); + + strlcpy(temp->relay_dir_name, name, sizeof(temp->relay_dir_name)); + + mutex_lock(&relay_mutex); + + relay->root = lookup_root(root); + if (IS_ERR(relay->root)) { + relay->root = NULL; + goto err; + } + + temp->relay_root = relay->root->root; + temp->relay_dir = debugfs_create_dir(name, relay->root->root); + + if (IS_ERR(temp->relay_dir)) + remove_root(relay); + else { + relay->root->users++; + temp->ti = relay; + list_add_tail(&temp->relay_dir_list, &relay_dirs); + } + +err: + mutex_unlock(&relay_mutex); + return temp->relay_dir; +} + +int relay_exists(const char *parent_dir, const char *dir, + struct relay_info **ti) +{ + struct list_head *pos; + struct relay_root *r; + struct relay_dir *temp; + + list_for_each(pos, &relay_roots) { + r = list_entry(pos, struct relay_root, list); + if (!strcmp(parent_dir, r->name)) + goto search_dir; + } + return RELAY_PARENT_DIR_ABSENT; + + search_dir: + list_for_each(pos, &relay_dirs) { + temp = list_entry(pos, struct relay_dir, relay_dir_list); + + if (!strcmp(dir, temp->relay_dir_name)) { + *ti = temp->ti; + return RELAY_DIR_EXISTS; + } + } + return RELAY_PARENT_DIR_EXISTS; +} +EXPORT_SYMBOL_GPL(relay_exists); + +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 relay_info *relay = filp->private_data; + char buf[16]; + + snprintf(buf, sizeof(buf), "%u\n", atomic_read(&relay->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) +{ + int ret = count; + struct relay_info *relay = filp->private_data; + + mutex_lock(&relay->state_mutex); + switch (relay->state) { + case RELAY_RUNNING: + relay->state = RELAY_STOPPED; + synchronize_rcu(); + relay_flush(relay->rchan); + relay_reset_consumed(relay->rchan); + relay->state = RELAY_RUNNING; + break; + case RELAY_STOPPED: + relay_reset_consumed(relay->rchan); + break; + default: + ret = -EINVAL; + } + mutex_unlock(&relay->state_mutex); + return ret; +} + +static 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 relay_info *relay = filp->private_data; + char buf[32]; + + snprintf(buf, sizeof(buf), "%zu\n", relay->rchan->subbuf_size); + + return simple_read_from_buffer(buffer, count, ppos, buf, strlen(buf)); +} + +static 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 relay_info *relay = filp->private_data; + char buf[32]; + + snprintf(buf, sizeof(buf), "%zu\n", relay->rchan->n_subbufs); + + return simple_read_from_buffer(buffer, count, ppos, buf, strlen(buf)); +} + +static 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 relay_info *relay = buf->chan->private_data; + + if (relay->flags & RELAY_FLIGHT_CHANNEL) + return 1; + + if (!relay_buf_full(buf)) + return 1; + + atomic_inc(&relay->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 relay_info *relay) +{ + debugfs_remove(relay->state_file); + debugfs_remove(relay->dropped_file); + debugfs_remove(relay->reset_consumed_file); + debugfs_remove(relay->nr_sub_file); + debugfs_remove(relay->sub_size_file); + remove_tree(relay); +} + +/* + * Setup controls for tracing. + */ +static struct relay_info *setup_controls(const char *root, + const char *name, u32 flags) +{ + struct relay_info *relay; + long ret; + + relay = kzalloc(sizeof(*relay), GFP_KERNEL); + if (!relay) { + ret = -ENOMEM; + goto err; + } + + relay->dir = create_tree(relay, root, name); + if (IS_ERR(relay->dir)) { + ret = PTR_ERR(relay->dir); + relay->dir = NULL; + goto err; + } + + relay->state_file = debugfs_create_file("state", 0444, relay->dir, + relay, &state_fops); + if (IS_ERR(relay->state_file)) { + ret = PTR_ERR(relay->state_file); + relay->state_file = NULL; + goto err; + } + + if (!(flags & RELAY_FLIGHT_CHANNEL)) { + relay->dropped_file = debugfs_create_file("dropped", 0444, + relay->dir, relay, + &dropped_fops); + if (IS_ERR(relay->dropped_file)) { + ret = PTR_ERR(relay->dropped_file); + relay->dropped_file = NULL; + goto err; + } + } + + if (flags & RELAY_FLIGHT_CHANNEL) { + relay->reset_consumed_file = debugfs_create_file("rewind", 0444, + relay->dir, relay, + &reset_consumed_fops); + if (IS_ERR(relay->reset_consumed_file)) { + ret = PTR_ERR(relay->reset_consumed_file); + relay->reset_consumed_file = NULL; + goto err; + } + } + + relay->nr_sub_file = debugfs_create_file("nr_sub", 0444, + relay->dir, relay, + &nr_sub_fops); + if (IS_ERR(relay->nr_sub_file)) { + ret = PTR_ERR(relay->nr_sub_file); + relay->nr_sub_file = NULL; + goto err; + } + + relay->sub_size_file = debugfs_create_file("sub_size", 0444, + relay->dir, relay, + &sub_size_fops); + if (IS_ERR(relay->sub_size_file)) { + ret = PTR_ERR(relay->sub_size_file); + relay->sub_size_file = NULL; + goto err; + } + + return relay; +err: + if (relay) { + remove_controls(relay); + kfree(relay); + } + + return ERR_PTR(ret); +} + +static int relay_setup_channel(struct relay_info *relay, u32 buf_size, + u32 buf_nr, u32 flags) +{ + if (!buf_size || !buf_nr) + return -EINVAL; + + if (flags & RELAY_GLOBAL_CHANNEL) + relay->rchan = relay_open("trace", relay->dir, buf_size, + buf_nr, &relay_callbacks_global, + relay); + else + relay->rchan = relay_open("trace", relay->dir, buf_size, + buf_nr, &relay_callbacks, relay); + + if (!relay->rchan) + return -ENOMEM; + + relay->flags = flags; + relay->state = RELAY_SETUP; + + return 0; +} + +/** + * relay_setup - create a new relay relay handle + * @root: The root directory name to place relay directories. + * @name: Relay debugfs directory name, created in @root + * @buf_size: size of the relay sub-buffers + * @buf_nr: number of relay sub-buffers + * @flags: Option selection (see relay channel flags definitions) + * + * returns a relay_info handle or NULL, if setup failed. + * + * The @root is created (if needed) in the root of the debugfs. + * The default values when flags=0 are: use per-CPU buffering, + * use non-overwrite mode. See Documentation/filesystems/relay.txt for + * details. + */ +struct relay_info *relay_setup(const char *root, const char *name, + u32 buf_size, u32 buf_nr, u32 flags) +{ + struct relay_info *relay; + + relay = setup_controls(root, name, flags); + if (IS_ERR(relay)) + return relay; + + relay->buf_size = buf_size; + relay->buf_nr = buf_nr; + relay->flags = flags; + mutex_init(&relay->state_mutex); + relay->state = RELAY_SETUP; + + return relay; +} +EXPORT_SYMBOL_GPL(relay_setup); + +/** + * relay_start - start tracing + * @relay: relay handle to start. + * + * returns 0 if successful. + */ +int relay_start(struct relay_info *relay) +{ + /* + * For starting a relay, we can transition from a setup or stopped + * relay. + */ + if (relay->state == RELAY_RUNNING) + return -EINVAL; + + mutex_lock(&relay->state_mutex); + if (relay->state == RELAY_SETUP) { + int ret; + + ret = relay_setup_channel(relay, relay->buf_size, + relay->buf_nr, relay->flags); + if (ret) { + mutex_unlock(&relay->state_mutex); + return ret; + } + } + + relay->state = RELAY_RUNNING; + mutex_unlock(&relay->state_mutex); + + return 0; +} +EXPORT_SYMBOL_GPL(relay_start); + +/** + * relay_stop - stop tracing + * @relay: relay handle to stop. + * + */ +int relay_stop(struct relay_info *relay) +{ + int ret = -EINVAL; + + /* + * For stopping a relay, the state must be running + */ + mutex_lock(&relay->state_mutex); + if (relay->state == RELAY_RUNNING) { + relay->state = RELAY_STOPPED; + /* + * wait for all cpus to see the change in + * state before continuing + */ + synchronize_sched(); + relay_flush(relay->rchan); + ret = 0; + } + mutex_unlock(&relay->state_mutex); + return ret; +} +EXPORT_SYMBOL_GPL(relay_stop); + +static void relay_cleanup_channel(struct relay_info *relay) +{ + relay_stop(relay); + relay_close(relay->rchan); + relay->rchan = NULL; +} + +/** + * relay_cleanup - destroys the relay channel, control files and dir + * @relay: relay handle to cleanup + */ +void relay_cleanup(struct relay_info *relay) +{ + relay_cleanup_channel(relay); + remove_controls(relay); + kfree(relay); +} +EXPORT_SYMBOL_GPL(relay_cleanup); + +/** + * relay_cleanup_all - Removes all relay debugfs directories in parent_dir + * @parent_dir: Name of the parent directory + */ +void relay_cleanup_all(const char *parent_dir) +{ + struct list_head *pos, *pos_temp; + struct relay_dir *temp; + + list_for_each_safe(pos, pos_temp, &relay_dirs) { + temp = list_entry(pos, struct relay_dir, relay_dir_list); + if (!strncmp(parent_dir, temp->relay_root->d_iname, \ + strlen(parent_dir))) + relay_cleanup(temp->ti); + } +} +EXPORT_SYMBOL_GPL(relay_cleanup_all); + +/* + * Send formatted data to relay debugfs channel. + */ +static int relay_printf(struct relay_info *trace, const char *format, + va_list ap) +{ + va_list aq; + char *record; + int len, ret = 0; + + if (relay_running(trace)) { + va_copy(aq, ap); + len = vsnprintf(NULL, 0, format, aq); + va_end(aq); + record = relay_reserve(trace->rchan, ++len); + if (record) + ret = vsnprintf(record, len, format, ap); + } + return ret; +} + +static inline int init_relay_interface(struct relay_printk_data *tpk) +{ + int ret = 0; + tpk->exists = relay_exists(tpk->parent_dir, tpk->dir, &tpk->ti); + + switch (tpk->exists) { + + case RELAY_PARENT_DIR_EXISTS: + case RELAY_PARENT_DIR_ABSENT: + if (!tpk->buf_size) + tpk->buf_size = DEFAULT_RELAY_BUF_SIZE; + if (!tpk->sub_buf_size) + tpk->sub_buf_size = DEFAULT_RELAY_SUB_BUF_NR; + tpk->ti = relay_setup(tpk->parent_dir, tpk->dir, + tpk->buf_size, tpk->sub_buf_size, tpk->flags); + printk(KERN_INFO "Trace interface %s setup\n", + tpk->ti->dir->d_iname); + if (IS_ERR(tpk->ti)) { + printk(KERN_ERR "Error initialising %s interface\n", + tpk->ti->dir->d_iname); + return -EPERM; + } + /* Fall through */ + case RELAY_DIR_EXISTS: + if (tpk->ti->state == RELAY_SETUP) + ret = relay_start(tpk->ti); + else + ret = -EPERM; + } + + return 0; +} + +/** + * relay_printk - Output a string to debugfs mount 'directly' using 'trace' + * @tpk: Structure containing info such as parent_dir and directory + * @format: String containing format string specifiers + * @ap: List of arguments + */ +int relay_printk(struct relay_printk_data *tpk, char *format, ...) +{ + int ret = 0; + va_list(ap); + unsigned long flags = 0; + + va_start(ap, format); + + ret = init_relay_interface(tpk); + if (unlikely(ret)) + return ret; + + /* Now do the actual printing */ + /* Take an RCU Lock over the relay_info state */ + rcu_read_lock(); + /* Take a spinlock for the global buffer used by relay */ + if (tpk->flags & RELAY_GLOBAL_CHANNEL) + spin_lock_irqsave(&tpk->ti->relay_lock, flags); + ret = relay_printf(tpk->ti, format, ap); + if (tpk->flags & RELAY_GLOBAL_CHANNEL) + spin_unlock_irqrestore(&tpk->ti->relay_lock, flags); + rcu_read_unlock(); + + va_end(ap); + return ret; +} +EXPORT_SYMBOL(relay_printk); + +/** + * relay_dump - Output binary into debugfs mount 'directly' using 'trace' + * @tpk: Structure containing info such as parent_dir and directory + * @output: Data that needs to be output + * @output_len: Length of the output data + */ +int relay_dump(struct relay_printk_data *tpk, const void *output, + const int output_len) +{ + char *record; + unsigned long flags = 0; + int ret = 0; + + ret = init_relay_interface(tpk); + if (unlikely(ret)) + return ret; + + /* Now do the actual printing */ + rcu_read_lock(); + /* Take a spinlock for the global buffer used by relay */ + if (tpk->flags & RELAY_GLOBAL_CHANNEL) + spin_lock_irqsave(&tpk->ti->relay_lock, flags); + record = relay_reserve(tpk->ti->rchan, output_len); + + if (record && relay_running(tpk->ti)) + memcpy(record, output, output_len); + else { + if (record) + ret = -EPERM; + else + ret = -ENOMEM; + } + if (tpk->flags & RELAY_GLOBAL_CHANNEL) + spin_unlock_irqrestore(&tpk->ti->relay_lock, flags); + rcu_read_unlock(); + + return ret; +} +EXPORT_SYMBOL(relay_dump); Index: linux-trace-relay-2.6.27-rc1-mm1/lib/trace.c =================================================================== --- linux-trace-relay-2.6.27-rc1-mm1.orig/lib/trace.c +++ /dev/null @@ -1,563 +0,0 @@ -/* - * Based on blktrace code, Copyright (C) 2006 Jens Axboe - * Moved to utt.c by Tom Zanussi , 2006 - * Additional contributions by: - * Martin Hunt , 2007 - * David Wilder , 2007 - * Renamed to trace , 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 -#include -#include -#include -#include -#include - -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]; - 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-1] = '\0'; - - if (strcmp(buf, "start") == 0) { - ret = trace_start(trace); - if (ret) - return ret; - } else if (strcmp(buf, "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) { - if (--trace->root->users == 0) - remove_root(trace); - } - - mutex_unlock(&trace_mutex); -} - -/* - * Creates the trace_root if it's not found. - */ -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 ERR_PTR(-ENOMEM); - - strlcpy(r->name, root, sizeof(r->name)); - - r->root = debugfs_create_dir(root, NULL); - if (IS_ERR(r->root)) - r->root = NULL; - else - 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 ERR_PTR(-EINVAL); - - mutex_lock(&trace_mutex); - - trace->root = lookup_root(root); - if (IS_ERR(trace->root)) { - trace->root = NULL; - goto err; - } - - dir = debugfs_create_dir(name, trace->root->root); - if (IS_ERR(dir)) - remove_root(trace); - else - trace->root->users++; - -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) -{ - int ret = count; - struct trace_info *trace = filp->private_data; - - mutex_lock(&trace->state_mutex); - switch (trace->state) { - case TRACE_RUNNING: - trace->state = TRACE_STOPPED; - synchronize_rcu(); - relay_flush(trace->rchan); - relay_reset_consumed(trace->rchan); - trace->state = TRACE_RUNNING; - break; - case TRACE_STOPPED: - relay_reset_consumed(trace->rchan); - break; - default: - ret = -EINVAL; - } - mutex_unlock(&trace->state_mutex); - return ret; -} - -static 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), "%zu\n", trace->rchan->subbuf_size); - - return simple_read_from_buffer(buffer, count, ppos, buf, strlen(buf)); -} - -static 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), "%zu\n", trace->rchan->n_subbufs); - - return simple_read_from_buffer(buffer, count, ppos, buf, strlen(buf)); -} - -static 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) -{ - debugfs_remove(trace->state_file); - debugfs_remove(trace->dropped_file); - debugfs_remove(trace->reset_consumed_file); - debugfs_remove(trace->nr_sub_file); - debugfs_remove(trace->sub_size_file); - 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; - long ret; - - trace = kzalloc(sizeof(*trace), GFP_KERNEL); - if (!trace) { - ret = -ENOMEM; - goto err; - } - - trace->dir = create_tree(trace, root, name); - if (IS_ERR(trace->dir)) { - ret = PTR_ERR(trace->dir); - trace->dir = NULL; - goto err; - } - - trace->state_file = debugfs_create_file("state", 0444, trace->dir, - trace, &state_fops); - if (IS_ERR(trace->state_file)) { - ret = PTR_ERR(trace->state_file); - trace->state_file = NULL; - goto err; - } - - if (!(flags & TRACE_FLIGHT_CHANNEL)) { - trace->dropped_file = debugfs_create_file("dropped", 0444, - trace->dir, trace, - &dropped_fops); - if (IS_ERR(trace->dropped_file)) { - ret = PTR_ERR(trace->dropped_file); - trace->dropped_file = NULL; - goto err; - } - } - - if (flags & TRACE_FLIGHT_CHANNEL) { - trace->reset_consumed_file = debugfs_create_file("rewind", 0444, - trace->dir, trace, - &reset_consumed_fops); - if (IS_ERR(trace->reset_consumed_file)) { - ret = PTR_ERR(trace->reset_consumed_file); - trace->reset_consumed_file = NULL; - goto err; - } - } - - trace->nr_sub_file = debugfs_create_file("nr_sub", 0444, - trace->dir, trace, - &nr_sub_fops); - if (IS_ERR(trace->nr_sub_file)) { - ret = PTR_ERR(trace->nr_sub_file); - trace->nr_sub_file = NULL; - goto err; - } - - trace->sub_size_file = debugfs_create_file("sub_size", 0444, - trace->dir, trace, - &sub_size_fops); - if (IS_ERR(trace->sub_size_file)) { - ret = PTR_ERR(trace->sub_size_file); - trace->sub_size_file = NULL; - goto err; - } - - return trace; -err: - if (trace) { - remove_controls(trace); - kfree(trace); - } - - return ERR_PTR(ret); -} - -static 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 trace trace handle - * @root: The root directory name to place trace directories. - * @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 trace channel flags definitions) - * - * returns a trace_info handle or NULL, if setup failed. - * - * The @root is created (if needed) in the root of the debugfs. - * The default values when flags=0 are: use per-CPU buffering, - * use non-overwrite mode. See Documentation/trace.txt for details. - */ -struct trace_info *trace_setup(const char *root, const char *name, - u32 buf_size, u32 buf_nr, u32 flags) -{ - struct trace_info *trace; - - trace = setup_controls(root, name, flags); - if (IS_ERR(trace)) - return trace; - - trace->buf_size = buf_size; - trace->buf_nr = buf_nr; - trace->flags = flags; - mutex_init(&trace->state_mutex); - trace->state = TRACE_SETUP; - - return trace; -} -EXPORT_SYMBOL_GPL(trace_setup); - -/** - * trace_start - start tracing - * @trace: trace handle to start. - * - * 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; - - mutex_lock(&trace->state_mutex); - if (trace->state == TRACE_SETUP) { - int ret; - - ret = trace_setup_channel(trace, trace->buf_size, - trace->buf_nr, trace->flags); - if (ret) { - mutex_unlock(&trace->state_mutex); - return ret; - } - } - - trace->state = TRACE_RUNNING; - mutex_unlock(&trace->state_mutex); - - return 0; -} -EXPORT_SYMBOL_GPL(trace_start); - -/** - * trace_stop - stop tracing - * @trace: trace handle to stop. - * - */ -int trace_stop(struct trace_info *trace) -{ - int ret = -EINVAL; - - /* - * For stopping a trace, the state must be running - */ - mutex_lock(&trace->state_mutex); - if (trace->state == TRACE_RUNNING) { - trace->state = TRACE_STOPPED; - /* - * wait for all cpus to see the change in - * state before continuing - */ - synchronize_sched(); - relay_flush(trace->rchan); - ret = 0; - } - mutex_unlock(&trace->state_mutex); - return ret; -} -EXPORT_SYMBOL_GPL(trace_stop); - -static void trace_cleanup_channel(struct trace_info *trace) -{ - trace_stop(trace); - relay_close(trace->rchan); - trace->rchan = NULL; -} - -/** - * trace_cleanup - destroys the trace channel, control files and dir - * @trace: 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); Index: linux-trace-relay-2.6.27-rc1-mm1/init/Kconfig =================================================================== --- linux-trace-relay-2.6.27-rc1-mm1.orig/init/Kconfig +++ linux-trace-relay-2.6.27-rc1-mm1/init/Kconfig @@ -460,6 +460,15 @@ config RELAY If unsure, say N. +config RELAY_DEBUGFS + bool "Relay debugfs setup and control" + depends on RELAY && DEBUG_FS + help + This option provides support for the setup, teardown and control + of relay channels from kernel code which are mounted on debugfs. + It also provides information and control to userspace via a set of + debugfs control files. If unsure, say N. + config NAMESPACES bool "Namespaces support" if EMBEDDED default !EMBEDDED Index: linux-trace-relay-2.6.27-rc1-mm1/kernel/Makefile =================================================================== --- linux-trace-relay-2.6.27-rc1-mm1.orig/kernel/Makefile +++ linux-trace-relay-2.6.27-rc1-mm1/kernel/Makefile @@ -77,6 +77,7 @@ ifeq ($(CONFIG_PREEMPT_RCU),y) obj-$(CONFIG_RCU_TRACE) += rcupreempt_trace.o endif obj-$(CONFIG_RELAY) += relay.o +obj-$(CONFIG_RELAY_DEBUGFS) += relay_debugfs.o obj-$(CONFIG_SYSCTL) += utsname_sysctl.o obj-$(CONFIG_TASK_DELAY_ACCT) += delayacct.o obj-$(CONFIG_TASKSTATS) += taskstats.o tsacct.o Index: linux-trace-relay-2.6.27-rc1-mm1/lib/Kconfig =================================================================== --- linux-trace-relay-2.6.27-rc1-mm1.orig/lib/Kconfig +++ linux-trace-relay-2.6.27-rc1-mm1/lib/Kconfig @@ -157,13 +157,4 @@ config CHECK_SIGNATURE config HAVE_LMB boolean -config TRACE - bool "Trace setup and control" - depends on RELAY && 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 Index: linux-trace-relay-2.6.27-rc1-mm1/lib/Makefile =================================================================== --- linux-trace-relay-2.6.27-rc1-mm1.orig/lib/Makefile +++ linux-trace-relay-2.6.27-rc1-mm1/lib/Makefile @@ -82,8 +82,6 @@ obj-$(CONFIG_HAVE_LMB) += lmb.o obj-$(CONFIG_HAVE_ARCH_TRACEHOOK) += syscall.o -obj-$(CONFIG_TRACE) += trace.o - obj-$(CONFIG_PROFILE_LIKELY) += likely_prof.o hostprogs-y := gen_crc32table Index: linux-trace-relay-2.6.27-rc1-mm1/samples/relay/Makefile =================================================================== --- /dev/null +++ linux-trace-relay-2.6.27-rc1-mm1/samples/relay/Makefile @@ -0,0 +1,4 @@ +# builds the trace example kernel modules; +# then to use (as root): insmod + +obj-$(CONFIG_SAMPLE_RELAY) := fork_trace.o fork_new_trace.o Index: linux-trace-relay-2.6.27-rc1-mm1/samples/relay/fork_trace.c =================================================================== --- /dev/null +++ linux-trace-relay-2.6.27-rc1-mm1/samples/relay/fork_trace.c @@ -0,0 +1,132 @@ +/* + * An example of using 'relay debugfs' in a kprobes module + * + * Copyright (C) 2007 IBM Inc. + * + * David Wilder + * + * 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 + * + * ------- + * This module creates a 'relay debugfs' channel and places a kprobe + * on the function do_fork(). The value of current->pid is written to + * the 'relay debugfs' channel each time the kprobe is hit.. + * + * How to run the example: + * $ mount -t debugfs /debug + * $ insmod fork_trace.ko + * + * To view the data produced by the module: + * $ cat /debug/relay_debugfs_example/do_fork/trace0 + * + */ + +#include +#include +#include +#include + +#define USE_GLOBAL_BUFFERS 1 +#define USE_FLIGHT 1 + +#define PROBE_POINT "do_fork" + +static struct kprobe kp; +static struct relay_info *kprobes_relay; + +#ifdef USE_GLOBAL_BUFFERS +static DEFINE_SPINLOCK(relay_debugfs_lock); +#endif + +/* + * Send formatted trace data to 'relay debugfs' channel. + * @note Preemption must be disabled to use this. + */ +static void relay_debugfs_printf(struct relay_info *relay, const char *format, ...) +{ + va_list ap, aq; + char *record; + unsigned long flags; + int len; + + if (!relay) + return; + +#ifdef USE_GLOBAL_BUFFERS + spin_lock_irqsave(&relay_debugfs_lock, flags); +#endif + if (relay_running(relay)) { + va_start(ap, format); + va_copy(aq, ap); + len = vsnprintf(NULL, 0, format, aq); + va_end(aq); + record = relay_reserve(relay->rchan, ++len); + if (record) + vsnprintf(record, len, format, ap); + va_end(ap); + } +#ifdef USE_GLOBAL_BUFFERS + spin_unlock_irqrestore(&relay_debugfs_lock, flags); +#endif +} + +static int handler_pre(struct kprobe *p, struct pt_regs *regs) +{ + rcu_read_lock(); + relay_debugfs_printf(kprobes_relay, "%d\n", current->pid); + rcu_read_unlock(); + return 0; +} + +int init_module(void) +{ + int ret; + u32 flags = 0; + +#ifdef USE_GLOBAL_BUFFERS + flags |= RELAY_GLOBAL_CHANNEL; +#endif + +#ifdef USE_FLIGHT + flags |= RELAY_FLIGHT_CHANNEL; +#endif + + /* setup the relay */ + kprobes_relay = relay_setup("relay_example", PROBE_POINT, + 1024, 8, flags); + if (IS_ERR(kprobes_relay)) + return PTR_ERR(kprobes_relay); + + relay_start(kprobes_relay); + + /* setup the kprobe */ + kp.pre_handler = handler_pre; + kp.post_handler = NULL; + kp.fault_handler = NULL; + kp.symbol_name = PROBE_POINT; + ret = register_kprobe(&kp); + if (ret) { + printk(KERN_ERR "fork_trace: register_kprobe failed\n"); + return ret; + } + return 0; +} + +void cleanup_module(void) +{ + unregister_kprobe(&kp); + relay_stop(kprobes_relay); + relay_cleanup(kprobes_relay); +} +MODULE_LICENSE("GPL"); Index: linux-trace-relay-2.6.27-rc1-mm1/samples/trace/Makefile =================================================================== --- linux-trace-relay-2.6.27-rc1-mm1.orig/samples/trace/Makefile +++ /dev/null @@ -1,4 +0,0 @@ -# builds the trace example kernel modules; -# then to use (as root): insmod - -obj-$(CONFIG_SAMPLE_TRACE) := fork_trace.o Index: linux-trace-relay-2.6.27-rc1-mm1/samples/trace/fork_trace.c =================================================================== --- linux-trace-relay-2.6.27-rc1-mm1.orig/samples/trace/fork_trace.c +++ /dev/null @@ -1,132 +0,0 @@ -/* - * An example of using trace in a kprobes module - * - * Copyright (C) 2007 IBM Inc. - * - * David Wilder - * - * 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 - * - * ------- - * This module creates a trace channel and places a kprobe - * on the function do_fork(). The value of current->pid is written to - * the trace channel each time the kprobe is hit.. - * - * How to run the example: - * $ mount -t debugfs /debug - * $ insmod fork_trace.ko - * - * To view the data produced by the module: - * $ cat /debug/trace_example/do_fork/trace0 - * - */ - -#include -#include -#include -#include - -#define USE_GLOBAL_BUFFERS 1 -#define USE_FLIGHT 1 - -#define PROBE_POINT "do_fork" - -static struct kprobe kp; -static struct trace_info *kprobes_trace; - -#ifdef USE_GLOBAL_BUFFERS -static DEFINE_SPINLOCK(trace_lock); -#endif - -/* - * Send formatted trace data to trace channel. - * @note Preemption must be disabled to use this. - */ -static void trace_printf(struct trace_info *trace, const char *format, ...) -{ - va_list ap, aq; - char *record; - unsigned long flags; - int len; - - if (!trace) - return; - -#ifdef USE_GLOBAL_BUFFERS - spin_lock_irqsave(&trace_lock, flags); -#endif - if (trace_running(trace)) { - va_start(ap, format); - va_copy(aq, ap); - len = vsnprintf(NULL, 0, format, aq); - va_end(aq); - record = relay_reserve(trace->rchan, ++len); - if (record) - vsnprintf(record, len, format, ap); - va_end(ap); - } -#ifdef USE_GLOBAL_BUFFERS - spin_unlock_irqrestore(&trace_lock, flags); -#endif -} - -static int handler_pre(struct kprobe *p, struct pt_regs *regs) -{ - rcu_read_lock(); - trace_printf(kprobes_trace, "%d\n", current->pid); - rcu_read_unlock(); - return 0; -} - -int init_module(void) -{ - int ret; - u32 flags = 0; - -#ifdef USE_GLOBAL_BUFFERS - flags |= TRACE_GLOBAL_CHANNEL; -#endif - -#ifdef USE_FLIGHT - flags |= TRACE_FLIGHT_CHANNEL; -#endif - - /* setup the trace */ - kprobes_trace = trace_setup("trace_example", PROBE_POINT, - 1024, 8, flags); - if (IS_ERR(kprobes_trace)) - return PTR_ERR(kprobes_trace); - - trace_start(kprobes_trace); - - /* setup the kprobe */ - kp.pre_handler = handler_pre; - kp.post_handler = NULL; - kp.fault_handler = NULL; - kp.symbol_name = PROBE_POINT; - ret = register_kprobe(&kp); - if (ret) { - printk(KERN_ERR "fork_trace: register_kprobe failed\n"); - return ret; - } - return 0; -} - -void cleanup_module(void) -{ - unregister_kprobe(&kp); - trace_stop(kprobes_trace); - trace_cleanup(kprobes_trace); -} -MODULE_LICENSE("GPL"); Index: linux-trace-relay-2.6.27-rc1-mm1/samples/Kconfig =================================================================== --- linux-trace-relay-2.6.27-rc1-mm1.orig/samples/Kconfig +++ linux-trace-relay-2.6.27-rc1-mm1/samples/Kconfig @@ -33,11 +33,11 @@ config SAMPLE_KRETPROBES default m depends on SAMPLE_KPROBES && KRETPROBES -config SAMPLE_TRACE - tristate "Build trace example -- loadable modules only" - depends on TRACE && KPROBES && m +config SAMPLE_RELAY + tristate "Build relay debugfs example -- loadable modules only" + depends on RELAY && KPROBES && m help - This builds a trace example module. + This builds a relay debugfs example module. endif # SAMPLES Index: linux-trace-relay-2.6.27-rc1-mm1/samples/Makefile =================================================================== --- linux-trace-relay-2.6.27-rc1-mm1.orig/samples/Makefile +++ linux-trace-relay-2.6.27-rc1-mm1/samples/Makefile @@ -1,3 +1,3 @@ # Makefile for Linux samples code -obj-$(CONFIG_SAMPLES) += markers/ kobject/ kprobes/ trace/ +obj-$(CONFIG_SAMPLES) += markers/ kobject/ kprobes/ relay/ Index: linux-trace-relay-2.6.27-rc1-mm1/Documentation/filesystems/relay.txt =================================================================== --- linux-trace-relay-2.6.27-rc1-mm1.orig/Documentation/filesystems/relay.txt +++ linux-trace-relay-2.6.27-rc1-mm1/Documentation/filesystems/relay.txt @@ -637,6 +637,28 @@ are: 5) Destroy the 'relay debugfs' channel and underlying relay channel - relay_cleanup(). +Alternatively the user may choose to make use of two new interfaces -- +relay_printk() and relay_dump() -- to setup trace interface and +relay_cleanup_all() to tear-down the same. + +Steps to use: +1) Create and populate an instance of relay_printk_data structure. The fields + parent_dir and dir are mandatory. The fields buf_size, sub_buf_size and flags + are optional and will take default values if not populated. The field + 'exists' and ti are for the trace infrastructure to use. The pointer to the + 'struct relay_info' i.e. ti may be used to perform fine granular operations + such as determine the state of the 'trace', stop individual traces, etc. +2) Default values for buf_size and sub_buf_size are 4096, 40 respectively. +3) Use relay_dump() to output binary data which may be acted upon by a + high-level program (say dumping a structure). relay_printk() can be used + for string output. Pass a pointer to the instance of relay_printk_data + structure to these functions along with other parameters. The output from + these functions can be found at + ///trace<0..n>. +4) relay_cleanup_all() for a given parent directory will cleanup and remove all + trace directories created under the specified directory. +5) Sample code for the same can be found in samples/trace/fork_new_trace.c + Kernel Configuration -------------------- To use 'relay debugfs', configure your kernel with CONFIG_TRACE=y. Index: linux-trace-relay-2.6.27-rc1-mm1/samples/trace/fork_new_trace.c =================================================================== --- /dev/null +++ linux-trace-relay-2.6.27-rc1-mm1/samples/trace/fork_new_trace.c @@ -0,0 +1,99 @@ +/* + * An example of using trace in a kprobes module + * + * Copyright (C) 2008 IBM Inc. + * + * K.Prasad + * + * 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 + * + * ------- + * This module creates a trace channel and places a kprobe + * on the function do_fork(). The value of current->pid is written to + * the trace channel each time the kprobe is hit.. + * + * How to run the example: + * $ mount -t debugfs /debug + * $ insmod fork_new_trace.ko + * + * To view the data produced by the module: + * $ cat /debug/relay_example/do_fork/trace0 + * + */ + +#include +#include +#include +#include + +#define SAMPLE_PARENT_DIR "relay_new_example" +#define PROBE_POINT "do_fork" + +static struct kprobe kp; +static struct relay_printk_data *tpk; + +static int handler_pre(struct kprobe *p, struct pt_regs *regs) +{ + relay_printk(tpk, "%d\n", current->pid); + return 0; +} + +int init_module(void) +{ + int ret = 0; + int len_parent_dir, len_dir; + + /* setup the kprobe */ + kp.pre_handler = handler_pre; + kp.post_handler = NULL; + kp.fault_handler = NULL; + kp.symbol_name = PROBE_POINT; + ret = register_kprobe(&kp); + if (ret) { + printk(KERN_ERR "fork_trace: register_kprobe failed\n"); + return ret; + } + + len_parent_dir = strlen(SAMPLE_PARENT_DIR) + 1; + /* Initialising len_dir to the larger of the two dir names */ + len_dir = strlen("kprobe_struct") + 1; + + tpk = kzalloc(sizeof(*tpk), GFP_KERNEL); + if (!tpk) + ret = 1; + + tpk->parent_dir = SAMPLE_PARENT_DIR; + + /* Let's do a binary dump of struct kprobe using relay_dump */ + tpk->dir = "kprobes_struct"; + tpk->flags = TRACE_GLOBAL_CHANNEL; + relay_dump(tpk, &kp, sizeof(kp)); + + /* Now change the directory to collect fork pid data */ + tpk->dir = PROBE_POINT; + + if (ret) + printk(KERN_ERR "Unable to find required free memory. " + "Trace new sample module loading aborted"); + return ret; +} + +void cleanup_module(void) +{ + unregister_kprobe(&kp); + + /* Just a single cleanup call passing the parent dir string */ + relay_cleanup_all(SAMPLE_PARENT_DIR); +} +MODULE_LICENSE("GPL"); -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/