* transport_msgs.h: ifdef old messages as such.
Add support for new transport.
* relayfs.c: Simplify and add new interface to look
like utt.
* utt.[ch]: New files. Similar to the proposed utt interface.
These setup and teardown relayfs on debugfs.
* control.c: New file. Implements a simple control channel.
A small subset of procfs.c.
* procfs.c: This is now only used for old kernels lacking newer
relayfs. Change STP_RELAYFS to STP_BULKMODE. Use new
messages from transport_msgs.h. Don't support
RELAYFS_CHANNEL_VERSION >= 4. CHanges all control channel functions
to new names. Use pids instead of module names in /proc names.
+2007-03-14 Martin Hunt <hunt@redhat.com>
+
+ * transport_msgs.h: ifdef old messages as such.
+ Add support for new transport.
+ * relayfs.c: Simplify and add new interface to look
+ like utt.
+ * utt.[ch]: New files. Similar to the proposed utt interface.
+ These setup and teardown relayfs on debugfs.
+ * control.c: New file. Implements a simple control channel.
+ A small subset of procfs.c.
+ * procfs.c: This is now only used for old kernels lacking newer
+ relayfs. Change STP_RELAYFS to STP_BULKMODE. Use new
+ messages from transport_msgs.h. Don't support
+ RELAYFS_CHANNEL_VERSION >= 4. CHanges all control channel functions
+ to new names. Use pids instead of module names in /proc names.
+
2007-03-12 Frank Ch. Eigler <fche@elastic.org>
* procfs.c (_stp_register_procfs): Use /proc/MODULE rather than
--- /dev/null
+/* -*- linux-c -*-
+ *
+ * debugfs control channel
+ * Copyright (C) 2007 Red Hat Inc.
+ *
+ * This file is part of systemtap, and is free software. You can
+ * redistribute it and/or modify it under the terms of the GNU General
+ * Public License (GPL); either version 2, or (at your option) any
+ * later version.
+ */
+
+#define STP_DEFAULT_BUFFERS 20
+static int _stp_current_buffers = STP_DEFAULT_BUFFERS;
+
+static struct list_head _stp_ready_q;
+static struct list_head _stp_pool_q;
+spinlock_t _stp_pool_lock = SPIN_LOCK_UNLOCKED;
+spinlock_t _stp_ready_lock = SPIN_LOCK_UNLOCKED;
+
+static ssize_t _stp_ctl_write_cmd (struct file *file, const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ int type;
+
+ if (count < sizeof(int))
+ return 0;
+
+ if (get_user(type, (int __user *)buf))
+ return -EFAULT;
+
+ //kbug ("count:%d type:%d\n", count, type);
+
+ if (type == STP_SYMBOLS) {
+ count -= sizeof(long);
+ buf += sizeof(long);
+ } else {
+ count -= sizeof(int);
+ buf += sizeof(int);
+ }
+
+ switch (type) {
+ case STP_START:
+ {
+ struct _stp_msg_start st;
+ if (count < sizeof(st))
+ return 0;
+ if (copy_from_user (&st, buf, sizeof(st)))
+ return -EFAULT;
+ _stp_handle_start (&st);
+ break;
+ }
+
+ case STP_SYMBOLS:
+ count = _stp_do_symbols(buf, count);
+ break;
+ case STP_MODULE:
+ count = _stp_do_module(buf, count);
+ break;
+ case STP_EXIT:
+ _stp_exit_flag = 1;
+ break;
+ default:
+ errk ("invalid command type %d\n", type);
+ return -EINVAL;
+ }
+
+ return count;
+}
+
+struct _stp_buffer {
+ struct list_head list;
+ int len;
+ int type;
+ char buf[STP_BUFFER_SIZE];
+};
+
+static DECLARE_WAIT_QUEUE_HEAD(_stp_ctl_wq);
+
+#ifdef DEBUG
+static void _stp_ctl_write_dbug (int type, void *data, int len)
+{
+ char buf[64];
+ switch (type) {
+ case STP_START:
+ printk("_stp_ctl_write: sending STP_START\n");
+ break;
+ case STP_EXIT:
+ printk("_stp_ctl_write: sending STP_EXIT\n");
+ break;
+ case STP_OOB_DATA:
+ snprintf(buf, sizeof(buf), "%s", (char *)data);
+ printk("_stp_ctl_write: sending %d bytes of STP_OOB_DATA: %s\n", len, buf);
+ break;
+ case STP_SYSTEM:
+ snprintf(buf, sizeof(buf), "%s", (char *)data);
+ printk("_stp_ctl_write: sending STP_SYSTEM: %s\n", buf);
+ break;
+ case STP_SYMBOLS:
+ printk("_stp_ctl_write: sending STP_SYMBOLS\n");
+ break;
+ case STP_MODULE:
+ printk("_stp_ctl_write: sending STP_MODULE\n");
+ break;
+ case STP_TRANSPORT:
+ printk("_stp_ctl_write: sending STP_TRANSPORT\n");
+ break;
+ default:
+ printk("_stp_ctl_write: ERROR: unknown message type: %d\n", type);
+ break;
+ }
+}
+#endif
+
+static int _stp_ctl_write (int type, void *data, int len)
+{
+ struct _stp_buffer *bptr;
+ unsigned long flags;
+ unsigned numtrylock;
+
+#ifdef DEBUG
+ _stp_ctl_write_dbug(type, data, len);
+#endif
+ numtrylock = 0;
+ while (!spin_trylock_irqsave (&_stp_pool_lock, flags) && (++numtrylock < MAXTRYLOCK))
+ ndelay (TRYLOCKDELAY);
+ if (unlikely (numtrylock >= MAXTRYLOCK))
+ return 0;
+
+ if (list_empty(&_stp_pool_q)) {
+ spin_unlock_irqrestore(&_stp_pool_lock, flags);
+ return -1;
+ }
+
+ /* get the next buffer from the pool */
+ bptr = (struct _stp_buffer *)_stp_pool_q.next;
+ list_del_init(&bptr->list);
+ spin_unlock_irqrestore(&_stp_pool_lock, flags);
+
+ bptr->type = type;
+ memcpy (bptr->buf, data, len);
+ bptr->len = len;
+
+ /* put it on the pool of ready buffers */
+ numtrylock = 0;
+ while (!spin_trylock_irqsave (&_stp_ready_lock, flags) && (++numtrylock < MAXTRYLOCK))
+ ndelay (TRYLOCKDELAY);
+ if (unlikely (numtrylock >= MAXTRYLOCK))
+ return 0;
+ list_add_tail(&bptr->list, &_stp_ready_q);
+ spin_unlock_irqrestore(&_stp_ready_lock, flags);
+
+ return len;
+}
+
+/* send commands with timeout and retry */
+static int _stp_ctl_send (int type, void *data, int len)
+{
+ int err, trylimit = 50;
+ kbug("ctl_send: type=%d len=%d\n", type, len);
+ while ((err = _stp_ctl_write(type, data, len)) < 0 && trylimit--)
+ msleep (5);
+ kbug("returning %d\n", err);
+ return err;
+}
+
+static ssize_t
+_stp_ctl_read_cmd (struct file *file, char __user *buf, size_t count, loff_t *ppos)
+{
+ struct _stp_buffer *bptr;
+ int len;
+ unsigned long flags;
+
+ /* FIXME FIXME FIXME. assuming count is large enough to hold buffer!! */
+
+ /* wait for nonempty ready queue */
+ spin_lock_irqsave(&_stp_ready_lock, flags);
+ while (list_empty(&_stp_ready_q)) {
+ spin_unlock_irqrestore(&_stp_ready_lock, flags);
+ if (file->f_flags & O_NONBLOCK)
+ return -EAGAIN;
+ if (wait_event_interruptible(_stp_ctl_wq, !list_empty(&_stp_ready_q)))
+ return -ERESTARTSYS;
+ spin_lock_irqsave(&_stp_ready_lock, flags);
+ }
+
+ /* get the next buffer off the ready list */
+ bptr = (struct _stp_buffer *)_stp_ready_q.next;
+ list_del_init(&bptr->list);
+ spin_unlock_irqrestore(&_stp_ready_lock, flags);
+
+ /* write it out */
+ len = bptr->len + 4;
+ if (copy_to_user(buf, &bptr->type, len)) {
+ /* now what? We took it off the queue then failed to send it */
+ /* we can't put it back on the queue because it will likely be out-of-order */
+ /* fortunately this should never happen */
+ /* FIXME need to mark this as a transport failure */
+ return -EFAULT;
+ }
+
+ /* put it on the pool of free buffers */
+ spin_lock_irqsave(&_stp_pool_lock, flags);
+ list_add_tail(&bptr->list, &_stp_pool_q);
+ spin_unlock_irqrestore(&_stp_pool_lock, flags);
+
+ return len;
+}
+
+
+static struct file_operations _stp_ctl_fops_cmd = {
+ .owner = THIS_MODULE,
+ .read = _stp_ctl_read_cmd,
+ .write = _stp_ctl_write_cmd,
+};
+
+static struct dentry *_stp_cmd_file = NULL;
+
+static int _stp_register_ctl_channel (void)
+{
+ int i;
+ struct list_head *p, *tmp;
+ char buf[32];
+
+ if (_stp_utt == NULL) {
+ errk("_expected _stp_utt to be set.\n");
+ return -1;
+ }
+
+ INIT_LIST_HEAD(&_stp_ready_q);
+ INIT_LIST_HEAD(&_stp_pool_q);
+
+ /* allocate buffers */
+ for (i = 0; i < STP_DEFAULT_BUFFERS; i++) {
+ p = (struct list_head *)kmalloc(sizeof(struct _stp_buffer),STP_ALLOC_FLAGS);
+ // printk("allocated buffer at %lx\n", (long)p);
+ if (!p)
+ goto err0;
+ _stp_allocated_net_memory += sizeof(struct _stp_buffer);
+ list_add (p, &_stp_pool_q);
+ }
+
+
+ /* create [debugfs]/systemtap_[pid]/cmd */
+ _stp_cmd_file = debugfs_create_file("cmd", 0444, _stp_utt->utt_tree_root, NULL, &_stp_ctl_fops_cmd);
+ if (_stp_cmd_file == NULL)
+ goto err0;
+
+ return 0;
+
+err0:
+ list_for_each_safe(p, tmp, &_stp_pool_q) {
+ list_del(p);
+ kfree(p);
+ }
+ errk ("Error creating systemtap debugfs entries.\n");
+ return -1;
+}
+
+
+static void _stp_unregister_ctl_channel (void)
+{
+ struct list_head *p, *tmp;
+ if (_stp_cmd_file) debugfs_remove(_stp_cmd_file);
+
+ /* free memory pools */
+ list_for_each_safe(p, tmp, &_stp_pool_q) {
+ list_del(p);
+ kfree(p);
+ }
+ list_for_each_safe(p, tmp, &_stp_ready_q) {
+ list_del(p);
+ kfree(p);
+ }
+}
+
spinlock_t _stp_pool_lock = SPIN_LOCK_UNLOCKED;
spinlock_t _stp_ready_lock = SPIN_LOCK_UNLOCKED;
-#ifdef STP_RELAYFS
+#ifdef STP_BULKMODE
extern int _stp_relay_flushing;
/* handle the per-cpu subbuf info read for relayfs */
static ssize_t
int cpu = *(int *)(PDE(file->f_dentry->d_inode)->data);
- if (!_stp_chan)
+ if (!_stp_utt->rchan)
return -EINVAL;
out.cpu = cpu;
-#if (RELAYFS_CHANNEL_VERSION >= 4) || defined (CONFIG_RELAY)
- out.produced = _stp_chan->buf[cpu]->subbufs_produced;
- out.consumed = _stp_chan->buf[cpu]->subbufs_consumed;
-#else
- out.produced = atomic_read(&_stp_chan->buf[cpu]->subbufs_produced);
- out.consumed = atomic_read(&_stp_chan->buf[cpu]->subbufs_consumed);
-#endif /* RELAYFS_CHANNEL_VERSION >= 4 || CONFIG_RELAY */
+ out.produced = atomic_read(&_stp_utt->rchan->buf[cpu]->subbufs_produced);
+ out.consumed = atomic_read(&_stp_utt->rchan->buf[cpu]->subbufs_consumed);
out.flushing = _stp_relay_flushing;
num = sizeof(out);
if (copy_from_user(&info, buf, count))
return -EFAULT;
- relay_subbufs_consumed(_stp_chan, cpu, info.consumed);
+ relay_subbufs_consumed(_stp_utt->rchan, cpu, info.consumed);
return count;
}
.read = _stp_proc_read,
.write = _stp_proc_write,
};
-#endif
+#endif /* STP_BULKMODE */
-static ssize_t _stp_proc_write_cmd (struct file *file, const char __user *buf,
+static ssize_t _stp_ctl_write_cmd (struct file *file, const char __user *buf,
size_t count, loff_t *ppos)
{
int type;
switch (type) {
case STP_START:
{
- struct _stp_transport_start st;
- if (count < sizeof(struct _stp_transport_start))
+ struct _stp_msg_start st;
+ if (count < sizeof(st))
return 0;
- if (copy_from_user (&st, buf, sizeof(struct _stp_transport_start)))
+ if (copy_from_user (&st, buf, sizeof(st)))
return -EFAULT;
_stp_handle_start (&st);
break;
case STP_EXIT:
_stp_exit_flag = 1;
break;
- case STP_TRANSPORT_INFO:
- {
- struct _stp_transport_info ti;
- kbug("STP_TRANSPORT_INFO %d %d\n", (int)count, (int)sizeof(struct _stp_transport_info));
- if (count < sizeof(struct _stp_transport_info))
- return 0;
- if (copy_from_user (&ti, buf, sizeof(struct _stp_transport_info)))
- return -EFAULT;
- if (_stp_transport_open (&ti) < 0)
- return -1;
- break;
- }
default:
- printk ("invalid command type %d\n", type);
+ errk ("invalid command type %d\n", type);
return -EINVAL;
}
char buf[STP_BUFFER_SIZE];
};
-static DECLARE_WAIT_QUEUE_HEAD(_stp_proc_wq);
+static DECLARE_WAIT_QUEUE_HEAD(_stp_ctl_wq);
-static int _stp_write (int type, void *data, int len)
+static int _stp_ctl_write (int type, void *data, int len)
{
struct _stp_buffer *bptr;
unsigned long flags;
return len;
}
+/* send commands with timeout and retry */
+static int _stp_ctl_send (int type, void *data, int len)
+{
+ int err, trylimit = 50;
+ kbug("ctl_send: type=%d len=%d\n", type, len);
+ while ((err = _stp_ctl_write(type, data, len)) < 0 && trylimit--)
+ msleep (5);
+ kbug("returning %d\n", err);
+ return err;
+}
+
static ssize_t
-_stp_proc_read_cmd (struct file *file, char __user *buf, size_t count, loff_t *ppos)
+_stp_ctl_read_cmd (struct file *file, char __user *buf, size_t count, loff_t *ppos)
{
struct _stp_buffer *bptr;
int len;
spin_unlock_irqrestore(&_stp_ready_lock, flags);
if (file->f_flags & O_NONBLOCK)
return -EAGAIN;
- if (wait_event_interruptible(_stp_proc_wq, !list_empty(&_stp_ready_q)))
+ if (wait_event_interruptible(_stp_ctl_wq, !list_empty(&_stp_ready_q)))
return -ERESTARTSYS;
spin_lock_irqsave(&_stp_ready_lock, flags);
}
static struct file_operations _stp_proc_fops_cmd = {
.owner = THIS_MODULE,
- .read = _stp_proc_read_cmd,
- .write = _stp_proc_write_cmd,
-// .poll = _stp_proc_poll_cmd
+ .read = _stp_ctl_read_cmd,
+ .write = _stp_ctl_write_cmd,
};
-static struct proc_dir_entry *_stp_proc_root, *_stp_proc_mod;
+static struct proc_dir_entry *_stp_proc_root, *_stp_proc_pid;
/* copy since proc_match is not MODULE_EXPORT'd */
static int my_proc_match(int len, const char *name, struct proc_dir_entry *de)
return _stp_current_buffers;
}
-static int _stp_register_procfs (void)
+static int _stp_register_ctl_channel (void)
{
int i;
-#ifdef STP_RELAYFS
+ char buf[32];
+#ifdef STP_BULKMODE
int j;
- char buf[8];
#endif
+
struct proc_dir_entry *de;
struct list_head *p, *tmp;
list_add (p, &_stp_pool_q);
}
- /* Formerly, we allocated /proc/systemtap, but unfortunately
- that's racy with multiple concurrent probes. So now we set
- _stp_proc_root to proc_root. This way, /proc/stap_XXXX
- rather than /proc/systemtap/stap_XXXX will be the directory
- under which cmd/ etc. will show up. */
- _stp_proc_root = NULL;
-
- /* now create /proc/systemtap/module_name */
- _stp_proc_mod = proc_mkdir (THIS_MODULE->name, _stp_proc_root);
- if (_stp_proc_mod == NULL)
+ /* now create /proc/systemtap_[pid] */
+ sprintf(buf, "systemtap_%d", _stp_pid);
+ _stp_proc_pid = proc_mkdir (buf, NULL);
+ if (!_stp_proc_pid)
goto err0;
-
-#ifdef STP_RELAYFS
- /* now for each cpu "n", create /proc/systemtap/module_name/n */
+#ifdef STP_BULKMODE
+ /* now for each cpu "n", create /proc/systemtap_[pid]/n */
for_each_cpu(i) {
sprintf(buf, "%d", i);
- de = create_proc_entry (buf, S_IFREG|S_IRUSR, _stp_proc_mod);
+ de = create_proc_entry (buf, S_IFREG|S_IRUSR, _stp_proc_pid);
if (de == NULL)
goto err1;
de->proc_fops = &_stp_proc_fops;
de->data = _stp_kmalloc(sizeof(int));
if (de->data == NULL) {
- remove_proc_entry (buf, _stp_proc_mod);
+ remove_proc_entry (buf, _stp_proc_pid);
goto err1;
}
*(int *)de->data = i;
}
-#endif
+#endif /* STP_BULKMODE */
- /* finally create /proc/systemtap/module_name/cmd */
- de = create_proc_entry ("cmd", S_IFREG|S_IRUSR, _stp_proc_mod);
+ /* finally create /proc/systemtap_[pid]/cmd */
+ de = create_proc_entry ("cmd", S_IFREG|S_IRUSR, _stp_proc_pid);
if (de == NULL)
goto err1;
de->proc_fops = &_stp_proc_fops_cmd;
return 0;
err1:
-#ifdef STP_RELAYFS
- for (de = _stp_proc_mod->subdir; de; de = de->next)
+#ifdef STP_BULKMODE
+ for (de = _stp_proc_pid->subdir; de; de = de->next)
kfree (de->data);
for_each_cpu(j) {
if (j == i)
break;
sprintf(buf, "%d", i);
- remove_proc_entry (buf, _stp_proc_mod);
+ remove_proc_entry (buf, _stp_proc_pid);
}
-#endif
- remove_proc_entry (THIS_MODULE->name, _stp_proc_root);
+#endif /* STP_BULKMODE */
+ sprintf(buf, "systemtap_%d", _stp_pid);
+ remove_proc_entry (buf, NULL);
err0:
list_for_each_safe(p, tmp, &_stp_pool_q) {
list_del(p);
kfree(p);
}
- printk (KERN_ERR "Error creating systemtap /proc entries.\n");
+ errk ("Error creating systemtap /proc entries.\n");
return -1;
}
-static void _stp_unregister_procfs (void)
+static void _stp_unregister_ctl_channel (void)
{
struct list_head *p, *tmp;
-#ifdef STP_RELAYFS
+ char buf[32];
+#ifdef STP_BULKMODE
int i;
- char buf[8];
struct proc_dir_entry *de;
-
- for (de = _stp_proc_mod->subdir; de; de = de->next)
+ kbug("unregistering procfs\n");
+ for (de = _stp_proc_pid->subdir; de; de = de->next)
kfree (de->data);
for_each_cpu(i) {
sprintf(buf, "%d", i);
- remove_proc_entry (buf, _stp_proc_mod);
+ remove_proc_entry (buf, _stp_proc_pid);
}
-#endif
- remove_proc_entry ("cmd", _stp_proc_mod);
- remove_proc_entry (THIS_MODULE->name, _stp_proc_root);
+#endif /* STP_BULKMODE */
+
+ remove_proc_entry ("cmd", _stp_proc_pid);
+ sprintf(buf, "systemtap_%d", _stp_pid);
+ remove_proc_entry (buf, NULL);
/* free memory pools */
list_for_each_safe(p, tmp, &_stp_pool_q) {
-#ifndef _TRANSPORT_RELAYFS_C_ /* -*- linux-c -*- */
-#define _TRANSPORT_RELAYFS_C_
-
-/*
- * relayfs.c - stp relayfs-related transport functions
+/* -*- linux-c -*-
+ * relayfs.c - relayfstransport functions
*
- * Copyright (C) IBM Corporation, 2005
- * Copyright (C) Redhat Inc, 2005
+ * Copyright (C) IBM Corporation, 2005, 2006
+ * Copyright (C) Red Hat Inc, 2005, 2006, 2007
*
- * This file is released under the GPL.
+ * This file is part of systemtap, and is free software. You can
+ * redistribute it and/or modify it under the terms of the GNU General
+ * Public License (GPL); either version 2, or (at your option) any
+ * later version.
*/
-/** @file relayfs.c
- * @brief Systemtap relayfs-related transport functions
- */
+/* This file is only for older kernels that have no debugfs. */
-/** @addtogroup transport Transport Functions
- * @{
- */
-
-#include "relayfs.h"
+/* relayfs is required! */
+#if !defined (CONFIG_RELAYFS_FS) && !defined (CONFIG_RELAYFS_FS_MODULE)
+#error "RelayFS does not appear to be in this kernel!"
+#endif
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/percpu.h>
+#include <linux/init.h>
+#include <linux/relayfs_fs.h>
+#include <linux/namei.h>
+#include "utt.h"
-#if (RELAYFS_CHANNEL_VERSION >= 4) || defined (CONFIG_RELAY)
+static int _stp_relay_flushing = 0;
-/**
- * _stp_subbuf_start - subbuf_start() relayfs callback implementation
- */
-static int _stp_subbuf_start(struct rchan_buf *buf,
- void *subbuf,
- void *prev_subbuf,
- size_t prev_padding)
+static void _stp_remove_relay_dir(struct dentry *dir)
{
- if (relay_buf_full(buf))
- return 0;
-
- if (prev_subbuf)
- *((unsigned *)prev_subbuf) = prev_padding;
-
- subbuf_start_reserve(buf, sizeof(unsigned int));
-
- return 1;
+ if (dir)
+ relayfs_remove_dir(dir);
}
-#else
/**
* _stp_subbuf_start - subbuf_start() relayfs callback implementation
*((unsigned *)subbuf) = padding;
}
-#endif /* RELAYFS_CHANNEL_VERSION >= 4 || CONFIG_RELAY */
-
-#if defined (CONFIG_RELAY)
-static struct dentry *_stp_create_buf_file(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 int _stp_remove_buf_file(struct dentry *dentry)
-{
- debugfs_remove(dentry);
-
- return 0;
-}
-#endif /* CONFIG_RELAY */
-
-/* relayfs callback functions */
-#if defined (CONFIG_RELAY)
static struct rchan_callbacks stp_rchan_callbacks =
{
.subbuf_start = _stp_subbuf_start,
- .create_buf_file = _stp_create_buf_file,
- .remove_buf_file = _stp_remove_buf_file,
-};
-#else
-static struct rchan_callbacks stp_rchan_callbacks =
-{
- .subbuf_start = _stp_subbuf_start,
-#if (RELAYFS_CHANNEL_VERSION < 4)
.buf_full = _stp_buf_full,
-#endif /* RELAYFS_CHANNEL_VERSION < 4 */
};
-#endif /* CONFIG_RELAY */
-
-static struct dentry *_stp_create_relay_dir(const char *dirname, struct dentry *parent)
-{
- struct dentry *dir;
-
-#if defined (CONFIG_RELAY)
- dir = debugfs_create_dir(dirname, parent);
- if (IS_ERR(dir)) {
- printk("STP: Couldn't create directory %s - debugfs not configured in.\n", dirname);
- dir = NULL;
- }
-#else
- dir = relayfs_create_dir(dirname, parent);
-#endif
-
- return dir;
-}
-static void _stp_remove_relay_dir(struct dentry *dir)
-{
- if (dir == NULL)
- return;
-
-#if defined (CONFIG_RELAY)
- debugfs_remove(dir);
-#else
- relayfs_remove_dir(dir);
-#endif
-}
-static inline void _stp_lock_inode(struct inode *inode)
+static void _stp_remove_relay_root(struct dentry *root)
{
-#ifdef DEFINE_MUTEX
- mutex_lock(&inode->i_mutex);
-#else
- down(&inode->i_sem);
-#endif
+ if (root)
+ _stp_remove_relay_dir(root);
}
-static inline void _stp_unlock_inode(struct inode *inode)
+struct utt_trace *utt_trace_setup(struct utt_trace_setup *utts)
{
-#ifdef DEFINE_MUTEX
- mutex_unlock(&inode->i_mutex);
-#else
- up(&inode->i_sem);
-#endif
-}
+ struct utt_trace *utt;
-static struct dentry *_stp_get_relay_root(void)
-{
- struct file_system_type *fs;
- struct super_block *sb;
- struct dentry *root;
- char *dirname = "systemtap";
+ utt = _stp_kzalloc(sizeof(*utt));
+ if (!utt)
+ return NULL;
- root = _stp_create_relay_dir(dirname, NULL);
- if (root)
- return root;
-
-#if defined (CONFIG_RELAY)
- fs = get_fs_type("debugfs");
-#else
- fs = get_fs_type("relayfs");
-#endif
- if (!fs)
+ utt->utt_tree_root = relayfs_create_dir(utts->root, NULL);
+ if (!utt->utt_tree_root) {
+ errk("couldn't get relay root dir.\n");
return NULL;
+ }
- sb = list_entry(fs->fs_supers.next, struct super_block, s_instances);
- _stp_lock_inode(sb->s_root->d_inode);
- root = lookup_one_len(dirname, sb->s_root, strlen(dirname));
- _stp_unlock_inode(sb->s_root->d_inode);
- if (!IS_ERR(root))
- dput(root);
- return root;
-}
-
-static void _stp_put_relay_root(struct dentry *root)
-{
- if (root)
- _stp_remove_relay_dir(root);
-}
-
-static struct dentry *_relay_root;
+ kbug("relay_open %d %d\n", utts->buf_size, utts->buf_nr);
+ utt->rchan = relay_open("trace", utt->utt_tree_root, utts->buf_size, utts->buf_nr, 0, &stp_rchan_callbacks);
+ if (!utt->rchan) {
+ errk("couldn't create relay channel.\n");
+ _stp_remove_relay_root(utt->utt_tree_root);
+ return NULL;
+ }
-/**
- * _stp_relayfs_close - destroys relayfs channel
- * @chan: the relayfs channel
- * @dir: the directory containing the relayfs files
- */
-void _stp_relayfs_close(struct rchan *chan, struct dentry *dir)
-{
- if (!chan)
- return;
- relay_close(chan);
- _stp_remove_relay_dir(dir);
- _stp_put_relay_root(_relay_root);
+ utt->rchan->private_data = utt;
+ utt->trace_state = Utt_trace_setup;
+ utts->err = 0;
+ return utt;
}
-/**
- * _stp_relayfs_open - create relayfs channel
- * @n_subbufs: number of relayfs sub-buffers
- * @subbuf_size: size of relayfs sub-buffers
- * @pid: daemon pid
- * @outdir: receives directory dentry
- *
- * Returns relay channel, NULL on failure
- *
- * Creates relayfs files as /systemtap/pid/cpuX in relayfs root
- */
-struct rchan *_stp_relayfs_open(unsigned n_subbufs,
- unsigned subbuf_size,
- int pid,
- struct dentry **outdir)
+int utt_trace_startstop(struct utt_trace *utt, int start,
+ unsigned int *trace_seq)
{
- char dirname[16];
- struct rchan *chan;
- struct dentry* root, *dir;
-
- sprintf(dirname, "%d", pid);
-
- root = _stp_get_relay_root();
- if (!root) {
- printk("STP: couldn't get relay root dir.\n");
- return NULL;
- }
+ int ret;
- dir = _stp_create_relay_dir(dirname, root);
- if (!dir) {
- printk("STP: couldn't create relay dir %s.\n", dirname);
- _stp_put_relay_root(root);
- return NULL;
- }
-
-#if (RELAYFS_CHANNEL_VERSION >= 4)
- chan = relay_open("cpu", dir, subbuf_size,
- n_subbufs, &stp_rchan_callbacks);
-#else
- chan = relay_open("cpu", dir, subbuf_size,
- n_subbufs, 0, &stp_rchan_callbacks);
-#endif /* RELAYFS_CHANNEL_VERSION >= 4 */
+ if (!utt)
+ return 0;
- if (!chan) {
- printk("STP: couldn't create relay channel.\n");
- _stp_remove_relay_dir(dir);
- _stp_put_relay_root(root);
+ /*
+ * For starting a trace, we can transition from a setup or stopped
+ * trace. For stopping a trace, the state must be running
+ */
+ ret = -EINVAL;
+ if (start) {
+ if (utt->trace_state == Utt_trace_setup ||
+ utt->trace_state == Utt_trace_stopped) {
+ if (trace_seq)
+ (*trace_seq)++;
+ smp_mb();
+ utt->trace_state = Utt_trace_running;
+ ret = 0;
+ }
+ } else {
+ if (utt->trace_state == Utt_trace_running) {
+ utt->trace_state = Utt_trace_stopped;
+ _stp_relay_flushing = 1;
+ relay_flush(utt->rchan);
+ ret = 0;
+ }
}
- _relay_root = root;
- *outdir = dir;
- return chan;
+ return ret;
}
-#endif /* _TRANSPORT_RELAYFS_C_ */
+int utt_trace_remove(struct utt_trace *utt)
+{
+ kbug("removing relayfs files. %d\n", utt->trace_state);
+ if (utt && (utt->trace_state == Utt_trace_setup || utt->trace_state == Utt_trace_stopped)) {
+ relay_close(utt->rchan);
+ _stp_remove_relay_root(utt->utt_tree_root);
+ kfree(utt);
+ }
+ return 0;
+}
switch (_stp_symbol_state) {
case 0:
if (count != 8) {
- printk("unexpected systemtap error: _stp_do_symbols: count=%d\n", count);
+ errk(" _stp_do_symbols: count=%d\n", count);
return -EFAULT;
}
if (get_user(num, (unsigned __user *)buf))
_stp_modules[0] = _stp_alloc_module(num, datasize);
if (_stp_modules[0] == NULL) {
- printk("unexpected systemtap error: cannot allocate memory\n");
+ errk("cannot allocate memory\n");
return -EFAULT;
}
_stp_symbol_state = 1;
_stp_modules_by_addr[0] = _stp_modules[0];
break;
default:
- printk("systemtap error: unexpected symbol data of size %d.\n", count);
+ errk("unexpected symbol data of size %d.\n", count);
}
return count;
}
mod = _stp_alloc_module_from_module(m);
if (mod == NULL) {
module_put(m);
- printk("Systemtap failed to allocate memory for module.\n");
+ errk("failed to allocate memory for module.\n");
return NULL;
}
int i;
if (count < sizeof(tmpmod)) {
- printk("_stp_do_modules: expected %d and got %d\n", (int)sizeof(tmpmod), count);
+ errk("expected %d and got %d\n", (int)sizeof(tmpmod), count);
return -EFAULT;
}
if (copy_from_user ((char *)&tmpmod, buf, sizeof(tmpmod)))
/* copy in section data */
tmpmod.sections = _stp_kmalloc(count - sizeof(tmpmod));
if (tmpmod.sections == NULL) {
- printk("_stp_do_module: unable to allocate memory.\n");
+ errk("unable to allocate memory.\n");
return -EFAULT;
}
if (copy_from_user ((char *)tmpmod.sections, buf+sizeof(tmpmod), count-sizeof(tmpmod))) {
return count;
}
-static int _stp_transport_send (int type, void *data, int len);
+static int _stp_ctl_send (int type, void *data, int len);
static int _stp_module_load_notify(struct notifier_block * self, unsigned long val, void * data)
{
case MODULE_STATE_COMING:
dbug("module %s loaded\n", mod->name);
strlcpy(rmod.name, mod->name, STP_MODULE_NAME_LEN);
- _stp_transport_send(STP_MODULE, &rmod, sizeof(struct _stp_module));
+ _stp_ctl_send(STP_MODULE, &rmod, sizeof(struct _stp_module));
break;
default:
- printk("module loaded? val=%ld\n", val);
+ errk("module loaded? val=%ld\n", val);
}
#endif
return 0;
#include "time.c"
#include "symbols.c"
-#ifdef STP_RELAYFS
+#ifdef STP_OLD_TRANSPORT
#include "relayfs.c"
-static struct rchan *_stp_chan;
-static struct dentry *_stp_dir;
-int _stp_relay_flushing = 0;
+#else
+#include "utt.c"
#endif
-static atomic_t _stp_start_finished = ATOMIC_INIT (0);
-static int _stp_dpid;
-static int _stp_pid;
+static struct utt_trace *_stp_utt = NULL;
+static unsigned int utt_seq = 1;
+
+static int _stp_start_finished = 0;
+static int _stp_probes_started = 0;
+/* module parameters */
+static int _stp_pid, _stp_bufsize;
module_param(_stp_pid, int, 0);
-MODULE_PARM_DESC(_stp_pid, "daemon pid");
+MODULE_PARM_DESC(_stp_pid, "pid");
+module_param(_stp_bufsize, int, 0);
+MODULE_PARM_DESC(_stp_bufsize, "buffer size");
-int _stp_target = 0;
-int _stp_exit_called = 0;
+pid_t _stp_target = 0;
+static int _stp_exit_called = 0;
int _stp_exit_flag = 0;
/* forward declarations */
void probe_exit(void);
int probe_start(void);
void _stp_exit(void);
-void _stp_handle_start (struct _stp_transport_start *st);
+void _stp_handle_start (struct _stp_msg_start *st);
+
-/* check for new workquqeq API */
+/* check for new workqueue API */
#ifdef DECLARE_DELAYED_WORK
static void _stp_work_queue (struct work_struct *data);
static DECLARE_DELAYED_WORK(_stp_work, _stp_work_queue);
#endif
static struct workqueue_struct *_stp_wq;
-int _stp_transport_open(struct _stp_transport_info *info);
+#ifdef STP_OLD_TRANSPORT
#include "procfs.c"
+#else
+#include "control.c"
+#endif
-/* send commands with timeout and retry */
-static int _stp_transport_send (int type, void *data, int len)
+void _stp_ask_for_symbols(void)
{
- int err, trylimit = 50;
- while ((err = _stp_write(type, data, len)) < 0 && trylimit--)
- msleep (5);
- return err;
-}
+ struct _stp_msg_symbol req;
+ struct _stp_module mod;
+ struct _stp_msg_trans tran;
-#ifndef STP_RELAYFS
-static int _stp_transport_write (void *data, int len)
-{
- /* when _stp_exit_called is set, we are in probe_exit() and we can sleep */
- if (_stp_exit_called)
- return _stp_transport_send (STP_REALTIME_DATA, data, len);
- return _stp_write(STP_REALTIME_DATA, data, len);
-}
-#endif /*STP_RELAYFS */
+ /* ask for symbols, modules, and send transport info */
+ kbug("AFS\n");
-/*
- * _stp_handle_buf_info - handle STP_BUF_INFO
- */
-#ifdef STP_RELAYFS
-static void _stp_handle_buf_info(int *cpuptr)
-{
- struct _stp_buf_info out;
+ req.endian = 0x1234;
+ req.ptr_size = sizeof(char *);
+ _stp_ctl_send(STP_SYMBOLS, &req, sizeof(req));
+
+ strcpy(mod.name, "");
+ _stp_ctl_send(STP_MODULE, &mod, sizeof(mod));
- out.cpu = *cpuptr;
-#if (RELAYFS_CHANNEL_VERSION >= 4) || defined (CONFIG_RELAY)
- out.produced = _stp_chan->buf[*cpuptr]->subbufs_produced;
- out.consumed = _stp_chan->buf[*cpuptr]->subbufs_consumed;
+#ifdef STP_BULKMODE
+ tran.bulk_mode = 1;
#else
- out.produced = atomic_read(&_stp_chan->buf[*cpuptr]->subbufs_produced);
- out.consumed = atomic_read(&_stp_chan->buf[*cpuptr]->subbufs_consumed);
-#endif /* RELAYFS_CHANNEL_VERSION >=_4 || CONFIG_RELAY */
-
- _stp_transport_send(STP_BUF_INFO, &out, sizeof(out));
-}
+ tran.bulk_mode = 0;
#endif
+ tran.subbuf_size =_stp_subbuf_size;
+ tran.n_subbufs = _stp_nsubbufs;
+ _stp_ctl_send(STP_TRANSPORT, &tran, sizeof(tran));
+}
/*
* _stp_handle_start - handle STP_START
*/
-void _stp_handle_start (struct _stp_transport_start *st)
+void _stp_handle_start (struct _stp_msg_start *st)
{
-#ifdef CONFIG_MODULES
- static int got_modules=0;
-#endif
-
- kbug ("stp_handle_start pid=%d\n", st->pid);
-
- /* we've got a start request, but first, grab kernel symbols if we need them */
- if (_stp_num_modules == 0) {
- struct _stp_symbol_req req;
- req.endian = 0x1234;
- req.ptr_size = sizeof(char *);
- _stp_transport_send(STP_SYMBOLS, &req, sizeof(req));
- return;
- }
-
-#ifdef CONFIG_MODULES
- /* grab current module addresses if we haven't already */
- if (got_modules == 0) {
- struct _stp_module mod;
- strcpy(mod.name, "");
- got_modules = 1;
- _stp_transport_send(STP_MODULE, &mod, sizeof(struct _stp_module));
- return;
- }
+ kbug ("stp_handle_start\n");
if (register_module_notifier(&_stp_module_load_nb))
- printk("Systemtap error: failed to load module notifier\n");
-#endif
+ errk("failed to load module notifier\n");
- /* note: st->pid is actually the return code for the reply packet */
- st->pid = probe_start();
- atomic_set(&_stp_start_finished,1);
+ _stp_target = st->target;
+ st->res = probe_start();
+ _stp_start_finished = 1;
+ if (st->res >= 0)
+ _stp_probes_started = 1;
- /* if probe_start() failed, suppress calling probe_exit() */
- if (st->pid < 0)
- _stp_exit_called = 1;
-
- _stp_transport_send(STP_START, st, sizeof(*st));
+ _stp_ctl_send(STP_START, st, sizeof(*st));
}
-#ifdef STP_RELAYFS
-/**
- * _stp_handle_subbufs_consumed - handle STP_SUBBUFS_CONSUMED
- */
-static void _stp_handle_subbufs_consumed(int pid, struct _stp_consumed_info *info)
-{
- relay_subbufs_consumed(_stp_chan, info->cpu, info->consumed);
-}
-#endif
/* common cleanup code. */
/* This is called from the kernel thread when an exit was requested */
if (!_stp_exit_called) {
int failures;
-#ifdef CONFIG_MODULES
unregister_module_notifier(&_stp_module_load_nb);
-#endif
+
/* we only want to do this stuff once */
_stp_exit_called = 1;
- kbug("calling probe_exit\n");
- /* tell the stap-generated code to unload its probes, etc */
- probe_exit();
- kbug("done with probe_exit\n");
+ if (_stp_probes_started) {
+ kbug("calling probe_exit\n");
+ /* tell the stap-generated code to unload its probes, etc */
+ probe_exit();
+ kbug("done with probe_exit\n");
+ }
failures = atomic_read(&_stp_transport_failures);
if (failures)
_stp_warn ("There were %d transport failures.\n", failures);
-#ifdef STP_RELAYFS
- if (_stp_transport_mode == STP_TRANSPORT_RELAYFS) {
- _stp_relay_flushing = 1;
- relay_flush(_stp_chan);
- }
-#endif
- kbug("transport_send STP_EXIT\n");
+ kbug("************** calling startstop 0 *************\n");
+ if (_stp_utt) utt_trace_startstop(_stp_utt, 0, &utt_seq);
+
+ kbug("ctl_send STP_EXIT\n");
/* tell staprun to exit (if it is still there) */
- _stp_transport_send(STP_EXIT, &dont_rmmod, sizeof(int));
- kbug("done with transport_send STP_EXIT\n");
+ _stp_ctl_send(STP_EXIT, &dont_rmmod, sizeof(int));
+ kbug("done with ctl_send STP_EXIT\n");
}
}
spin_unlock_irqrestore(&_stp_ready_lock, flags);
if (do_io)
- wake_up_interruptible(&_stp_proc_wq);
+ wake_up_interruptible(&_stp_ctl_wq);
/* if exit flag is set AND we have finished with probe_start() */
- if (unlikely(_stp_exit_flag && atomic_read(&_stp_start_finished))) {
+ if (unlikely(_stp_exit_flag && _stp_start_finished)) {
_stp_cleanup_and_exit(0);
cancel_delayed_work(&_stp_work);
flush_workqueue(_stp_wq);
- wake_up_interruptible(&_stp_proc_wq);
+ wake_up_interruptible(&_stp_ctl_wq);
} else
queue_delayed_work(_stp_wq, &_stp_work, STP_WORK_TIMER);
}
/**
- * _stp_transport_close - close proc and relayfs channels
+ * _stp_transport_close - close ctl and relayfs channels
*
* This is called automatically when the module is unloaded.
*
_stp_cleanup_and_exit(1);
cancel_delayed_work(&_stp_work);
destroy_workqueue(_stp_wq);
- wake_up_interruptible(&_stp_proc_wq);
-#ifdef STP_RELAYFS
- if (_stp_transport_mode == STP_TRANSPORT_RELAYFS)
- _stp_relayfs_close(_stp_chan, _stp_dir);
-#endif
-#ifdef CONFIG_MODULES
+ wake_up_interruptible(&_stp_ctl_wq);
unregister_module_notifier(&_stp_module_load_nb);
-#endif
- _stp_unregister_procfs();
+ _stp_unregister_ctl_channel();
+ if (_stp_utt) utt_trace_remove(_stp_utt);
_stp_free_modules();
_stp_kill_time();
_stp_print_cleanup(); /* free print buffers */
kbug("---- CLOSED ----\n");
}
-/**
- * _stp_transport_open - open proc and relayfs channels
- * with proper parameters
- * Returns negative on failure, >0 otherwise.
- *
- * This function registers the probe with the control channel,
- * and if the probe output will not be 'streaming', creates a
- * relayfs channel for it. This must be called before any I/O is
- * done.
- *
- * This function is called in response to an STP_TRANSPORT
- * message from staprun cmd. It replies with a similar message
- * containing the final parameters used.
- */
-int _stp_transport_open(struct _stp_transport_info *info)
+static struct utt_trace *_stp_utt_open(void)
{
- kbug ("stp_transport_open: %d Mb buffer. target=%d\n", info->buf_size, info->target);
-
- info->transport_mode = _stp_transport_mode;
- info->merge = 0;
+ struct utt_trace_setup utts;
+ sprintf(utts.root, "systemtap_%d", _stp_pid);
+ utts.buf_size = _stp_subbuf_size;
+ utts.buf_nr = _stp_nsubbufs;
- kbug("transport_mode=%d\n", info->transport_mode);
- _stp_target = info->target;
-
-#ifdef STP_RELAYFS
- if (_stp_transport_mode == STP_TRANSPORT_RELAYFS) {
- if (info->buf_size) {
- unsigned size = info->buf_size * 1024 * 1024;
- subbuf_size = ((size >> 2) + 1) * 65536;
- n_subbufs = size / subbuf_size;
- }
- info->n_subbufs = n_subbufs;
- info->subbuf_size = subbuf_size;
-
-#ifdef STP_RELAYFS_MERGE
- info->merge = 1;
-#endif
-
- _stp_chan = _stp_relayfs_open(n_subbufs, subbuf_size, _stp_pid, &_stp_dir);
- _stp_allocated_net_memory += n_subbufs * subbuf_size;
-
- if (!_stp_chan)
- return -ENOMEM;
- kbug ("stp_transport_open: %u Mb buffers, subbuf_size=%u, n_subbufs=%u\n",
- info->buf_size, subbuf_size, n_subbufs);
- } else
+#ifdef STP_BULKMODE
+ utts.is_global = 0;
+#else
+ utts.is_global = 1;
#endif
- {
- if (info->buf_size)
- _stp_set_buffers(info->buf_size * 1024 * 1024 / STP_BUFFER_SIZE);
- }
- /* send reply */
- return _stp_transport_send (STP_TRANSPORT_INFO, info, sizeof(*info));
+ return utt_trace_setup(&utts);
}
/**
kbug("transport_init from %ld %ld\n", (long)_stp_pid, (long)current->pid);
- /* create print buffers */
- ret = _stp_print_init();
- if (ret < 0)
- goto out;
+ if (_stp_bufsize) {
+ unsigned size = _stp_bufsize * 1024 * 1024;
+ _stp_subbuf_size = ((size >> 2) + 1) * 65536;
+ _stp_nsubbufs = size / _stp_subbuf_size;
+ kbug("Using %d subbufs of size %d\n", _stp_nsubbufs, _stp_subbuf_size);
+ }
/* initialize timer code */
- ret =_stp_init_time();
- if (ret)
- goto print_cleanup;
+ if (_stp_init_time())
+ return -1;
+
+#if !defined (STP_OLD_TRANSPORT) || defined (STP_BULKMODE)
+ /* open utt (relayfs) channel to send data to userspace */
+ _stp_utt = _stp_utt_open();
+ if (!_stp_utt)
+ goto err0;
+#endif
+ /* create debugfs/procfs control channel */
+ if (_stp_register_ctl_channel() < 0)
+ goto err1;
+
+ /* create print buffers */
+ if (_stp_print_init() < 0)
+ goto err2;
- /* set up procfs communications */
- ret = _stp_register_procfs();
- if (ret < 0)
- goto kill_time;
+ utt_trace_startstop(_stp_utt, 1, &utt_seq);
/* create workqueue of kernel threads */
_stp_wq = create_workqueue("systemtap");
if (!_stp_wq)
- goto proc_cleanup;
+ goto err3;
+
queue_delayed_work(_stp_wq, &_stp_work, STP_WORK_TIMER);
-out:
- return ret;
-proc_cleanup:
- ret = -1;
- _stp_unregister_procfs();
-kill_time:
+ /* request symbolic information */
+ _stp_ask_for_symbols();
+ return 0;
+
+err3:
+ _stp_print_cleanup();
+err2:
+ _stp_unregister_ctl_channel();
+err1:
+ if (_stp_utt) utt_trace_remove(_stp_utt);
+err0:
_stp_kill_time();
-print_cleanup:
- _stp_print_cleanup(); /* free print buffers */
- goto out;
+ return -1;
}
-
-/* like relay_write except returns an error code */
-
-#ifdef STP_RELAYFS
-static int _stp_relay_write (const void *data, unsigned length)
-{
- unsigned long flags;
- struct rchan_buf *buf;
-
- if (unlikely(length == 0))
- return 0;
-
- local_irq_save(flags);
- buf = _stp_chan->buf[smp_processor_id()];
- if (unlikely(buf->offset + length > _stp_chan->subbuf_size))
- length = relay_switch_subbuf(buf, length);
- memcpy(buf->data + buf->offset, data, length);
- buf->offset += length;
- local_irq_restore(flags);
-
- if (unlikely(length == 0))
- return -1;
-
- return length;
-}
-#endif
#endif /* _TRANSPORT_C_ */
* @brief Header file for stp transport
*/
-#include "relayfs.h"
#include "transport_msgs.h"
void _stp_warn (const char *fmt, ...);
/* how often the work queue wakes up and checks buffers */
#define STP_WORK_TIMER (HZ/100)
-#ifdef STP_RELAYFS
-static unsigned n_subbufs = 16;
-static unsigned subbuf_size = 65536;
-#define _stp_transport_write(data, len) _stp_relay_write(data, len)
-static int _stp_transport_mode = STP_TRANSPORT_RELAYFS;
-#else
-static int _stp_transport_mode = STP_TRANSPORT_PROC;
-#endif
-
+static unsigned _stp_nsubbufs = 4;
+static unsigned _stp_subbuf_size = 65536*2;
extern void _stp_transport_close(void);
extern int _stp_print_init(void);
extern void _stp_print_cleanup(void);
-/* SystemTap transport values */
-enum
-{
- STP_TRANSPORT_PROC = 1,
- STP_TRANSPORT_RELAYFS
+/* -*- linux-c -*-
+ * transport_msgs.h - messages exchanged between module and userspace
+ *
+ * Copyright (C) Red Hat Inc, 2006-2007
+ *
+ * This file is part of systemtap, and is free software. You can
+ * redistribute it and/or modify it under the terms of the GNU General
+ * Public License (GPL); either version 2, or (at your option) any
+ * later version.
+ */
+
+struct _stp_trace {
+ uint32_t sequence; /* event number */
+ uint32_t pdu_len; /* length of data after this trace */
};
/* stp control channel command values */
enum
{
- STP_BUF_INFO = 1,
- STP_SUBBUFS_CONSUMED,
- STP_REALTIME_DATA,
- STP_TRANSPORT_INFO,
STP_START,
STP_EXIT,
STP_OOB_DATA,
STP_SYSTEM,
STP_SYMBOLS,
STP_MODULE,
+ STP_TRANSPORT,
+ STP_CONNECT,
+ STP_DISCONNECT,
+#ifdef STP_OLD_TRANSPORT
+ /** deprecated **/
+ STP_BUF_INFO,
+ STP_SUBBUFS_CONSUMED,
+ STP_REALTIME_DATA,
+#endif
};
-/* control channel command structs */
-struct _stp_buf_info
+/* control channel messages */
+
+/* command to execute: sent to staprun */
+struct _stp_msg_cmd
{
- int32_t cpu;
- uint32_t produced;
- uint32_t consumed;
- int32_t flushing;
+ char cmd[128];
};
-struct _stp_consumed_info
+/* request for symbol data. sent to staprun */
+struct _stp_msg_symbol
{
- int32_t cpu;
- uint32_t consumed;
+ int32_t endian;
+ int32_t ptr_size;
};
-struct _stp_transport_info
+/* Request to start probes. */
+/* Sent from staprun. Then returned from module. */
+struct _stp_msg_start
{
- uint32_t buf_size;
- uint32_t subbuf_size;
- uint32_t n_subbufs;
- int32_t transport_mode;
- int32_t merge; // merge relayfs output?
- int32_t target; // target pid
-#if 0
- char cmd[256]; // cmd to process data
-#endif
+ pid_t target;
+ int32_t res; // for reply: result of probe_start()
};
-struct _stp_transport_start
+/* transport information. sent to staprun */
+struct _stp_msg_trans
{
- int32_t pid; // pid for streaming data
+ int32_t bulk_mode;
+
+ /* old relayfs uses these */
+ uint32_t subbuf_size;
+ uint32_t n_subbufs;
};
-struct _stp_cmd_info
+#ifdef STP_OLD_TRANSPORT
+/**** for compatibility with old relayfs ****/
+struct _stp_buf_info
{
- char cmd[128];
+ int32_t cpu;
+ uint32_t produced;
+ uint32_t consumed;
+ int32_t flushing;
};
-
-struct _stp_symbol_req
+struct _stp_consumed_info
{
- int32_t endian;
- int32_t ptr_size;
+ int32_t cpu;
+ uint32_t consumed;
};
-
-
+#endif
--- /dev/null
+/* -*- linux-c -*-
+ *
+ * This is a modified version of the proposed utt interface. If that
+ * interface makes it into the kernel, this file can go away.
+ *
+ * Copyright (C) 2006 Jens Axboe <axboe@suse.de>
+ *
+ * Moved to utt.c by Tom Zanussi, 2006
+ *
+ * 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/percpu.h>
+#include <linux/init.h>
+#include <linux/debugfs.h>
+#include <linux/relay.h>
+#include "utt.h"
+
+static void utt_remove_root(struct utt_trace *utt)
+{
+ if (utt->utt_tree_root && simple_empty(utt->utt_tree_root)) {
+ debugfs_remove(utt->utt_tree_root);
+ utt->utt_tree_root = NULL;
+ }
+}
+
+static void utt_remove_tree(struct utt_trace *utt)
+{
+ utt_remove_root(utt);
+}
+
+static struct dentry *utt_create_tree(struct utt_trace *utt, const char *root)
+{
+ if (root == NULL)
+ return NULL;
+
+ if (!utt->utt_tree_root)
+ utt->utt_tree_root = debugfs_create_dir(root, NULL);
+ return utt->utt_tree_root;
+}
+
+
+void utt_trace_cleanup(struct utt_trace *utt)
+{
+ relay_close(utt->rchan);
+ debugfs_remove(utt->dropped_file);
+ utt_remove_tree(utt);
+ free_percpu(utt->sequence);
+ kfree(utt);
+}
+
+int utt_trace_remove(struct utt_trace *utt)
+{
+ if (utt->trace_state == Utt_trace_setup ||
+ utt->trace_state == Utt_trace_stopped)
+ utt_trace_cleanup(utt);
+
+ return 0;
+}
+
+static int utt_dropped_open(struct inode *inode, struct file *filp)
+{
+ filp->private_data = inode->i_private;
+
+ return 0;
+}
+
+static ssize_t utt_dropped_read(struct file *filp, char __user *buffer,
+ size_t count, loff_t *ppos)
+{
+ struct utt_trace *utt = filp->private_data;
+ char buf[16];
+
+ snprintf(buf, sizeof(buf), "%u\n", atomic_read(&utt->dropped));
+
+ return simple_read_from_buffer(buffer, count, ppos, buf, strlen(buf));
+}
+
+static struct file_operations utt_dropped_fops = {
+ .owner = THIS_MODULE,
+ .open = utt_dropped_open,
+ .read = utt_dropped_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 utt_subbuf_start_callback(struct rchan_buf *buf, void *subbuf,
+ void *prev_subbuf, size_t prev_padding)
+{
+ struct utt_trace *utt;
+
+ if (!relay_buf_full(buf))
+ return 1;
+
+ utt = buf->chan->private_data;
+ atomic_inc(&utt->dropped);
+ return 0;
+}
+
+static int utt_remove_buf_file_callback(struct dentry *dentry)
+{
+ debugfs_remove(dentry);
+ return 0;
+}
+
+static struct dentry *utt_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 *utt_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 utt_relay_callbacks = {
+ .subbuf_start = utt_subbuf_start_callback,
+ .create_buf_file = utt_create_buf_file_callback,
+ .remove_buf_file = utt_remove_buf_file_callback,
+};
+
+static struct rchan_callbacks utt_relay_callbacks_global = {
+ .subbuf_start = utt_subbuf_start_callback,
+ .create_buf_file = utt_create_global_buf_file_callback,
+ .remove_buf_file = utt_remove_buf_file_callback,
+};
+
+/*
+ * Setup everything required to start tracing
+ */
+struct utt_trace *utt_trace_setup(struct utt_trace_setup *utts)
+{
+ struct utt_trace *utt = NULL;
+ struct dentry *dir = NULL;
+ int ret = -EINVAL;
+
+ if (!utts->buf_size || !utts->buf_nr)
+ goto err;
+
+ ret = -ENOMEM;
+ utt = kzalloc(sizeof(*utt), GFP_KERNEL);
+ if (!utt)
+ goto err;
+
+ utt->sequence = alloc_percpu(unsigned long);
+ if (!utt->sequence)
+ goto err;
+
+ ret = -ENOENT;
+ dir = utt_create_tree(utt, utts->root);
+ if (!dir)
+ goto err;
+
+ atomic_set(&utt->dropped, 0);
+
+ ret = -EIO;
+ utt->dropped_file = debugfs_create_file("dropped", 0444, dir, utt, &utt_dropped_fops);
+ if (!utt->dropped_file)
+ goto err;
+
+#if (RELAYFS_CHANNEL_VERSION >= 7)
+ if (utts->is_global)
+ utt->rchan = relay_open("trace", dir, utts->buf_size, utts->buf_nr,
+ &utt_relay_callbacks_global, NULL);
+ else
+ utt->rchan = relay_open("trace", dir, utts->buf_size, utts->buf_nr,
+ &utt_relay_callbacks, NULL);
+#else
+ if (utts->is_global)
+ utt->rchan = relay_open("trace", dir, utts->buf_size, utts->buf_nr, &utt_relay_callbacks_global);
+ else
+ utt->rchan = relay_open("trace", dir, utts->buf_size, utts->buf_nr, &utt_relay_callbacks);
+#endif
+
+ if (!utt->rchan)
+ goto err;
+ utt->rchan->private_data = utt;
+
+ utt->trace_state = Utt_trace_setup;
+
+ utts->err = 0;
+ return utt;
+err:
+ if (utt) {
+ if (utt->dropped_file)
+ debugfs_remove(utt->dropped_file);
+ if (utt->sequence)
+ free_percpu(utt->sequence);
+ if (utt->rchan)
+ relay_close(utt->rchan);
+ kfree(utt);
+ }
+ if (dir)
+ utt_remove_tree(utt);
+ utts->err = ret;
+ return NULL;
+}
+
+int utt_trace_startstop(struct utt_trace *utt, int start,
+ unsigned int *trace_seq)
+{
+ int ret;
+
+ /*
+ * For starting a trace, we can transition from a setup or stopped
+ * trace. For stopping a trace, the state must be running
+ */
+ ret = -EINVAL;
+ if (start) {
+ if (utt->trace_state == Utt_trace_setup ||
+ utt->trace_state == Utt_trace_stopped) {
+ if (trace_seq)
+ (*trace_seq)++;
+ smp_mb();
+ utt->trace_state = Utt_trace_running;
+ ret = 0;
+ }
+ } else {
+ if (utt->trace_state == Utt_trace_running) {
+ utt->trace_state = Utt_trace_stopped;
+ relay_flush(utt->rchan);
+ ret = 0;
+ }
+ }
+
+ return ret;
+}
--- /dev/null
+#ifndef UTT_H
+#define UTT_H
+
+enum {
+ Utt_trace_setup = 1,
+ Utt_trace_running,
+ Utt_trace_stopped,
+};
+
+struct utt_trace {
+ int trace_state;
+ struct rchan *rchan;
+ unsigned long *sequence;
+ struct dentry *dropped_file;
+ atomic_t dropped;
+ struct dentry *utt_tree_root;
+ void *private_data;
+};
+
+#define UTT_TRACE_ROOT_NAME_SIZE 32 /* Largest string for a root dir identifier */
+#define UTT_TRACE_NAME_SIZE 32 /* Largest string for a trace identifier */
+
+/*
+ * User setup structure
+ */
+struct utt_trace_setup {
+ char root[UTT_TRACE_ROOT_NAME_SIZE]; /* input */
+ char name[UTT_TRACE_NAME_SIZE]; /* input */
+ u32 buf_size; /* input */
+ u32 buf_nr; /* input */
+ int is_global; /* input */
+ int err; /* output */
+};
+
+
+extern struct utt_trace *utt_trace_setup(struct utt_trace_setup *utts);
+extern int utt_trace_startstop(struct utt_trace *utt, int start,
+ unsigned int *trace_seq);
+extern void utt_trace_cleanup(struct utt_trace *utt);
+extern int utt_trace_remove(struct utt_trace *utt);
+
+#endif