diff options
author | hunt <hunt> | 2007-03-14 16:12:40 +0000 |
---|---|---|
committer | hunt <hunt> | 2007-03-14 16:12:40 +0000 |
commit | fbbb89fffb3533a3b4fb63c15eaeaae6956391eb (patch) | |
tree | 6e3420267ef6917d71667070dd5479a569591b13 /runtime/transport/control.c | |
parent | a846e9cd2b991b2b6f895b45f0a2af1ce2c994bc (diff) | |
download | systemtap-steved-fbbb89fffb3533a3b4fb63c15eaeaae6956391eb.tar.gz systemtap-steved-fbbb89fffb3533a3b4fb63c15eaeaae6956391eb.tar.xz systemtap-steved-fbbb89fffb3533a3b4fb63c15eaeaae6956391eb.zip |
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.
Diffstat (limited to 'runtime/transport/control.c')
-rw-r--r-- | runtime/transport/control.c | 275 |
1 files changed, 275 insertions, 0 deletions
diff --git a/runtime/transport/control.c b/runtime/transport/control.c new file mode 100644 index 00000000..3ad3e309 --- /dev/null +++ b/runtime/transport/control.c @@ -0,0 +1,275 @@ +/* -*- 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); + } +} + |