[<prev] [next>] [<thread-prev] [day] [month] [year] [list]
Message-ID: <op.ubvnvvxu03j166@prasadkr_t60p.in.ibm.com>
Date: Thu, 29 May 2008 00:18:57 +0530
From: "K.Prasad" <prasad@...ux.vnet.ibm.com>
To: akpm@...ux-foundation.org, zanussi@...cast.net
Cc: linux-kernel@...r.kernel.org, dwilder@...ibm.com,
hch@...radead.org, prasad@...ux.vnet.ibm.com
Subject: [RFC Patch 2/2] Renaming lib/trace.[ch] files to kernel/relay_debugfs.[ch] and enhancements
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 <prasad@...ux.vnet.ibm.com>
---
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-2.6.25/include/linux/relay_debugfs.h
===================================================================
--- /dev/null
+++ linux-2.6.25/include/linux/relay_debugfs.h
@@ -0,0 +1,155 @@
+/*
+ * RELAY DEBUGFS defines and function prototypes
+ *
+ * Copyright (C) 2007 IBM Inc.
+ *
+ * Tom Zanussi <zanussi@...ibm.com>
+ * Martin Hunt <hunt@...hat.com>
+ * David Wilder <dwilder@...ibm.com>
+ *
+ * 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 <linux/relay.h>
+
+/*
+ * 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-2.6.25/include/linux/trace.h
===================================================================
--- linux-2.6.25.orig/include/linux/trace.h
+++ /dev/null
@@ -1,99 +0,0 @@
-/*
- * TRACE defines and function prototypes
- *
- * Copyright (C) 2007 IBM Inc.
- *
- * Tom Zanussi <zanussi@...ibm.com>
- * Martin Hunt <hunt@...hat.com>
- * David Wilder <dwilder@...ibm.com>
- *
- * 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 <linux/relay.h>
-
-/*
- * 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-2.6.25/kernel/relay_debugfs.c
===================================================================
--- /dev/null
+++ linux-2.6.25/kernel/relay_debugfs.c
@@ -0,0 +1,760 @@
+/*
+ * Based on blktrace code, Copyright (C) 2006 Jens Axboe <axboe@...nel.dk>
+ * Moved to utt.c by Tom Zanussi <zanussi@...ibm.com>, 2006
+ * Additional contributions by:
+ * Martin Hunt <hunt@...hat.com>, 2007
+ * David Wilder <dwilder@...ibm.com>, 2007
+ * Renamed to trace <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/relay_debugfs.h>
+
+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-2.6.25/lib/trace.c
===================================================================
--- linux-2.6.25.orig/lib/trace.c
+++ /dev/null
@@ -1,563 +0,0 @@
-/*
- * Based on blktrace code, Copyright (C) 2006 Jens Axboe <axboe@...nel.dk>
- * Moved to utt.c by Tom Zanussi <zanussi@...ibm.com>, 2006
- * Additional contributions by:
- * Martin Hunt <hunt@...hat.com>, 2007
- * David Wilder <dwilder@...ibm.com>, 2007
- * Renamed to trace <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/trace.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];
- 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-2.6.25/init/Kconfig
===================================================================
--- linux-2.6.25.orig/init/Kconfig
+++ linux-2.6.25/init/Kconfig
@@ -435,6 +435,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-2.6.25/kernel/Makefile
===================================================================
--- linux-2.6.25.orig/kernel/Makefile
+++ linux-2.6.25/kernel/Makefile
@@ -73,6 +73,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-2.6.25/lib/Kconfig
===================================================================
--- linux-2.6.25.orig/lib/Kconfig
+++ linux-2.6.25/lib/Kconfig
@@ -144,13 +144,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-2.6.25/lib/Makefile
===================================================================
--- linux-2.6.25.orig/lib/Makefile
+++ linux-2.6.25/lib/Makefile
@@ -80,8 +80,6 @@ lib-$(CONFIG_GENERIC_BUG) += bug.o
obj-$(CONFIG_HAVE_LMB) += lmb.o
-obj-$(CONFIG_TRACE) += trace.o
-
obj-$(CONFIG_PROFILE_LIKELY) += likely_prof.o
hostprogs-y := gen_crc32table
Index: linux-2.6.25/samples/relay/Makefile
===================================================================
--- /dev/null
+++ linux-2.6.25/samples/relay/Makefile
@@ -0,0 +1,4 @@
+# builds the trace example kernel modules;
+# then to use (as root): insmod <fork_trace.ko>
+
+obj-$(CONFIG_SAMPLE_RELAY) := fork_trace.o fork_new_trace.o
Index: linux-2.6.25/samples/relay/fork_trace.c
===================================================================
--- /dev/null
+++ linux-2.6.25/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 <dwilder@...ibm.com>
+ *
+ * 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 <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/kprobes.h>
+#include <linux/relay_debugfs.h>
+
+#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-2.6.25/samples/trace/Makefile
===================================================================
--- linux-2.6.25.orig/samples/trace/Makefile
+++ /dev/null
@@ -1,4 +0,0 @@
-# builds the trace example kernel modules;
-# then to use (as root): insmod <fork_trace.ko>
-
-obj-$(CONFIG_SAMPLE_TRACE) := fork_trace.o
Index: linux-2.6.25/samples/trace/fork_trace.c
===================================================================
--- linux-2.6.25.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 <dwilder@...ibm.com>
- *
- * 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 <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/kprobes.h>
-#include <linux/trace.h>
-
-#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-2.6.25/samples/Kconfig
===================================================================
--- linux-2.6.25.orig/samples/Kconfig
+++ linux-2.6.25/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-2.6.25/samples/Makefile
===================================================================
--- linux-2.6.25.orig/samples/Makefile
+++ linux-2.6.25/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-2.6.25/Documentation/filesystems/relay.txt
===================================================================
--- linux-2.6.25.orig/Documentation/filesystems/relay.txt
+++ linux-2.6.25/Documentation/filesystems/relay.txt
@@ -627,6 +627,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
+ <debugfs_mount>/<parent_dir>/<dir>/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-2.6.25/samples/trace/fork_new_trace.c
===================================================================
--- /dev/null
+++ linux-2.6.25/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 <prasad@...ux.vnet.ibm.com>
+ *
+ * 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 <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/kprobes.h>
+#include <linux/relay_debugfs.h>
+
+#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@...r.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
Powered by blists - more mailing lists