diff options
31 files changed, 1414 insertions, 725 deletions
diff --git a/runtime/debug.h b/runtime/debug.h index e8b2e701..5c9cca74 100644 --- a/runtime/debug.h +++ b/runtime/debug.h @@ -14,7 +14,6 @@ * _dbug() writes to systemtap stderr. * errk() writes to the system log. */ -static int _stp_transport_state = 0; #define _dbug(args...) _stp_dbug(__FUNCTION__, __LINE__, args) @@ -39,8 +38,14 @@ static int _stp_transport_state = 0; printk(args); \ } \ } while (0) + +#define dbug_trans2(args...) do { \ + printk("%s:%d ",__FUNCTION__, __LINE__); \ + printk(args); \ + } while (0) #else #define dbug_trans(level, args...) ; +#define dbug_trans2(args...) ; #endif #ifdef DEBUG_UNWIND /* stack unwinder */ diff --git a/runtime/print.c b/runtime/print.c index 964a73c2..d51c8108 100644 --- a/runtime/print.c +++ b/runtime/print.c @@ -13,6 +13,8 @@ #include "string.h" +#include "vsprintf.c" +#include "print.h" #include "transport/transport.c" #include "vsprintf.c" @@ -86,14 +88,8 @@ static void _stp_print_cleanup (void) #endif #if !defined(RELAY_GUEST) -/* The relayfs API changed between 2.6.15 and 2.6.16. */ -/* Use the appropriate print flush function. */ -#ifdef STP_OLD_TRANSPORT -#include "print_old.c" -#else #include "print_new.c" -#endif #if defined(RELAY_HOST) EXPORT_SYMBOL_GPL(EXPORT_FN(stp_print_flush)); #endif diff --git a/runtime/print.h b/runtime/print.h new file mode 100644 index 00000000..c2731a2f --- /dev/null +++ b/runtime/print.h @@ -0,0 +1,17 @@ +/* -*- linux-c -*- + * Copyright (C) 2009 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. + */ + +#ifndef _STP_PRINT_H_ +#define _STP_PRINT_H_ + +static int _stp_print_init(void); +static void _stp_print_cleanup(void); +static void *_stp_reserve_bytes(int numbytes); + +#endif /* _STP_PRINT_H_ */ diff --git a/runtime/print_new.c b/runtime/print_new.c index fa7b4727..2d5a6e10 100644 --- a/runtime/print_new.c +++ b/runtime/print_new.c @@ -18,50 +18,113 @@ static DEFINE_SPINLOCK(_stp_print_lock); -void EXPORT_FN(stp_print_flush) (_stp_pbuf *pb) +void EXPORT_FN(stp_print_flush)(_stp_pbuf *pb) { - uint32_t len = pb->len; + size_t len = pb->len; + void *entry = NULL; /* check to see if there is anything in the buffer */ - if (likely (len == 0)) + dbug_trans(1, "len = %zu\n", len); + if (likely(len == 0)) return; pb->len = 0; - if (unlikely(!_stp_utt || _stp_utt->trace_state != Utt_trace_running)) - return; +//DRS FIXME: this digs down too deep in internals +// if (unlikely(!_stp_utt || _stp_utt->trace_state != Utt_trace_running)) +// return; #ifdef STP_BULKMODE - { #ifdef NO_PERCPU_HEADERS - void *buf = utt_reserve(_stp_utt, len); - if (likely(buf)) - memcpy(buf, pb->buf, len); - else - atomic_inc (&_stp_transport_failures); -#else - void *buf = utt_reserve(_stp_utt, - sizeof(struct _stp_trace) + len); - if (likely(buf)) { - struct _stp_trace t = { .sequence = _stp_seq_inc(), - .pdu_len = len}; - memcpy(buf, &t, sizeof(t)); // prevent unaligned access - memcpy(buf + sizeof(t), pb->buf, len); - } else - atomic_inc (&_stp_transport_failures); -#endif - } -#else { - void *buf; + char *bufp = pb->buf; + + while (len > 0) { + size_t bytes_reserved; + + bytes_reserved = _stp_data_write_reserve(len, &entry); + if (likely(entry && bytes_reserved > 0)) { + memcpy(_stp_data_entry_data(entry), bufp, + bytes_reserved); + _stp_data_write_commit(entry); + bufp += bytes_reserved; + len -= bytes_reserved; + } + else { + atomic_inc(&_stp_transport_failures); + break; + } + } + } + +#else /* !NO_PERCPU_HEADERS */ + + { + char *bufp = pb->buf; + struct _stp_trace t = { .sequence = _stp_seq_inc(), + .pdu_len = len}; + size_t bytes_reserved; + + bytes_reserved = _stp_data_write_reserve(sizeof(struct _stp_trace), &entry); + if (likely(entry && bytes_reserved > 0)) { + /* prevent unaligned access by using memcpy() */ + memcpy(_stp_data_entry_data(entry), &t, sizeof(t)); + _stp_data_write_commit(entry); + } + else { + atomic_inc(&_stp_transport_failures); + return; + } + + while (len > 0) { + bytes_reserved = _stp_data_write_reserve(len, &entry); + if (likely(entry && bytes_reserved > 0)) { + memcpy(_stp_data_entry_data(entry), bufp, + bytes_reserved); + _stp_data_write_commit(entry); + bufp += bytes_reserved; + len -= bytes_reserved; + } + else { + atomic_inc(&_stp_transport_failures); + break; + } + } + } +#endif /* !NO_PERCPU_HEADERS */ + +#else /* !STP_BULKMODE */ + +#if STP_TRANSPORT_VERSION == 1 + + if (unlikely(_stp_ctl_write(STP_REALTIME_DATA, pb->buf, len) <= 0)) + atomic_inc (&_stp_transport_failures); + +#else /* STP_TRANSPORT_VERSION != 1 */ + { unsigned long flags; + char *bufp = pb->buf; + + dbug_trans(1, "calling _stp_data_write...\n"); spin_lock_irqsave(&_stp_print_lock, flags); - buf = utt_reserve(_stp_utt, len); - if (likely(buf)) - memcpy(buf, pb->buf, len); - else - atomic_inc (&_stp_transport_failures); + while (len > 0) { + size_t bytes_reserved; + + bytes_reserved = _stp_data_write_reserve(len, &entry); + if (likely(entry && bytes_reserved > 0)) { + memcpy(_stp_data_entry_data(entry), bufp, + bytes_reserved); + _stp_data_write_commit(entry); + bufp += bytes_reserved; + len -= bytes_reserved; + } + else { + atomic_inc(&_stp_transport_failures); + break; + } + } spin_unlock_irqrestore(&_stp_print_lock, flags); } -#endif /* STP_BULKMODE */ +#endif /* STP_TRANSPORT_VERSION != 1 */ +#endif /* !STP_BULKMODE */ } diff --git a/runtime/runtime.h b/runtime/runtime.h index c2e927cc..7418d13b 100644 --- a/runtime/runtime.h +++ b/runtime/runtime.h @@ -34,9 +34,14 @@ #if !defined (CONFIG_DEBUG_FS) && !defined (CONFIG_DEBUG_FS_MODULE) #error "DebugFS is required and was not found in the kernel." #endif +#ifdef CONFIG_RING_BUFFER +#define STP_TRANSPORT_VERSION 3 +#else +#define STP_TRANSPORT_VERSION 2 +#endif #else /* older kernels have no debugfs and older version of relayfs. */ -#define STP_OLD_TRANSPORT +#define STP_TRANSPORT_VERSION 1 #endif #ifndef stp_for_each_cpu @@ -45,6 +50,9 @@ static void _stp_dbug (const char *func, int line, const char *fmt, ...); static void _stp_error (const char *fmt, ...); +static void _stp_warn (const char *fmt, ...); + +static void _stp_exit(void); #include "debug.h" diff --git a/runtime/staprun/mainloop.c b/runtime/staprun/mainloop.c index 205fdf37..7125a7bb 100644 --- a/runtime/staprun/mainloop.c +++ b/runtime/staprun/mainloop.c @@ -487,7 +487,7 @@ int stp_main_loop(void) nb -= sizeof(uint32_t); switch (type) { -#ifdef STP_OLD_TRANSPORT +#if STP_TRANSPORT_VERSION == 1 case STP_REALTIME_DATA: if (write_realtime_data(data, nb)) { _perr("write error (nb=%ld)", (long)nb); diff --git a/runtime/staprun/staprun.h b/runtime/staprun/staprun.h index acc533b2..bd6402e4 100644 --- a/runtime/staprun/staprun.h +++ b/runtime/staprun/staprun.h @@ -94,8 +94,9 @@ extern char *__name__; /* Grabbed from linux/module.h kernel include. */ #define MODULE_NAME_LEN (64 - sizeof(unsigned long)) -/* we define this so we are compatible with old transport, but we don't have to use it. */ -#define STP_OLD_TRANSPORT +/* We define this so we are compatible with old transport, but we + * don't have to use it. */ +#define STP_TRANSPORT_VERSION 1 #include "../transport/transport_msgs.h" #define RELAYFS_MAGIC 0xF0B4A981 diff --git a/runtime/sym.c b/runtime/sym.c index 63dad1af..386005b2 100644 --- a/runtime/sym.c +++ b/runtime/sym.c @@ -12,6 +12,7 @@ #ifndef _STP_SYM_C_ #define _STP_SYM_C_ +#include "sym.h" #include "string.c" #include "task_finder_vma.c" diff --git a/runtime/transport/control.c b/runtime/transport/control.c index 680d7306..35130f0f 100644 --- a/runtime/transport/control.c +++ b/runtime/transport/control.c @@ -9,6 +9,11 @@ * later version. */ +#include "control.h" +#include "../mempool.c" +#include "symbols.c" +#include <linux/delay.h> + static _stp_mempool_t *_stp_pool_q; static struct list_head _stp_ctl_ready_q; static DEFINE_SPINLOCK(_stp_ctl_ready_lock); @@ -32,7 +37,8 @@ static ssize_t _stp_ctl_write_cmd(struct file *file, const char __user *buf, siz #ifdef DEBUG_TRANS if (type < STP_MAX_CMD) - _dbug("Got %s. len=%d\n", _stp_command_name[type], (int)count); + dbug_trans2("Got %s. len=%d\n", _stp_command_name[type], + (int)count); #endif switch (type) { @@ -79,27 +85,46 @@ static void _stp_ctl_write_dbug(int type, void *data, int len) char buf[64]; switch (type) { case STP_START: - _dbug("sending STP_START\n"); + dbug_trans2("sending STP_START\n"); break; case STP_EXIT: - _dbug("sending STP_EXIT\n"); + dbug_trans2("sending STP_EXIT\n"); break; case STP_OOB_DATA: snprintf(buf, sizeof(buf), "%s", (char *)data); - _dbug("sending %d bytes of STP_OOB_DATA: %s\n", len, buf); + dbug_trans2("sending %d bytes of STP_OOB_DATA: %s\n", len, + buf); break; case STP_SYSTEM: snprintf(buf, sizeof(buf), "%s", (char *)data); - _dbug("sending STP_SYSTEM: %s\n", buf); + dbug_trans2("sending STP_SYSTEM: %s\n", buf); break; case STP_TRANSPORT: - _dbug("sending STP_TRANSPORT\n"); + dbug_trans2("sending STP_TRANSPORT\n"); + break; + case STP_CONNECT: + dbug_trans2("sending STP_CONNECT\n"); + break; + case STP_DISCONNECT: + dbug_trans2("sending STP_DISCONNECT\n"); + break; + case STP_BULK: + dbug_trans2("sending STP_BULK\n"); + break; + case STP_READY: + case STP_RELOCATION: + case STP_BUF_INFO: + case STP_SUBBUFS_CONSUMED: + dbug_trans2("sending old message\n"); + break; + case STP_REALTIME_DATA: + dbug_trans2("sending %d bytes of STP_REALTIME_DATA\n", len); break; case STP_REQUEST_EXIT: - _dbug("sending STP_REQUEST_EXIT\n"); + dbug_trans2("sending STP_REQUEST_EXIT\n"); break; default: - _dbug("ERROR: unknown message type: %d\n", type); + dbug_trans2("ERROR: unknown message type: %d\n", type); break; } } @@ -197,7 +222,7 @@ static ssize_t _stp_ctl_read_cmd(struct file *file, char __user *buf, static int _stp_ctl_open_cmd(struct inode *inode, struct file *file) { - if (_stp_attached) + if (_stp_ctl_attached) return -1; _stp_attach(); return 0; @@ -205,7 +230,7 @@ static int _stp_ctl_open_cmd(struct inode *inode, struct file *file) static int _stp_ctl_close_cmd(struct inode *inode, struct file *file) { - if (_stp_attached) + if (_stp_ctl_attached) _stp_detach(); return 0; } diff --git a/runtime/transport/control.h b/runtime/transport/control.h index 5e7204ee..48289276 100644 --- a/runtime/transport/control.h +++ b/runtime/transport/control.h @@ -15,7 +15,6 @@ #include <linux/spinlock.h> #include <linux/list.h> -static _stp_mempool_t *_stp_pool_q; static struct list_head _stp_ctl_ready_q; static spinlock_t _stp_ctl_ready_lock; static wait_queue_head_t _stp_ctl_wq; diff --git a/runtime/transport/debugfs.c b/runtime/transport/debugfs.c index 85ee604d..6bbef53b 100644 --- a/runtime/transport/debugfs.c +++ b/runtime/transport/debugfs.c @@ -10,6 +10,7 @@ */ #include <linux/debugfs.h> +#include "transport.h" #define STP_DEFAULT_BUFFERS 50 @@ -22,18 +23,26 @@ static struct dentry *_stp_cmd_file = NULL; static int _stp_register_ctl_channel_fs(void) { - if (_stp_utt == NULL) { - errk("_expected _stp_utt to be set.\n"); + struct dentry *module_dir = _stp_get_module_dir(); + if (module_dir == NULL) { + errk("no module directory found.\n"); return -1; } /* create [debugfs]/systemtap/module_name/.cmd */ - _stp_cmd_file = debugfs_create_file(".cmd", 0600, _stp_utt->dir, + _stp_cmd_file = debugfs_create_file(".cmd", 0600, module_dir, NULL, &_stp_ctl_fops_cmd); if (_stp_cmd_file == NULL) { errk("Error creating systemtap debugfs entries.\n"); return -1; } + else if (IS_ERR(_stp_cmd_file)) { + _stp_cmd_file = NULL; + errk("Error creating systemtap debugfs entries: %ld\n", + -PTR_ERR(_stp_cmd_file)); + return -1; + } + _stp_cmd_file->d_inode->i_uid = _stp_uid; _stp_cmd_file->d_inode->i_gid = _stp_gid; diff --git a/runtime/transport/procfs.c b/runtime/transport/procfs.c index 6afbdea1..9e05cc14 100644 --- a/runtime/transport/procfs.c +++ b/runtime/transport/procfs.c @@ -14,7 +14,6 @@ #define STP_DEFAULT_BUFFERS 256 #ifdef STP_BULKMODE -extern int _stp_relay_flushing; /* handle the per-cpu subbuf info read for relayfs */ static ssize_t _stp_proc_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) { @@ -23,13 +22,13 @@ static ssize_t _stp_proc_read(struct file *file, char __user *buf, size_t count, int cpu = *(int *)(PDE(file->f_dentry->d_inode)->data); - if (!_stp_utt->rchan) + if (!_stp_relay_data.rchan) return -EINVAL; out.cpu = cpu; - 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; + out.produced = atomic_read(&_stp_relay_data.rchan->buf[cpu]->subbufs_produced); + out.consumed = atomic_read(&_stp_relay_data.rchan->buf[cpu]->subbufs_consumed); + out.flushing = _stp_relay_data.flushing; num = sizeof(out); if (copy_to_user(buf, &out, num)) @@ -46,7 +45,7 @@ static ssize_t _stp_proc_write(struct file *file, const char __user *buf, size_t if (copy_from_user(&info, buf, count)) return -EFAULT; - relay_subbufs_consumed(_stp_utt->rchan, cpu, info.consumed); + relay_subbufs_consumed(_stp_relay_data.rchan, cpu, info.consumed); return count; } diff --git a/runtime/transport/relay_v2.c b/runtime/transport/relay_v2.c new file mode 100644 index 00000000..16b5afa1 --- /dev/null +++ b/runtime/transport/relay_v2.c @@ -0,0 +1,370 @@ +/* -*- linux-c -*- + * + * This transport version uses relayfs on top of a debugfs file. This + * code started as a proposed relayfs interface called 'utt'. It has + * been modified and simplified for systemtap. + * + * Changes Copyright (C) 2009 Red Hat Inc. + * + * Original utt code by: + * 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/mm.h> +#include <linux/relay.h> +#include <linux/timer.h> + +#ifndef STP_RELAY_TIMER_INTERVAL +/* Wakeup timer interval in jiffies (default 10 ms) */ +#define STP_RELAY_TIMER_INTERVAL ((HZ + 99) / 100) +#endif + +enum _stp_transport_state { + STP_TRANSPORT_STOPPED, + STP_TRANSPORT_INITIALIZED, + STP_TRANSPORT_RUNNING, +}; + +struct _stp_relay_data_type { + enum _stp_transport_state transport_state; + struct rchan *rchan; + struct dentry *dropped_file; + atomic_t dropped; + atomic_t wakeup; + struct timer_list timer; + int overwrite_flag; +}; +struct _stp_relay_data_type _stp_relay_data; + +/* + * __stp_relay_switch_subbuf - switch to a new sub-buffer + * + * Most of this function is deadcopy of relay_switch_subbuf. + */ +static size_t __stp_relay_switch_subbuf(struct rchan_buf *buf, size_t length) +{ + char *old, *new; + size_t old_subbuf, new_subbuf; + + if (unlikely(buf == NULL)) + return 0; + + if (unlikely(length > buf->chan->subbuf_size)) + goto toobig; + + if (buf->offset != buf->chan->subbuf_size + 1) { + buf->prev_padding = buf->chan->subbuf_size - buf->offset; + old_subbuf = buf->subbufs_produced % buf->chan->n_subbufs; + buf->padding[old_subbuf] = buf->prev_padding; + buf->subbufs_produced++; + buf->dentry->d_inode->i_size += buf->chan->subbuf_size - + buf->padding[old_subbuf]; + smp_mb(); + if (waitqueue_active(&buf->read_wait)) + /* + * Calling wake_up_interruptible() and __mod_timer() + * from here will deadlock if we happen to be logging + * from the scheduler and timer (trying to re-grab + * rq->lock/timer->base->lock), so just set a flag. + */ + atomic_set(&_stp_relay_data.wakeup, 1); + } + + old = buf->data; + new_subbuf = buf->subbufs_produced % buf->chan->n_subbufs; + new = (char*)buf->start + new_subbuf * buf->chan->subbuf_size; + buf->offset = 0; + if (!buf->chan->cb->subbuf_start(buf, new, old, buf->prev_padding)) { + buf->offset = buf->chan->subbuf_size + 1; + return 0; + } + buf->data = new; + buf->padding[new_subbuf] = 0; + + if (unlikely(length + buf->offset > buf->chan->subbuf_size)) + goto toobig; + + return length; + +toobig: + buf->chan->last_toobig = length; + return 0; +} + +static void __stp_relay_wakeup_readers(struct rchan_buf *buf) +{ + if (buf && waitqueue_active(&buf->read_wait) && + buf->subbufs_produced != buf->subbufs_consumed) + wake_up_interruptible(&buf->read_wait); +} + +static void __stp_relay_wakeup_timer(unsigned long val) +{ +#ifdef STP_BULKMODE + int i; +#endif + + if (atomic_read(&_stp_relay_data.wakeup)) { + atomic_set(&_stp_relay_data.wakeup, 0); +#ifdef STP_BULKMODE + for_each_possible_cpu(i) + __stp_relay_wakeup_readers(_stp_relay_data.rchan->buf[i]); +#else + __stp_relay_wakeup_readers(_stp_relay_data.rchan->buf[0]); +#endif + } + + mod_timer(&_stp_relay_data.timer, jiffies + STP_RELAY_TIMER_INTERVAL); +} + +static void __stp_relay_timer_init(void) +{ + atomic_set(&_stp_relay_data.wakeup, 0); + init_timer(&_stp_relay_data.timer); + _stp_relay_data.timer.expires = jiffies + STP_RELAY_TIMER_INTERVAL; + _stp_relay_data.timer.function = __stp_relay_wakeup_timer; + _stp_relay_data.timer.data = 0; + add_timer(&_stp_relay_data.timer); + smp_mb(); +} + +static void stp_relay_set_overwrite(int overwrite) +{ + _stp_relay_data.overwrite_flag = overwrite; +} + +static int __stp_relay_dropped_open(struct inode *inode, struct file *filp) +{ + return 0; +} + +static ssize_t __stp_relay_dropped_read(struct file *filp, char __user *buffer, + size_t count, loff_t *ppos) +{ + char buf[16]; + + snprintf(buf, sizeof(buf), "%u\n", + atomic_read(&_stp_relay_data.dropped)); + + return simple_read_from_buffer(buffer, count, ppos, buf, strlen(buf)); +} + +static struct file_operations __stp_relay_dropped_fops = { + .owner = THIS_MODULE, + .open = __stp_relay_dropped_open, + .read = __stp_relay_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 __stp_relay_subbuf_start_callback(struct rchan_buf *buf, + void *subbuf, void *prev_subbuf, + size_t prev_padding) +{ + if (_stp_relay_data.overwrite_flag || !relay_buf_full(buf)) + return 1; + + atomic_inc(&_stp_relay_data.dropped); + return 0; +} + +static int __stp_relay_remove_buf_file_callback(struct dentry *dentry) +{ + debugfs_remove(dentry); + return 0; +} + +static struct dentry * +__stp_relay_create_buf_file_callback(const char *filename, + struct dentry *parent, + int mode, + struct rchan_buf *buf, + int *is_global) +{ + struct dentry *file = debugfs_create_file(filename, mode, parent, buf, + &relay_file_operations); + if (IS_ERR(file)) { + file = NULL; + } + else if (file) { + file->d_inode->i_uid = _stp_uid; + file->d_inode->i_gid = _stp_gid; + } + return file; +} + +static struct rchan_callbacks __stp_relay_callbacks = { + .subbuf_start = __stp_relay_subbuf_start_callback, + .create_buf_file = __stp_relay_create_buf_file_callback, + .remove_buf_file = __stp_relay_remove_buf_file_callback, +}; + +static void _stp_transport_data_fs_start(void) +{ + if (_stp_relay_data.transport_state == STP_TRANSPORT_INITIALIZED) { + /* We're initialized. Now start the timer. */ + __stp_relay_timer_init(); + _stp_relay_data.transport_state = STP_TRANSPORT_RUNNING; + } +} + +static void _stp_transport_data_fs_stop(void) +{ + if (_stp_relay_data.transport_state == STP_TRANSPORT_RUNNING) { + del_timer_sync(&_stp_relay_data.timer); + dbug_trans(0, "flushing...\n"); + _stp_relay_data.transport_state = STP_TRANSPORT_STOPPED; + if (_stp_relay_data.rchan) + relay_flush(_stp_relay_data.rchan); + } +} + +static void _stp_transport_data_fs_close(void) +{ + _stp_transport_data_fs_stop(); + if (_stp_relay_data.dropped_file) + debugfs_remove(_stp_relay_data.dropped_file); + if (_stp_relay_data.rchan) { + relay_close(_stp_relay_data.rchan); + _stp_relay_data.rchan = NULL; + } +} + +static int _stp_transport_data_fs_init(void) +{ + int rc; + u64 npages; + struct sysinfo si; + + _stp_relay_data.transport_state = STP_TRANSPORT_STOPPED; + _stp_relay_data.overwrite_flag = 0; + atomic_set(&_stp_relay_data.dropped, 0); + _stp_relay_data.dropped_file = NULL; + _stp_relay_data.rchan = NULL; + + /* Create "dropped" file. */ + _stp_relay_data.dropped_file + = debugfs_create_file("dropped", 0444, _stp_get_module_dir(), + NULL, &__stp_relay_dropped_fops); + if (!_stp_relay_data.dropped_file) { + rc = -EIO; + goto err; + } + else if (IS_ERR(_stp_relay_data.dropped_file)) { + rc = PTR_ERR(_stp_relay_data.dropped_file); + _stp_relay_data.dropped_file = NULL; + goto err; + } + + _stp_relay_data.dropped_file->d_inode->i_uid = _stp_uid; + _stp_relay_data.dropped_file->d_inode->i_gid = _stp_gid; + + /* Create "trace" file. */ + npages = _stp_subbuf_size * _stp_nsubbufs; +#ifdef STP_BULKMODE + npages *= num_possible_cpus(); +#endif + npages >>= PAGE_SHIFT; + si_meminfo(&si); +#define MB(i) (unsigned long)((i) >> (20 - PAGE_SHIFT)) + if (npages > (si.freeram + si.bufferram)) { + errk("Not enough free+buffered memory(%luMB) for log buffer(%luMB)\n", + MB(si.freeram + si.bufferram), + MB(npages)); + rc = -ENOMEM; + goto err; + } + else if (npages > si.freeram) { + /* exceeds freeram, but below freeram+bufferram */ + printk(KERN_WARNING + "log buffer size exceeds free memory(%luMB)\n", + MB(si.freeram)); + } +#if (RELAYFS_CHANNEL_VERSION >= 7) + _stp_relay_data.rchan = relay_open("trace", _stp_get_module_dir(), + _stp_subbuf_size, _stp_nsubbufs, + &__stp_relay_callbacks, NULL); +#else /* (RELAYFS_CHANNEL_VERSION < 7) */ + _stp_relay_data.rchan = relay_open("trace", _stp_get_module_dir(), + _stp_subbuf_size, _stp_nsubbufs, + &__stp_relay_callbacks); +#endif /* (RELAYFS_CHANNEL_VERSION < 7) */ + if (!_stp_relay_data.rchan) { + rc = -ENOENT; + goto err; + } + dbug_trans(1, "returning 0...\n"); + _stp_relay_data.transport_state = STP_TRANSPORT_INITIALIZED; + + return 0; + +err: + _stp_transport_data_fs_close(); + return rc; +} + + +/** + * _stp_data_write_reserve - try to reserve size_request bytes + * @size_request: number of bytes to attempt to reserve + * @entry: entry is returned here + * + * Returns number of bytes reserved, 0 if full. On return, entry + * will point to allocated opaque pointer. Use + * _stp_data_entry_data() to get pointer to copy data into. + * + * (For this code's purposes, entry is filled in with the actual + * data pointer, but the caller doesn't know that.) + */ +static size_t +_stp_data_write_reserve(size_t size_request, void **entry) +{ + struct rchan_buf *buf; + + if (entry == NULL) + return -EINVAL; + + buf = _stp_relay_data.rchan->buf[smp_processor_id()]; + if (unlikely(buf->offset + size_request > buf->chan->subbuf_size)) { + size_request = __stp_relay_switch_subbuf(buf, size_request); + if (!size_request) + return 0; + } + *entry = (char*)buf->data + buf->offset; + buf->offset += size_request; + + return size_request; +} + +static unsigned char *_stp_data_entry_data(void *entry) +{ + /* Nothing to do here. */ + return entry; +} + +static int _stp_data_write_commit(void *entry) +{ + /* Nothing to do here. */ + return 0; +} diff --git a/runtime/transport/relayfs.c b/runtime/transport/relayfs.c index 6eefda8d..191b932f 100644 --- a/runtime/transport/relayfs.c +++ b/runtime/transport/relayfs.c @@ -22,17 +22,33 @@ #include <linux/init.h> #include <linux/relayfs_fs.h> #include <linux/namei.h> -#include "utt.h" -static int _stp_relay_flushing = 0; +enum _stp_transport_state { + STP_TRANSPORT_STOPPED, + STP_TRANSPORT_INITIALIZED, + STP_TRANSPORT_RUNNING, +}; + +struct _stp_relay_data_type { + enum _stp_transport_state transport_state; + struct rchan *rchan; + int flushing; +}; +struct _stp_relay_data_type _stp_relay_data; + +/* We need to include procfs.c here so that it can see the + * _stp_relay_data_type definition. */ +#include "procfs.c" /** - * _stp_subbuf_start - subbuf_start() relayfs callback implementation + * __stp_relay_subbuf_start_callback - subbuf_start() relayfs + * callback implementation */ -static int _stp_subbuf_start(struct rchan_buf *buf, - void *subbuf, - unsigned prev_subbuf_idx, - void *prev_subbuf) +static int +__stp_relay_subbuf_start_callback(struct rchan_buf *buf, + void *subbuf, + unsigned prev_subbuf_idx, + void *prev_subbuf) { unsigned padding = buf->padding[prev_subbuf_idx]; if (prev_subbuf) @@ -42,11 +58,12 @@ static int _stp_subbuf_start(struct rchan_buf *buf, } /** - * _stp_buf_full - buf_full() relayfs callback implementation + * __stp_relay_buf_full_callback - buf_full() relayfs callback + * implementation */ -static void _stp_buf_full(struct rchan_buf *buf, - unsigned subbuf_idx, - void *subbuf) +static void __stp_relay_buf_full_callback(struct rchan_buf *buf, + unsigned subbuf_idx, + void *subbuf) { unsigned padding = buf->padding[subbuf_idx]; *((unsigned *)subbuf) = padding; @@ -54,126 +71,108 @@ static void _stp_buf_full(struct rchan_buf *buf, static struct rchan_callbacks stp_rchan_callbacks = { - .subbuf_start = _stp_subbuf_start, - .buf_full = _stp_buf_full, + .subbuf_start = __stp_relay_subbuf_start_callback, + .buf_full = __stp_relay_buf_full_callback, }; +static void _stp_transport_data_fs_start(void) +{ + if (_stp_relay_data.transport_state == STP_TRANSPORT_INITIALIZED) + _stp_relay_data.transport_state = STP_TRANSPORT_RUNNING; +} -static void _stp_remove_relay_dir(struct dentry *dir) +static void _stp_transport_data_fs_stop(void) { - if (dir) - relayfs_remove_dir(dir); + if (_stp_relay_data.transport_state == STP_TRANSPORT_RUNNING) { + _stp_relay_data.transport_state = STP_TRANSPORT_STOPPED; + _stp_relay_data.flushing = 1; + if (_stp_relay_data.rchan) + relay_flush(_stp_relay_data.rchan); + } } -static void _stp_remove_relay_root(struct dentry *root) +static void _stp_transport_data_fs_close(void) { - if (root) { - if (!_stp_lock_transport_dir()) { - errk("Unable to lock transport directory.\n"); - return; - } - _stp_remove_relay_dir(root); - _stp_unlock_transport_dir(); + _stp_transport_data_fs_stop(); + if (_stp_relay_data.rchan) { + relay_close(_stp_relay_data.rchan); + _stp_relay_data.rchan = NULL; } } -static struct utt_trace *utt_trace_setup(struct utt_trace_setup *utts) +static int _stp_transport_data_fs_init(void) { - struct utt_trace *utt; + int rc = 0; int i; - utt = _stp_kzalloc(sizeof(*utt)); - if (!utt) - return NULL; - - utt->utt_tree_root = _stp_get_root_dir(utts->root); - if (!utt->utt_tree_root) - goto err; - - utt->dir = relayfs_create_dir(utts->name, utt->utt_tree_root); - if (!utt->dir) - goto err; - - dbug_trans(1, "relay_open %d %d\n", utts->buf_size, utts->buf_nr); + dbug_trans(1, "relay_open %d %d\n", _stp_subbuf_size, _stp_nsubbufs); + _stp_relay_data.transport_state = STP_TRANSPORT_STOPPED; + _stp_relay_data.flushing = 0; - utt->rchan = relay_open("trace", utt->dir, utts->buf_size, - utts->buf_nr, 0, &stp_rchan_callbacks); - if (!utt->rchan) + /* Create "trace" file. */ + _stp_relay_data.rchan = relay_open("trace", _stp_get_module_dir(), + _stp_subbuf_size, _stp_nsubbufs, + 0, &stp_rchan_callbacks); + if (!_stp_relay_data.rchan) { + rc = -ENOENT; goto err; + } /* now set ownership */ for_each_online_cpu(i) { - utt->rchan->buf[i]->dentry->d_inode->i_uid = _stp_uid; - utt->rchan->buf[i]->dentry->d_inode->i_gid = _stp_gid; + _stp_relay_data.rchan->buf[i]->dentry->d_inode->i_uid + = _stp_uid; + _stp_relay_data.rchan->buf[i]->dentry->d_inode->i_gid + = _stp_gid; } - utt->rchan->private_data = utt; - utt->trace_state = Utt_trace_setup; - utts->err = 0; - return utt; + /* We're initialized. */ + _stp_relay_data.transport_state = STP_TRANSPORT_INITIALIZED; + return rc; err: errk("couldn't create relay channel.\n"); - if (utt->dir) - _stp_remove_relay_dir(utt->dir); - if (utt->utt_tree_root) - _stp_remove_relay_root(utt->utt_tree_root); - _stp_kfree(utt); - return NULL; + _stp_transport_data_fs_close(); + return rc; } -static void utt_set_overwrite(int overwrite) +static void stp_relay_set_overwrite(int overwrite) { - if (_stp_utt) - _stp_utt->rchan->overwrite = overwrite; + _stp_relay_data.rchan->overwrite = overwrite; } -static int utt_trace_startstop(struct utt_trace *utt, int start, - unsigned int *trace_seq) +/** + * _stp_data_write_reserve - try to reserve size_request bytes + * @size_request: number of bytes to attempt to reserve + * @entry: entry is returned here + * + * Returns number of bytes reserved, 0 if full. On return, entry + * will point to allocated opaque pointer. Use + * _stp_data_entry_data() to get pointer to copy data into. + * + * (For this code's purposes, entry is filled in with the actual + * data pointer, but the caller doesn't know that.) + */ +static size_t +_stp_data_write_reserve(size_t size_request, void **entry) { - int ret; + if (entry == NULL) + return -EINVAL; - if (!utt) + *entry = relay_reserve(_stp_relay_data.rchan, size_request); + if (*entry == NULL) return 0; - - /* - * 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; - } - } - - return ret; + return size_request; } +static unsigned char *_stp_data_entry_data(void *entry) +{ + /* Nothing to do here. */ + return entry; +} -static int utt_trace_remove(struct utt_trace *utt) +static int _stp_data_write_commit(void *entry) { - dbug_trans(1, "removing relayfs files. %d\n", utt->trace_state); - if (utt && (utt->trace_state == Utt_trace_setup || utt->trace_state == Utt_trace_stopped)) { - if (utt->rchan) - relay_close(utt->rchan); - if (utt->dir) - _stp_remove_relay_dir(utt->dir); - if (utt->utt_tree_root) - _stp_remove_relay_root(utt->utt_tree_root); - _stp_kfree(utt); - } + /* Nothing to do here. */ return 0; } diff --git a/runtime/transport/ring_buffer.c b/runtime/transport/ring_buffer.c new file mode 100644 index 00000000..bed441e5 --- /dev/null +++ b/runtime/transport/ring_buffer.c @@ -0,0 +1,450 @@ +#include <linux/types.h> +#include <linux/ring_buffer.h> +#include <linux/wait.h> +#include <linux/poll.h> +#include <linux/cpumask.h> + +struct _stp_data_entry { + size_t len; + unsigned char buf[]; +}; + +static struct ring_buffer *__stp_ring_buffer = NULL; + +/* _stp_poll_wait is a waitqueue for tasks blocked on + * _stp_data_poll_trace() */ +static DECLARE_WAIT_QUEUE_HEAD(_stp_poll_wait); + +/* + * Trace iterator - used by printout routines who present trace + * results to users and which routines might sleep, etc: + */ +struct _stp_ring_buffer_data { + int cpu; + u64 ts; +}; +static struct _stp_ring_buffer_data _stp_rb_data; + +static cpumask_var_t _stp_trace_reader_cpumask; + +static void __stp_free_ring_buffer(void) +{ + free_cpumask_var(_stp_trace_reader_cpumask); + if (__stp_ring_buffer) + ring_buffer_free(__stp_ring_buffer); + __stp_ring_buffer = NULL; +} + +static int __stp_alloc_ring_buffer(void) +{ + int i; + unsigned long buffer_size = _stp_bufsize; + + if (!alloc_cpumask_var(&_stp_trace_reader_cpumask, GFP_KERNEL)) + goto fail; + cpumask_clear(_stp_trace_reader_cpumask); + + if (buffer_size == 0) { + dbug_trans(1, "using default buffer size...\n"); + buffer_size = _stp_nsubbufs * _stp_subbuf_size; + } + /* The number passed to ring_buffer_alloc() is per cpu. Our + * 'buffer_size' is a total number of bytes to allocate. So, + * we need to divide buffer_size by the number of cpus. */ + buffer_size /= num_online_cpus(); + dbug_trans(1, "%lu\n", buffer_size); + __stp_ring_buffer = ring_buffer_alloc(buffer_size, 0); + if (!__stp_ring_buffer) + goto fail; + + dbug_trans(1, "size = %lu\n", ring_buffer_size(__stp_ring_buffer)); + return 0; + +fail: + __stp_free_ring_buffer(); + return -ENOMEM; +} + +static int _stp_data_open_trace(struct inode *inode, struct file *file) +{ + long cpu_file = (long) inode->i_private; + + /* We only allow for one reader per cpu */ + dbug_trans(1, "trace attach\n"); +#ifdef STP_BULKMODE + if (!cpumask_test_cpu(cpu_file, _stp_trace_reader_cpumask)) + cpumask_set_cpu(cpu_file, _stp_trace_reader_cpumask); + else { + dbug_trans(1, "returning EBUSY\n"); + return -EBUSY; + } +#else + if (!cpumask_empty(_stp_trace_reader_cpumask)) { + dbug_trans(1, "returning EBUSY\n"); + return -EBUSY; + } + cpumask_setall(_stp_trace_reader_cpumask); +#endif + file->private_data = inode->i_private; + return 0; +} + +static int _stp_data_release_trace(struct inode *inode, struct file *file) +{ + long cpu_file = (long) inode->i_private; + dbug_trans(1, "trace detach\n"); +#ifdef STP_BULKMODE + cpumask_clear_cpu(cpu_file, _stp_trace_reader_cpumask); +#else + cpumask_clear(_stp_trace_reader_cpumask); +#endif + + return 0; +} + +size_t +_stp_event_to_user(struct ring_buffer_event *event, char __user *ubuf, + size_t cnt) +{ + int ret; + struct _stp_data_entry *entry; + + dbug_trans(1, "event(%p), ubuf(%p), cnt(%lu)\n", event, ubuf, cnt); + if (event == NULL || ubuf == NULL) + return -EFAULT; + + entry = (struct _stp_data_entry *)ring_buffer_event_data(event); + if (entry == NULL) + return -EFAULT; + + /* We don't do partial entries - just fail. */ + if (entry->len > cnt) + return -EBUSY; + + if (cnt > entry->len) + cnt = entry->len; + ret = copy_to_user(ubuf, entry->buf, cnt); + if (ret) + return -EFAULT; + + return cnt; +} + +static ssize_t tracing_wait_pipe(struct file *filp) +{ + while (ring_buffer_empty(__stp_ring_buffer)) { + + if ((filp->f_flags & O_NONBLOCK)) { + dbug_trans(1, "returning -EAGAIN\n"); + return -EAGAIN; + } + + /* + * This is a make-shift waitqueue. The reason we don't use + * an actual wait queue is because: + * 1) we only ever have one waiter + * 2) the tracing, traces all functions, we don't want + * the overhead of calling wake_up and friends + * (and tracing them too) + * Anyway, this is really very primitive wakeup. + */ + set_current_state(TASK_INTERRUPTIBLE); + + /* sleep for 100 msecs, and try again. */ + schedule_timeout(HZ/10); + + if (signal_pending(current)) { + dbug_trans(1, "returning -EINTR\n"); + return -EINTR; + } + } + + dbug_trans(1, "returning 1\n"); + return 1; +} + +static struct ring_buffer_event * +peek_next_event(int cpu, u64 *ts) +{ + return ring_buffer_peek(__stp_ring_buffer, cpu, ts); +} + +/* Find the next real event */ +static struct ring_buffer_event * +_stp_find_next_event(long cpu_file) +{ + struct ring_buffer_event *event; + +#ifdef STP_BULKMODE + /* + * If we are in a per_cpu trace file, don't bother by iterating over + * all cpus and peek directly. + */ + if (ring_buffer_empty_cpu(__stp_ring_buffer, (int)cpu_file)) + return NULL; + event = peek_next_event(cpu_file, &_stp_rb_data.ts); + _stp_rb_data.cpu = cpu_file; + + return event; +#else + struct ring_buffer_event *next = NULL; + u64 next_ts = 0, ts; + int next_cpu = -1; + int cpu; + + for_each_possible_cpu(cpu) { + + if (ring_buffer_empty_cpu(__stp_ring_buffer, cpu)) + continue; + + event = peek_next_event(cpu, &ts); + + /* + * Pick the event with the smallest timestamp: + */ + if (event && (!next || ts < next_ts)) { + next = event; + next_cpu = cpu; + next_ts = ts; + } + } + + _stp_rb_data.cpu = next_cpu; + _stp_rb_data.ts = next_ts; + + return next; +#endif +} + + +/* + * Consumer reader. + */ +static ssize_t +_stp_data_read_trace(struct file *filp, char __user *ubuf, + size_t cnt, loff_t *ppos) +{ + ssize_t sret; + struct ring_buffer_event *event; + long cpu_file = (long) filp->private_data; + + dbug_trans(1, "%lu\n", (unsigned long)cnt); + + sret = tracing_wait_pipe(filp); + dbug_trans(1, "tracing_wait_pipe returned %ld\n", sret); + if (sret <= 0) + goto out; + + /* stop when tracing is finished */ + if (ring_buffer_empty(__stp_ring_buffer)) { + sret = 0; + goto out; + } + + if (cnt >= PAGE_SIZE) + cnt = PAGE_SIZE - 1; + + dbug_trans(1, "sret = %lu\n", (unsigned long)sret); + sret = 0; + while ((event = _stp_find_next_event(cpu_file)) != NULL) { + ssize_t len; + + len = _stp_event_to_user(event, ubuf, cnt); + if (len <= 0) + break; + + ring_buffer_consume(__stp_ring_buffer, _stp_rb_data.cpu, + &_stp_rb_data.ts); + ubuf += len; + cnt -= len; + sret += len; + if (cnt <= 0) + break; + } +out: + return sret; +} + + +static unsigned int +_stp_data_poll_trace(struct file *filp, poll_table *poll_table) +{ + dbug_trans(1, "entry\n"); + if (! ring_buffer_empty(__stp_ring_buffer)) + return POLLIN | POLLRDNORM; + poll_wait(filp, &_stp_poll_wait, poll_table); + if (! ring_buffer_empty(__stp_ring_buffer)) + return POLLIN | POLLRDNORM; + + dbug_trans(1, "exit\n"); + return 0; +} + +static struct file_operations __stp_data_fops = { + .owner = THIS_MODULE, + .open = _stp_data_open_trace, + .release = _stp_data_release_trace, + .poll = _stp_data_poll_trace, + .read = _stp_data_read_trace, +#if 0 + .splice_read = tracing_splice_read_pipe, +#endif +}; + +/* + * Here's how __STP_MAX_RESERVE_SIZE is figured. The value of + * BUF_PAGE_SIZE was gotten from the kernel's ring_buffer code. It + * is divided by 4, so we waste a maximum of 1/4 of the buffer (in + * the case of a small reservation). + */ +#define __STP_MAX_RESERVE_SIZE ((/*BUF_PAGE_SIZE*/ 4080 / 4) \ + - sizeof(struct _stp_data_entry) \ + - sizeof(struct ring_buffer_event)) + +/* + * This function prepares the cpu buffer to write a sample. + * + * Struct op_entry is used during operations on the ring buffer while + * struct op_sample contains the data that is stored in the ring + * buffer. Struct entry can be uninitialized. The function reserves a + * data array that is specified by size. Use + * op_cpu_buffer_write_commit() after preparing the sample. In case of + * errors a null pointer is returned, otherwise the pointer to the + * sample. + * + */ +static size_t +_stp_data_write_reserve(size_t size_request, void **entry) +{ + struct ring_buffer_event *event; + struct _stp_data_entry *sde; + + if (entry == NULL) + return -EINVAL; + + if (size_request > __STP_MAX_RESERVE_SIZE) { + size_request = __STP_MAX_RESERVE_SIZE; + } + + event = ring_buffer_lock_reserve(__stp_ring_buffer, + sizeof(struct _stp_data_entry) + size_request, + 0); + if (unlikely(! event)) { + dbug_trans(1, "event = NULL (%p)?\n", event); + entry = NULL; + return 0; + } + + sde = (struct _stp_data_entry *)ring_buffer_event_data(event); + sde->len = size_request; + + *entry = event; + return size_request; +} + +static unsigned char *_stp_data_entry_data(void *entry) +{ + struct ring_buffer_event *event = entry; + struct _stp_data_entry *sde; + + if (event == NULL) + return NULL; + + sde = (struct _stp_data_entry *)ring_buffer_event_data(event); + return sde->buf; +} + +static int _stp_data_write_commit(void *entry) +{ + int ret; + struct ring_buffer_event *event = (struct ring_buffer_event *)entry; + + if (unlikely(! entry)) { + dbug_trans(1, "entry = NULL, returning -EINVAL\n"); + return -EINVAL; + } + + ret = ring_buffer_unlock_commit(__stp_ring_buffer, event, 0); + dbug_trans(1, "after commit, empty returns %d\n", + ring_buffer_empty(__stp_ring_buffer)); + + wake_up_interruptible(&_stp_poll_wait); + return ret; +} + + +static struct dentry *__stp_entry[NR_CPUS] = { NULL }; + +static int _stp_transport_data_fs_init(void) +{ + int rc; + long cpu; + + // allocate buffer + dbug_trans(1, "entry...\n"); + rc = __stp_alloc_ring_buffer(); + if (rc != 0) + return rc; + + // create file(s) + for_each_online_cpu(cpu) { + char cpu_file[9]; /* 5(trace) + 3(XXX) + 1(\0) = 9 */ + + if (cpu > 999 || cpu < 0) { + _stp_transport_data_fs_close(); + return -EINVAL; + } + sprintf(cpu_file, "trace%ld", cpu); + __stp_entry[cpu] = debugfs_create_file(cpu_file, 0600, + _stp_get_module_dir(), + (void *)cpu, + &__stp_data_fops); + + if (!__stp_entry[cpu]) { + pr_warning("Could not create debugfs 'trace' entry\n"); + __stp_free_ring_buffer(); + return -ENOENT; + } + else if (IS_ERR(__stp_entry[cpu])) { + rc = PTR_ERR(__stp_entry[cpu]); + pr_warning("Could not create debugfs 'trace' entry\n"); + __stp_free_ring_buffer(); + return rc; + } + + __stp_entry[cpu]->d_inode->i_uid = _stp_uid; + __stp_entry[cpu]->d_inode->i_gid = _stp_gid; + +#ifndef STP_BULKMODE + if (cpu != 0) + break; +#endif + } + + dbug_trans(1, "returning 0...\n"); + return 0; +} + +static void _stp_transport_data_fs_start(void) +{ + /* Do nothing. */ +} + +static void _stp_transport_data_fs_stop(void) +{ + /* Do nothing. */ +} + +static void _stp_transport_data_fs_close(void) +{ + int cpu; + + for_each_possible_cpu(cpu) { + if (__stp_entry[cpu]) + debugfs_remove(__stp_entry[cpu]); + __stp_entry[cpu] = NULL; + } + + __stp_free_ring_buffer(); +} + diff --git a/runtime/transport/transport.c b/runtime/transport/transport.c index 762c0a92..84959f71 100644 --- a/runtime/transport/transport.c +++ b/runtime/transport/transport.c @@ -14,28 +14,55 @@ #ifndef _TRANSPORT_TRANSPORT_C_ #define _TRANSPORT_TRANSPORT_C_ -#include <linux/delay.h> -#include <linux/namei.h> #include "transport.h" -#include "time.c" -#include "../mempool.c" -#include "symbols.c" +#include <linux/debugfs.h> +#include <linux/namei.h> +#include <linux/workqueue.h> +#include <linux/delay.h> + +#if 0 +static void utt_set_overwrite(int overwrite) +{ + return; +} +#endif -static struct utt_trace *_stp_utt = NULL; -static unsigned int utt_seq = 1; -static int _stp_probes_started = 0; -static pid_t _stp_target = 0; static int _stp_exit_flag = 0; + +static uid_t _stp_uid = 0; +static gid_t _stp_gid = 0; +static int _stp_pid = 0; + +static int _stp_ctl_attached = 0; + +static pid_t _stp_target = 0; +static int _stp_probes_started = 0; + +#if 1 +//static struct utt_trace *_stp_utt = NULL; +//static unsigned int utt_seq = 1; #include "control.h" -#ifdef STP_OLD_TRANSPORT +#if STP_TRANSPORT_VERSION == 1 #include "relayfs.c" -#include "procfs.c" -#else -#include "utt.c" +#elif STP_TRANSPORT_VERSION == 2 +#include "relay_v2.c" #include "debugfs.c" +#elif STP_TRANSPORT_VERSION == 3 +#include "ring_buffer.c" +#include "debugfs.c" +#else +#error "Unknown STP_TRANSPORT_VERSION" #endif #include "control.c" +#else /* #if 0 */ +#include "control.h" +#include "debugfs.c" +#include "control.c" +#endif /* if 0 */ +static unsigned _stp_nsubbufs = 8; +static unsigned _stp_subbuf_size = 65536*4; + /* module parameters */ static int _stp_bufsize; module_param(_stp_bufsize, int, 0); @@ -44,7 +71,6 @@ MODULE_PARM_DESC(_stp_bufsize, "buffer size"); /* forward declarations */ static void probe_exit(void); static int probe_start(void); -static void _stp_exit(void); /* check for new workqueue API */ #ifdef DECLARE_DELAYED_WORK @@ -88,14 +114,15 @@ static void _stp_handle_start(struct _stp_msg_start *st) /* when someone does /sbin/rmmod on a loaded systemtap module. */ static void _stp_cleanup_and_exit(int send_exit) { - static int called = 0; - if (!called) { + static int _stp_exit_called = 0; + + if (!_stp_exit_called) { int failures; dbug_trans(1, "cleanup_and_exit (%d)\n", send_exit); _stp_exit_flag = 1; /* we only want to do this stuff once */ - called = 1; + _stp_exit_called = 1; if (_stp_probes_started) { dbug_trans(1, "calling probe_exit\n"); @@ -108,9 +135,8 @@ static void _stp_cleanup_and_exit(int send_exit) if (failures) _stp_warn("There were %d transport failures.\n", failures); - dbug_trans(1, "************** calling startstop 0 *************\n"); - if (_stp_utt) - utt_trace_startstop(_stp_utt, 0, &utt_seq); + dbug_trans(1, "*** calling _stp_transport_data_fs_stop ***\n"); + _stp_transport_data_fs_stop(); dbug_trans(1, "ctl_send STP_EXIT\n"); if (send_exit) @@ -137,11 +163,13 @@ static void _stp_request_exit(void) static void _stp_detach(void) { dbug_trans(1, "detach\n"); - _stp_attached = 0; + _stp_ctl_attached = 0; _stp_pid = 0; +#if 0 if (!_stp_exit_flag) utt_set_overwrite(1); +#endif cancel_delayed_work(&_stp_work); wake_up_interruptible(&_stp_ctl_wq); @@ -153,9 +181,11 @@ static void _stp_detach(void) static void _stp_attach(void) { dbug_trans(1, "attach\n"); - _stp_attached = 1; + _stp_ctl_attached = 1; _stp_pid = current->pid; +#if 0 utt_set_overwrite(0); +#endif queue_delayed_work(_stp_wq, &_stp_work, STP_WORK_TIMER); } @@ -182,7 +212,7 @@ static void _stp_work_queue(void *data) /* if exit flag is set AND we have finished with probe_start() */ if (unlikely(_stp_exit_flag && _stp_probes_started)) _stp_request_exit(); - if (likely(_stp_attached)) + if (likely(_stp_ctl_attached)) queue_delayed_work(_stp_wq, &_stp_work, STP_WORK_TIMER); } @@ -192,19 +222,25 @@ static void _stp_work_queue(void *data) * This is called automatically when the module is unloaded. * */ -static void _stp_transport_close() +static void _stp_transport_close(void) { - dbug_trans(1, "%d: ************** transport_close *************\n", current->pid); + dbug_trans(1, "%d: ************** transport_close *************\n", + current->pid); _stp_cleanup_and_exit(0); destroy_workqueue(_stp_wq); _stp_unregister_ctl_channel(); +#if 0 if (_stp_utt) utt_trace_remove(_stp_utt); +#endif /* #if 0 */ + _stp_transport_fs_close(); _stp_print_cleanup(); /* free print buffers */ _stp_mem_debug_done(); + dbug_trans(1, "---- CLOSED ----\n"); } +#if 0 static struct utt_trace *_stp_utt_open(void) { struct utt_trace_setup utts; @@ -221,6 +257,7 @@ static struct utt_trace *_stp_utt_open(void) return utt_trace_setup(&utts); } +#endif /* #if 0 */ /** * _stp_transport_init() is called from the module initialization. @@ -229,7 +266,6 @@ static struct utt_trace *_stp_utt_open(void) static int _stp_transport_init(void) { dbug_trans(1, "transport_init\n"); - _stp_init_pid = current->pid; #ifdef STAPCONF_TASK_UID _stp_uid = current->uid; _stp_gid = current->gid; @@ -238,6 +274,8 @@ static int _stp_transport_init(void) _stp_gid = current_gid(); #endif +// DRS: is RELAY_GUEST/RELAY_HOST documented? does it work? are there +// test cases? #ifdef RELAY_GUEST /* Guest scripts use relay only for reporting warnings and errors */ _stp_subbuf_size = 65536; @@ -255,12 +293,18 @@ static int _stp_transport_init(void) dbug_trans(1, "Using %d subbufs of size %d\n", _stp_nsubbufs, _stp_subbuf_size); } + if (_stp_transport_fs_init(THIS_MODULE->name) != 0) + goto err0; + +#if 0 #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 +#else /* #if 0 */ +#endif /* #if 0 */ /* create control channel */ if (_stp_register_ctl_channel() < 0) @@ -271,28 +315,35 @@ static int _stp_transport_init(void) goto err2; /* start transport */ - utt_trace_startstop(_stp_utt, 1, &utt_seq); + _stp_transport_data_fs_start(); /* create workqueue of kernel threads */ _stp_wq = create_workqueue("systemtap"); if (!_stp_wq) goto err3; - _stp_transport_state = 1; - /* Signal stapio to send us STP_START back (XXX: ?!?!?!). */ _stp_ctl_send(STP_TRANSPORT, NULL, 0); + dbug_trans(1, "returning 0...\n"); return 0; err3: + dbug_trans(1, "err3\n"); _stp_print_cleanup(); err2: + dbug_trans(1, "err2\n"); _stp_unregister_ctl_channel(); err1: +#if 0 if (_stp_utt) utt_trace_remove(_stp_utt); +#else + dbug_trans(1, "err1\n"); + _stp_transport_fs_close(); +#endif /* #if 0 */ err0: + dbug_trans(1, "err0\n"); return -1; } @@ -319,7 +370,7 @@ static struct dentry *_stp_lockfile = NULL; static int _stp_lock_transport_dir(void) { int numtries = 0; -#ifdef STP_OLD_TRANSPORT +#if STP_TRANSPORT_VERSION == 1 while ((_stp_lockfile = relayfs_create_dir("systemtap_lock", NULL)) == NULL) { #else while ((_stp_lockfile = debugfs_create_dir("systemtap_lock", NULL)) == NULL) { @@ -334,7 +385,7 @@ static int _stp_lock_transport_dir(void) static void _stp_unlock_transport_dir(void) { if (_stp_lockfile) { -#ifdef STP_OLD_TRANSPORT +#if STP_TRANSPORT_VERSION == 1 relayfs_remove_dir(_stp_lockfile); #else debugfs_remove(_stp_lockfile); @@ -343,51 +394,151 @@ static void _stp_unlock_transport_dir(void) } } -/* _stp_get_root_dir(name) - creates root directory 'name' or */ -/* returns a pointer to it if it already exists. Used in */ -/* utt.c and relayfs.c. Will not be necessary if utt is included */ -/* in the kernel. */ +static struct dentry *__stp_root_dir = NULL; -static struct dentry *_stp_get_root_dir(const char *name) +/* _stp_get_root_dir() - creates root directory or returns + * a pointer to it if it already exists. */ + +static struct dentry *_stp_get_root_dir(void) { struct file_system_type *fs; - struct dentry *root; struct super_block *sb; + const char *name = "systemtap"; + + if (__stp_root_dir != NULL) { + return __stp_root_dir; + } -#ifdef STP_OLD_TRANSPORT +#if STP_TRANSPORT_VERSION == 1 fs = get_fs_type("relayfs"); + if (!fs) { + errk("Couldn't find relayfs filesystem.\n"); + return NULL; + } #else fs = get_fs_type("debugfs"); -#endif if (!fs) { - errk("Couldn't find debugfs or relayfs filesystem.\n"); + errk("Couldn't find debugfs filesystem.\n"); return NULL; } +#endif if (!_stp_lock_transport_dir()) { errk("Couldn't lock transport directory.\n"); return NULL; } -#ifdef STP_OLD_TRANSPORT - root = relayfs_create_dir(name, NULL); +#if STP_TRANSPORT_VERSION == 1 + __stp_root_dir = relayfs_create_dir(name, NULL); #else - root = debugfs_create_dir(name, NULL); + __stp_root_dir = debugfs_create_dir(name, NULL); #endif - if (!root) { - /* couldn't create it because it is already there, so find it. */ - sb = list_entry(fs->fs_supers.next, struct super_block, s_instances); + if (!__stp_root_dir) { + /* Couldn't create it because it is already there, so + * find it. */ + sb = list_entry(fs->fs_supers.next, struct super_block, + s_instances); _stp_lock_inode(sb->s_root->d_inode); - root = lookup_one_len(name, sb->s_root, strlen(name)); + __stp_root_dir = lookup_one_len(name, sb->s_root, + strlen(name)); _stp_unlock_inode(sb->s_root->d_inode); - if (!IS_ERR(root)) - dput(root); + if (!IS_ERR(__stp_root_dir)) + dput(__stp_root_dir); else { - root = NULL; + __stp_root_dir = NULL; errk("Could not create or find transport directory.\n"); } } + else if (IS_ERR(__stp_root_dir)) { + __stp_root_dir = NULL; + errk("Could not create root directory \"%s\", error %ld\n", name, + -PTR_ERR(__stp_root_dir)); + } + _stp_unlock_transport_dir(); - return root; + return __stp_root_dir; +} + +static void _stp_remove_root_dir(void) +{ + if (__stp_root_dir) { + if (!_stp_lock_transport_dir()) { + errk("Unable to lock transport directory.\n"); + return; + } + if (simple_empty(__stp_root_dir)) { +#if STP_TRANSPORT_VERSION == 1 + relayfs_remove_dir(__stp_root_dir); +#else + debugfs_remove(__stp_root_dir); +#endif + } + _stp_unlock_transport_dir(); + __stp_root_dir = NULL; + } +} + +static struct dentry *__stp_module_dir = NULL; + +static struct dentry *_stp_get_module_dir(void) +{ + return __stp_module_dir; +} + +static int _stp_transport_fs_init(const char *module_name) +{ + struct dentry *root_dir; + + dbug_trans(1, "entry\n"); + if (module_name == NULL) + return -1; + + root_dir = _stp_get_root_dir(); + if (root_dir == NULL) + return -1; + +#if STP_TRANSPORT_VERSION == 1 + __stp_module_dir = relayfs_create_dir(module_name, root_dir); +#else + __stp_module_dir = debugfs_create_dir(module_name, root_dir); +#endif + if (!__stp_module_dir) { + errk("Could not create module directory \"%s\"\n", + module_name); + _stp_remove_root_dir(); + return -1; + } + else if (IS_ERR(__stp_module_dir)) { + errk("Could not create module directory \"%s\", error %ld\n", + module_name, -PTR_ERR(__stp_module_dir)); + _stp_remove_root_dir(); + return -1; + } + + if (_stp_transport_data_fs_init() != 0) { + _stp_remove_root_dir(); + return -1; + } + dbug_trans(1, "returning 0\n"); + return 0; } +static void _stp_transport_fs_close(void) +{ + dbug_trans(1, "stp_transport_fs_close\n"); + + _stp_transport_data_fs_close(); + + if (__stp_module_dir) { +#if STP_TRANSPORT_VERSION == 1 + relayfs_remove_dir(__stp_module_dir); +#else + debugfs_remove(__stp_module_dir); +#endif + __stp_module_dir = NULL; + } + + _stp_remove_root_dir(); +} + + #endif /* _TRANSPORT_C_ */ diff --git a/runtime/transport/transport.h b/runtime/transport/transport.h index 7d249c45..df2d496d 100644 --- a/runtime/transport/transport.h +++ b/runtime/transport/transport.h @@ -11,9 +11,22 @@ /* amount of data a print can send. */ #define STP_BUFFER_SIZE 8192 +struct utt_trace; + +static int _stp_ctl_write(int type, void *data, unsigned len); + +static int _stp_transport_init(void); +static void _stp_transport_close(void); + +static inline void *utt_reserve(struct utt_trace *utt, size_t length) +{ + return NULL; +} + + /* STP_CTL_BUFFER_SIZE is the maximum size of a message */ /* exchanged on the control channel. */ -#ifdef STP_OLD_TRANSPORT +#if STP_TRANSPORT_VERSION == 1 /* Old transport sends print output on control channel */ #define STP_CTL_BUFFER_SIZE STP_BUFFER_SIZE #else @@ -23,27 +36,94 @@ /* how often the work queue wakes up and checks buffers */ #define STP_WORK_TIMER (HZ/100) -static unsigned _stp_nsubbufs = 8; -static unsigned _stp_subbuf_size = 65536*4; +static unsigned _stp_nsubbufs; +static unsigned _stp_subbuf_size; static int _stp_transport_init(void); static void _stp_transport_close(void); -static void _stp_warn (const char *fmt, ...); -static int _stp_print_init(void); -static void _stp_print_cleanup(void); -static struct dentry *_stp_get_root_dir(const char *name); - static int _stp_lock_transport_dir(void); static void _stp_unlock_transport_dir(void); +static struct dentry *_stp_get_root_dir(void); +static struct dentry *_stp_get_module_dir(void); + +static int _stp_transport_fs_init(const char *module_name); +static void _stp_transport_fs_close(void); + static void _stp_attach(void); static void _stp_detach(void); static void _stp_handle_start(struct _stp_msg_start *st); -static int _stp_pid = 0; -static uid_t _stp_uid = 0; -static gid_t _stp_gid = 0; -static pid_t _stp_init_pid = 0; -static int _stp_attached = 0; +static uid_t _stp_uid; +static gid_t _stp_gid; + +static int _stp_ctl_attached; + +static int _stp_bufsize; + +/* + * All transports must provide the following functions. + */ + +/* + * _stp_transport_data_fs_init + * + * This function allocates any buffers needed, creates files, + * etc. needed for this transport. + */ +static int _stp_transport_data_fs_init(void); + +/* + * _stp_transport_data_fs_start + * + * This function actually starts the transport. + */ +static void _stp_transport_data_fs_start(void); + +/* + * _stp_transport_data_fs_start + * + * This function stops the transport without doing any cleanup. + */ +static void _stp_transport_data_fs_stop(void); + +/* + * _stp_transport_data_fs_close + * + * This function cleans up items created by + * _stp_transport_data_fs_init(). + */ +static void _stp_transport_data_fs_close(void); + +/* + * _stp_data_write_reserve - reserve bytes + * size_request: number of bytes to reserve + * entry: allocated buffer is returned here + * + * This function attempts to reserve size_request number of bytes, + * returning the number of bytes actually reserved. The allocated + * buffer is returned in entry. Note that the number of bytes + * allocated may be less than the number of bytes requested. + */ +static size_t _stp_data_write_reserve(size_t size_request, void **entry); + + +/* + * _stp_data_entry_data - return data pointer from entry + * entry: entry + * + * This function returns the data pointer from entry. + */ +static unsigned char *_stp_data_entry_data(void *entry); + +/* + * _stp_data_write_commit - + * entry: pointer returned by _stp-data_write_reserve() + * + * This function notifies the transport that the bytes in entry are + * ready to be written. + */ +static int _stp_data_write_commit(void *entry); + #endif /* _TRANSPORT_TRANSPORT_H_ */ diff --git a/runtime/transport/transport_msgs.h b/runtime/transport/transport_msgs.h index 0d9a5983..aa50051c 100644 --- a/runtime/transport/transport_msgs.h +++ b/runtime/transport/transport_msgs.h @@ -29,8 +29,8 @@ enum STP_DISCONNECT, STP_BULK, STP_READY, - STP_RELOCATION, - /** deprecated STP_OLD_TRANSPORT **/ + STP_RELOCATION, + /** deprecated STP_TRANSPORT_VERSION == 1 **/ STP_BUF_INFO, STP_SUBBUFS_CONSUMED, STP_REALTIME_DATA, @@ -83,7 +83,7 @@ struct _stp_msg_start int32_t res; // for reply: result of probe_start() }; -#ifdef STP_OLD_TRANSPORT +#if STP_TRANSPORT_VERSION == 1 /**** for compatibility with old relayfs ****/ struct _stp_buf_info { diff --git a/runtime/transport/utt.c b/runtime/transport/utt.c deleted file mode 100644 index 915662b2..00000000 --- a/runtime/transport/utt.c +++ /dev/null @@ -1,406 +0,0 @@ -/* -*- 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 <linux/mm.h> -#include "utt.h" - -static int utt_overwrite_flag = 0; - -/* - * utt_switch_subbuf - switch to a new sub-buffer - * - * Most of this function is deadcopy of relay_switch_subbuf. - */ -static size_t utt_switch_subbuf(struct utt_trace *utt, struct rchan_buf *buf, - size_t length) -{ - char *old, *new; - size_t old_subbuf, new_subbuf; - - if (unlikely(buf == NULL)) - return 0; - - if (unlikely(length > buf->chan->subbuf_size)) - goto toobig; - - if (buf->offset != buf->chan->subbuf_size + 1) { - buf->prev_padding = buf->chan->subbuf_size - buf->offset; - old_subbuf = buf->subbufs_produced % buf->chan->n_subbufs; - buf->padding[old_subbuf] = buf->prev_padding; - buf->subbufs_produced++; - buf->dentry->d_inode->i_size += buf->chan->subbuf_size - - buf->padding[old_subbuf]; - smp_mb(); - if (waitqueue_active(&buf->read_wait)) - /* - * Calling wake_up_interruptible() and __mod_timer() - * from here will deadlock if we happen to be logging - * from the scheduler and timer (trying to re-grab - * rq->lock/timer->base->lock), so just set a flag. - */ - atomic_set(&utt->wakeup, 1); - } - - old = buf->data; - new_subbuf = buf->subbufs_produced % buf->chan->n_subbufs; - new = (char*)buf->start + new_subbuf * buf->chan->subbuf_size; - buf->offset = 0; - if (!buf->chan->cb->subbuf_start(buf, new, old, buf->prev_padding)) { - buf->offset = buf->chan->subbuf_size + 1; - return 0; - } - buf->data = new; - buf->padding[new_subbuf] = 0; - - if (unlikely(length + buf->offset > buf->chan->subbuf_size)) - goto toobig; - - return length; - -toobig: - buf->chan->last_toobig = length; - return 0; -} - -static void __utt_wakeup_readers(struct rchan_buf *buf) -{ - if (buf && waitqueue_active(&buf->read_wait) && - buf->subbufs_produced != buf->subbufs_consumed) - wake_up_interruptible(&buf->read_wait); -} - -static void __utt_wakeup_timer(unsigned long val) -{ - struct utt_trace *utt = (struct utt_trace *)val; - int i; - - if (atomic_read(&utt->wakeup)) { - atomic_set(&utt->wakeup, 0); - if (utt->is_global) - __utt_wakeup_readers(utt->rchan->buf[0]); - else - for_each_possible_cpu(i) - __utt_wakeup_readers(utt->rchan->buf[i]); - } - - mod_timer(&utt->timer, jiffies + UTT_TIMER_INTERVAL); -} - -static void __utt_timer_init(struct utt_trace * utt) -{ - atomic_set(&utt->wakeup, 0); - init_timer(&utt->timer); - utt->timer.expires = jiffies + UTT_TIMER_INTERVAL; - utt->timer.function = __utt_wakeup_timer; - utt->timer.data = (unsigned long)utt; - add_timer(&utt->timer); -} - -static void utt_set_overwrite(int overwrite) -{ - utt_overwrite_flag = overwrite; -} - -static void utt_remove_root(struct utt_trace *utt) -{ - if (utt->utt_tree_root) { - if (!_stp_lock_transport_dir()) { - errk("Unable to lock transport directory.\n"); - return; - } - if (simple_empty(utt->utt_tree_root)) - debugfs_remove(utt->utt_tree_root); - _stp_unlock_transport_dir(); - utt->utt_tree_root = NULL; - } -} - -static void utt_remove_tree(struct utt_trace *utt) -{ - if (utt == NULL || utt->dir == NULL) - return; - debugfs_remove(utt->dir); - utt_remove_root(utt); -} - -static struct dentry *utt_create_tree(struct utt_trace *utt, const char *root, const char *name) -{ - struct dentry *dir = NULL; - - if (root == NULL || name == NULL) - return NULL; - - if (!utt->utt_tree_root) { - utt->utt_tree_root = _stp_get_root_dir(root); - if (!utt->utt_tree_root) - goto err; - } - - dir = debugfs_create_dir(name, utt->utt_tree_root); - if (!dir) - utt_remove_root(utt); -err: - return dir; -} - - -static void utt_trace_cleanup(struct utt_trace *utt) -{ - if (utt == NULL) - return; - if (utt->rchan) - relay_close(utt->rchan); - if (utt->dropped_file) - debugfs_remove(utt->dropped_file); - utt_remove_tree(utt); - _stp_kfree(utt); -} - -static 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) -{ -#ifdef STAPCONF_INODE_PRIVATE - filp->private_data = inode->i_private; -#else - filp->private_data = inode->u.generic_ip; -#endif - 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 (utt_overwrite_flag || !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) -{ - struct dentry *file = debugfs_create_file(filename, mode, parent, buf, - &relay_file_operations); - if (file) { - file->d_inode->i_uid = _stp_uid; - file->d_inode->i_gid = _stp_gid; - } - return file; -} - -static struct dentry *utt_create_global_buf_file_callback(const char *filename, - struct dentry *parent, - int mode, - struct rchan_buf *buf, - int *is_global) -{ - struct dentry *file; - *is_global = 1; - file = debugfs_create_file(filename, mode, parent, buf, - &relay_file_operations); - if (file) { - file->d_inode->i_uid = _stp_uid; - file->d_inode->i_gid = _stp_gid; - } - return file; -} - -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 - */ -static struct utt_trace *utt_trace_setup(struct utt_trace_setup *utts) -{ - struct utt_trace *utt = NULL; - struct dentry *dir = NULL; - int ret = -EINVAL; - u64 npages; - struct sysinfo si; - - if (!utts->buf_size || !utts->buf_nr) - goto err; - - ret = -ENOMEM; - utt = _stp_kzalloc(sizeof(*utt)); - if (!utt) - goto err; - - ret = -ENOENT; - dir = utt_create_tree(utt, utts->root, utts->name); - if (!dir) - goto err; - utt->dir = dir; - 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; - - npages = utts->buf_size * utts->buf_nr; - if (!utts->is_global) - npages *= num_possible_cpus(); - npages >>= PAGE_SHIFT; - si_meminfo(&si); -#define MB(i) (unsigned long)((i) >> (20 - PAGE_SHIFT)) - if (npages > (si.freeram + si.bufferram)) { - errk("Not enough free+buffered memory(%luMB) for log buffer(%luMB)\n", - MB(si.freeram + si.bufferram), - MB(npages)); - ret = -ENOMEM; - goto err; - } else if (npages > si.freeram) { - /* exceeds freeram, but below freeram+bufferram */ - printk(KERN_WARNING - "log buffer size exceeds free memory(%luMB)\n", - MB(si.freeram)); - } - -#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->is_global = utts->is_global; - - 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->rchan) - relay_close(utt->rchan); - _stp_kfree(utt); - } - if (dir) - utt_remove_tree(utt); - utts->err = ret; - return NULL; -} - -static 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)++; - __utt_timer_init(utt); - smp_mb(); - utt->trace_state = Utt_trace_running; - ret = 0; - } - } else { - if (utt->trace_state == Utt_trace_running) { - utt->trace_state = Utt_trace_stopped; - del_timer_sync(&utt->timer); - relay_flush(utt->rchan); - ret = 0; - } - } - - return ret; -} diff --git a/runtime/transport/utt.h b/runtime/transport/utt.h deleted file mode 100644 index 40e54919..00000000 --- a/runtime/transport/utt.h +++ /dev/null @@ -1,78 +0,0 @@ -#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; - struct dentry *dir; /* systemtap/module_name */ - struct dentry *dropped_file; - atomic_t dropped; - struct dentry *utt_tree_root; /* systemtap */ - void *private_data; - atomic_t wakeup; - struct timer_list timer; - int is_global; -}; - -#define UTT_TRACE_ROOT_NAME_SIZE 64 /* Largest string for a root dir identifier */ -#define UTT_TRACE_NAME_SIZE 64 /* Largest string for a trace identifier */ - -#ifndef UTT_TIMER_INTERVAL -#define UTT_TIMER_INTERVAL ((HZ + 99) / 100) /* Wakeup timer interval in jiffies (default 10 ms)*/ -#endif - -/* - * 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 */ -}; - - -static struct utt_trace *utt_trace_setup(struct utt_trace_setup *utts); -static int utt_trace_startstop(struct utt_trace *utt, int start, - unsigned int *trace_seq); -static void utt_trace_cleanup(struct utt_trace *utt); -static int utt_trace_remove(struct utt_trace *utt); - -#ifndef STP_OLD_TRANSPORT -static size_t utt_switch_subbuf(struct utt_trace *utt, struct rchan_buf *buf, - size_t length); -/** - * utt_reserve - reserve slot in channel buffer - * @utt: utt channel - * @length: number of bytes to reserve - * - * Returns pointer to reserved slot, NULL if full. - * - * This function is utt_switch_subbuf version of relay_reserve. - */ -static inline void *utt_reserve(struct utt_trace *utt, size_t length) -{ - void *reserved; - struct rchan_buf *buf = utt->rchan->buf[smp_processor_id()]; - - if (unlikely(buf->offset + length > buf->chan->subbuf_size)) { - length = utt_switch_subbuf(utt, buf, length); - if (!length) - return NULL; - } - reserved = (char*)buf->data + buf->offset; - buf->offset += length; - - return reserved; -} -#endif - -#endif diff --git a/runtime/vsprintf.c b/runtime/vsprintf.c index 23810e75..5875d509 100644 --- a/runtime/vsprintf.c +++ b/runtime/vsprintf.c @@ -12,8 +12,8 @@ #ifndef _VSPRINTF_C_ #define _VSPRINTF_C_ -//forward declaration for _stp_vsnprintf -static void * _stp_reserve_bytes (int); +#include "print.h" +#include "transport/transport.h" static int skip_atoi(const char **s) { diff --git a/testsuite/systemtap.printf/end1.exp b/testsuite/systemtap.printf/end1.exp index 590340d3..ab1de590 100644 --- a/testsuite/systemtap.printf/end1.exp +++ b/testsuite/systemtap.printf/end1.exp @@ -11,15 +11,15 @@ if {[catch {exec mktemp -t staptestXXXXXX} tmpfile]} { } if {[catch {exec stap -o $tmpfile $tpath} res]} { - untested $TEST_NAME - puts "$res" + fail $TEST_NAME + puts "stap failed: $res" catch {exec rm -f $tmpfile} return } if {[catch {exec cmp $tmpfile $srcdir/$subdir/large_output} res]} { - fail $TEST_NAME puts "$res" + fail $TEST_NAME catch {exec rm -f $tmpfile} return } diff --git a/testsuite/systemtap.printf/end1b.exp b/testsuite/systemtap.printf/end1b.exp index bea5736e..46cdc9c7 100644 --- a/testsuite/systemtap.printf/end1b.exp +++ b/testsuite/systemtap.printf/end1b.exp @@ -16,8 +16,8 @@ if {[catch {exec mktemp -t staptestXXXXXX} tmpfile]} { } if {[catch {exec stap -b -o $tmpfile $test} res]} { - untested $TEST_NAME puts "stap failed: $res" + fail $TEST_NAME as_root "/bin/rm -f [glob ${tmpfile}*]" return } diff --git a/testsuite/systemtap.printf/mixed_out.exp b/testsuite/systemtap.printf/mixed_out.exp index 3b66e7c0..55320e80 100644 --- a/testsuite/systemtap.printf/mixed_out.exp +++ b/testsuite/systemtap.printf/mixed_out.exp @@ -11,8 +11,8 @@ if {[catch {exec mktemp -t staptestXXXXXX} tmpfile]} { } if {[catch {exec stap -DMAXACTION=100000 -o $tmpfile $tpath} res]} { - untested $TEST_NAME - puts "$res" + fail $TEST_NAME + puts "stap failed: $res" catch {exec rm -f $tmpfile} return } diff --git a/testsuite/systemtap.printf/mixed_outb.exp b/testsuite/systemtap.printf/mixed_outb.exp index db82cc79..c15520b1 100644 --- a/testsuite/systemtap.printf/mixed_outb.exp +++ b/testsuite/systemtap.printf/mixed_outb.exp @@ -16,7 +16,7 @@ if {[catch {exec mktemp -t staptestXXXXXX} tmpfile]} { } if {[catch {exec stap -DMAXACTION=100000 -b -o $tmpfile $test} res]} { - untested $TEST_NAME + fail $TEST_NAME puts "stap failed: $res" as_root "/bin/rm -f [glob ${tmpfile}*]" return diff --git a/testsuite/systemtap.printf/out1.exp b/testsuite/systemtap.printf/out1.exp index 7577a54d..f973ae00 100644 --- a/testsuite/systemtap.printf/out1.exp +++ b/testsuite/systemtap.printf/out1.exp @@ -11,7 +11,7 @@ if {[catch {exec mktemp -t staptestXXXXXX} tmpfile]} { } if {[catch {exec stap -o $tmpfile $tpath} res]} { - untested $TEST_NAME + fail $TEST_NAME puts "$res" catch {exec rm -f $tmpfile} return diff --git a/testsuite/systemtap.printf/out1b.exp b/testsuite/systemtap.printf/out1b.exp index c3e21ba9..24efbf4c 100644 --- a/testsuite/systemtap.printf/out1b.exp +++ b/testsuite/systemtap.printf/out1b.exp @@ -16,7 +16,7 @@ if {[catch {exec mktemp -t staptestXXXXXX} tmpfile]} { } if {[catch {exec stap -b -o $tmpfile $test} res]} { - untested $TEST_NAME + fail $TEST_NAME puts "stap failed: $res" as_root "/bin/rm -f [glob ${tmpfile}*]" return diff --git a/testsuite/systemtap.printf/out2.exp b/testsuite/systemtap.printf/out2.exp index ce1f7c6e..8c66e73d 100644 --- a/testsuite/systemtap.printf/out2.exp +++ b/testsuite/systemtap.printf/out2.exp @@ -11,7 +11,7 @@ if {[catch {exec mktemp -t staptestXXXXXX} tmpfile]} { } if {[catch {exec stap -o $tmpfile $tpath} res]} { - untested $TEST_NAME + fail $TEST_NAME puts "$res" catch {exec rm -f $tmpfile} return diff --git a/testsuite/systemtap.printf/out2b.exp b/testsuite/systemtap.printf/out2b.exp index fcc12f63..70a98ea2 100644 --- a/testsuite/systemtap.printf/out2b.exp +++ b/testsuite/systemtap.printf/out2b.exp @@ -16,7 +16,7 @@ if {[catch {exec mktemp -t staptestXXXXXX} tmpfile]} { } if {[catch {exec stap -b -o $tmpfile $test} res]} { - untested $TEST_NAME + fail $TEST_NAME puts "stap failed: $res" as_root "/bin/rm -f [glob ${tmpfile}*]" return diff --git a/testsuite/systemtap.printf/out3.exp b/testsuite/systemtap.printf/out3.exp index 51124757..63a67d8f 100644 --- a/testsuite/systemtap.printf/out3.exp +++ b/testsuite/systemtap.printf/out3.exp @@ -11,7 +11,7 @@ if {[catch {exec mktemp -t staptestXXXXXX} tmpfile]} { } if {[catch {exec stap -DMAXACTION=100000 -o $tmpfile $tpath} res]} { - untested $TEST_NAME + fail $TEST_NAME puts "$res" catch {exec rm -f $tmpfile} return diff --git a/testsuite/systemtap.printf/out3b.exp b/testsuite/systemtap.printf/out3b.exp index 740a8b68..d49625e9 100644 --- a/testsuite/systemtap.printf/out3b.exp +++ b/testsuite/systemtap.printf/out3b.exp @@ -16,7 +16,7 @@ if {[catch {exec mktemp -t staptestXXXXXX} tmpfile]} { } if {[catch {exec stap -DMAXACTION=100000 -b -o $tmpfile $test} res]} { - untested $TEST_NAME + fail $TEST_NAME puts "stap failed: $res" as_root "/bin/rm -f [glob ${tmpfile}*]" return |