[<prev] [next>] [thread-next>] [day] [month] [year] [list]
Message-Id: <1160557065.9547.12.camel@localhost.localdomain>
Date: Wed, 11 Oct 2006 10:57:45 +0200
From: Paolo Abeni <paolo.abeni@...il.it>
To: gregkh@...e.de
Cc: linux-kernel@...r.kernel.org, linux-usb-devel@...ts.sourceforge.net
Subject: [PATCH] usbmon: add binary interface
From: Paolo Abeni <paolo.abeni@...il.it>
A binary interface is added to usbmon. For each USB bus present on the host system a new file is added to the debugfs directory, in the form "usb%db".
USB records are stored in a liked list, alike current text interface implementation, so most code is shared from binary and text interface.
This code has been moved into mon_commom.c.
The binary interface support resizing the amount of USB data stored in each record via an ioctl method.
Signed-off-by: Paolo Abeni <paolo.abeni@...il.it>
---
patch against linux 2.6.18
Makefile | 2
mon_binary.c | 203 ++++++++++++++++++++++++++++++++++++
mon_common.c | 329 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
mon_main.c | 13 ++
mon_stat.c | 4
mon_text.c | 319 +++++++--------------------------------------------------
usb_mon.h | 46 ++++++++
diff -uprN -X linux-2.6.18-vanilla/Documentation/dontdiff linux-2.6.18-vanilla/drivers/usb/mon/Makefile linux-2.6.18-monioctl/drivers/usb/mon/Makefile
--- linux-2.6.18-vanilla/drivers/usb/mon/Makefile 2006-09-20 05:42:06.000000000 +0200
+++ linux-2.6.18-monioctl/drivers/usb/mon/Makefile 2006-10-10 10:18:25.000000000 +0200
@@ -2,7 +2,7 @@
# Makefile for USB Core files and filesystem
#
-usbmon-objs := mon_main.o mon_stat.o mon_text.o mon_dma.o
+usbmon-objs := mon_main.o mon_stat.o mon_text.o mon_dma.o mon_common.o mon_binary.o
# This does not use CONFIG_USB_MON because we want this to use a tristate.
obj-$(CONFIG_USB) += usbmon.o
diff -uprN -X linux-2.6.18-vanilla/Documentation/dontdiff linux-2.6.18-vanilla/drivers/usb/mon/mon_binary.c linux-2.6.18-monioctl/drivers/usb/mon/mon_binary.c
--- linux-2.6.18-vanilla/drivers/usb/mon/mon_binary.c 1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.18-monioctl/drivers/usb/mon/mon_binary.c 2006-10-11 10:52:16.000000000 +0200
@@ -0,0 +1,203 @@
+/*
+ * The USB Monitor, inspired by Dave Harding's USBMon.
+ *
+ * This is a binary format reader.
+ */
+
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/usb.h>
+#include <linux/time.h>
+#include <linux/mutex.h>
+#include <linux/ioctl.h>
+#include <asm/uaccess.h>
+
+#include "usb_mon.h"
+
+/* ioctl macros */
+#define MON_IOC_MAGIC 0x92
+
+#define MON_IOCT_DATA_MAX _IO(MON_IOC_MAGIC, 1)
+#define MON_IOCG_DATA_MAX _IOR(MON_IOC_MAGIC, 2, u32)
+
+#define MON_IOC_MAXNR 2
+
+/*
+ */
+static int mon_binary_open(struct inode *inode, struct file *file)
+{
+ struct mon_bus *mbus;
+ struct mon_reader_list *rp;
+ int rc;
+
+ mutex_lock(&mon_lock);
+ mbus = inode->u.generic_ip;
+
+ rp = kzalloc(sizeof(struct mon_reader_list), GFP_KERNEL);
+ if (rp == NULL) {
+ rc = -ENOMEM;
+ goto err_alloc;
+ }
+
+ if ((rc = mon_list_init(rp, mbus, 'b')) < 0)
+ goto err_alloc_pr;
+
+ mon_reader_add(mbus, &rp->r);
+
+ file->private_data = rp;
+ mutex_unlock(&mon_lock);
+ return 0;
+
+err_alloc_pr:
+ kfree(rp);
+err_alloc:
+ mutex_unlock(&mon_lock);
+ return rc;
+}
+
+/*
+ * For simplicity, we read one record in one system call and throw out
+ * what does not fit. This means that the following does not work:
+ * dd if=/dbg/usbmon/0t bs=10
+ * Also, we do not allow seeks and do not bother advancing the offset.
+ */
+static ssize_t mon_binary_read(struct file *file, char __user *buf,
+ size_t nbytes, loff_t *ppos)
+{
+ struct mon_reader_list *rp = file->private_data;
+ struct mon_bus *mbus = rp->r.m_bus;
+ DECLARE_WAITQUEUE(waita, current);
+ struct mon_event_list *ep;
+ int cnt, hdr_bytes, data_bytes, setup_bytes;
+ int data_len = 0;
+ int setup_len = 0;
+
+ add_wait_queue(&rp->wait, &waita);
+ set_current_state(TASK_INTERRUPTIBLE);
+ while ((ep = mon_list_fetch(rp, mbus)) == NULL) {
+ if (file->f_flags & O_NONBLOCK) {
+ set_current_state(TASK_RUNNING);
+ remove_wait_queue(&rp->wait, &waita);
+ return -EWOULDBLOCK; /* Same as EAGAIN in Linux */
+ }
+ /*
+ * We do not count nwaiters, because ->release is supposed
+ * to be called when all openers are gone only.
+ */
+ schedule();
+ if (signal_pending(current)) {
+ remove_wait_queue(&rp->wait, &waita);
+ return -EINTR;
+ }
+ set_current_state(TASK_INTERRUPTIBLE);
+ }
+ set_current_state(TASK_RUNNING);
+ remove_wait_queue(&rp->wait, &waita);
+
+ /* check out how much URB data is present in this buffer*/
+ if ((ep->hdr.length > 0) && (ep->hdr.data_flag == 0))
+ data_len = (ep->hdr.length > rp->data_max) ? rp->data_max:
+ ep->hdr.length;
+
+ /* avoid overrun user buffer; copy as much data as possible*/
+ hdr_bytes = sizeof(struct mon_event_list) > nbytes ? nbytes:
+ sizeof(struct mon_event_list);
+ nbytes -= hdr_bytes;
+ if (ep->hdr.setup_flag == 0)
+ setup_len = SETUP_MAX;
+ setup_bytes = setup_len > nbytes ? nbytes : setup_len;
+ nbytes -= setup_bytes;
+ data_bytes = data_len > nbytes ? nbytes: data_len;
+
+ cnt = hdr_bytes + setup_bytes + data_bytes;
+ if (copy_to_user(buf, ep, hdr_bytes))
+ {
+ cnt = -EFAULT;
+ goto out;
+ }
+ if ((setup_bytes > 0) && copy_to_user(buf+hdr_bytes, ep->setup,
+ setup_bytes))
+ {
+ cnt = -EFAULT;
+ goto out;
+ }
+ if ((data_bytes > 0) && copy_to_user(buf+hdr_bytes, ep->data,
+ data_bytes))
+ cnt = -EFAULT;
+out:
+ kmem_cache_free(rp->e_slab, ep);
+ return cnt;
+}
+
+static int mon_binary_release(struct inode *inode, struct file *file)
+{
+ struct mon_reader_list *rp = file->private_data;
+ struct mon_bus *mbus;
+ /* unsigned long flags; */
+
+ mutex_lock(&mon_lock);
+ mbus = inode->u.generic_ip;
+
+ if (mbus->nreaders <= 0) {
+ printk(KERN_ERR TAG ": consistency error on close\n");
+ mutex_unlock(&mon_lock);
+ return 0;
+ }
+ mon_reader_del(mbus, &rp->r);
+
+ mon_list_destroy(rp);
+ kfree(rp);
+
+ mutex_unlock(&mon_lock);
+ return 0;
+}
+
+static int mon_binary_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ unsigned new_data_max;
+ struct mon_reader_list *rp;
+ int ret = 0;
+
+ /* basic sanity check */
+ if (!(rp = file->private_data))
+ return -ENODEV;
+ if (_IOC_TYPE(cmd) != MON_IOC_MAGIC)
+ return -ENOTTY;
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+
+ switch (cmd) {
+ case MON_IOCG_DATA_MAX:
+ if (put_user(rp->data_max, (u32 __user *)arg))
+ ret = -EFAULT;
+ break;
+
+ case MON_IOCT_DATA_MAX:
+ new_data_max = arg;
+ if (new_data_max == rp->data_max)
+ break;
+
+ /* try to allocate new buffer before relasing old one
+ * to be safe*/
+ ret = mon_list_resize(rp, new_data_max);
+ break;
+
+ default:
+ ret = -ENOTTY;
+ break;
+ }
+
+ return ret;
+}
+
+struct file_operations mon_fops_binary = {
+ .owner = THIS_MODULE,
+ .open = mon_binary_open,
+ .llseek = no_llseek,
+ .read = mon_binary_read,
+ /* .write = mon_text_write, */
+ /* .poll = mon_text_poll, */
+ .ioctl = mon_binary_ioctl,
+ .release = mon_binary_release,
+};
diff -uprN -X linux-2.6.18-vanilla/Documentation/dontdiff linux-2.6.18-vanilla/drivers/usb/mon/mon_common.c linux-2.6.18-monioctl/drivers/usb/mon/mon_common.c
--- linux-2.6.18-vanilla/drivers/usb/mon/mon_common.c 1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.18-monioctl/drivers/usb/mon/mon_common.c 2006-10-10 22:04:23.000000000 +0200
@@ -0,0 +1,329 @@
+/*
+ * The USB Monitor, inspired by Dave Harding's USBMon.
+ *
+ * This are common function used by both text and binary reader
+ */
+
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/usb.h>
+#include <linux/time.h>
+#include <linux/mutex.h>
+#include <asm/uaccess.h>
+
+#include "usb_mon.h"
+
+/*
+ * This limit can be changed using ioctl
+ */
+#define DATA_DFL 32
+#define DATA_MIN 16
+#define DATA_MAX 1024
+
+/*
+ * This limit exists to prevent OOMs when the user process stops reading.
+ * If usbmon were available to unprivileged processes, it might be open
+ * to a local DoS. But we have to keep to root in order to prevent
+ * password sniffing from HID devices.
+ */
+#define EVENT_MAX (2*PAGE_SIZE / sizeof(struct mon_event_list))
+
+static void mon_list_ctor(void *, kmem_cache_t *, unsigned long);
+
+/*
+ * mon_list_submit
+ * mon_list_complete
+ *
+ * May be called from an interrupt.
+ *
+ * This is called with the whole mon_bus locked, so no additional lock.
+ */
+
+static inline char mon_list_get_setup(struct mon_event_list *ep,
+ struct urb *urb, char ev_type)
+{
+
+ if (!usb_pipecontrol(urb->pipe) || ev_type != 'S')
+ return '-';
+
+ if (urb->transfer_flags & URB_NO_SETUP_DMA_MAP)
+ return mon_dmapeek(ep->setup, urb->setup_dma, SETUP_MAX);
+ if (urb->setup_packet == NULL)
+ return 'Z'; /* '0' would be not as pretty. */
+
+ memcpy(ep->setup, urb->setup_packet, SETUP_MAX);
+ return 0;
+}
+
+static inline char mon_list_get_data(struct mon_event_list *ep, struct urb *urb,
+ int len, char ev_type, int data_max)
+{
+ int pipe = urb->pipe;
+
+ if (len <= 0)
+ return 'L';
+ if (len >= data_max)
+ len = data_max;
+
+ if (usb_pipein(pipe)) {
+ if (ev_type == 'S')
+ return '<';
+ } else {
+ if (ev_type == 'C')
+ return '>';
+ }
+
+ /*
+ * The check to see if it's safe to poke at data has an enormous
+ * number of corner cases, but it seems that the following is
+ * more or less safe.
+ *
+ * We do not even try to look at transfer_buffer, because it can
+ * contain non-NULL garbage in case the upper level promised to
+ * set DMA for the HCD.
+ */
+ if (urb->transfer_flags & URB_NO_TRANSFER_DMA_MAP)
+ return mon_dmapeek(ep->data, urb->transfer_dma, len);
+
+ if (urb->transfer_buffer == NULL)
+ return 'Z'; /* '0' would be not as pretty. */
+
+ memcpy(ep->data, urb->transfer_buffer, len);
+ return 0;
+}
+
+/*
+ * [Re]allocate slab cache accoring to specified sizes.
+ * Temporary remove reader from reader list during resize.
+ */
+int mon_list_resize(struct mon_reader_list* rp, int data_max)
+{
+ unsigned long flags;
+ int pos;
+ kmem_cache_t * new_e_slab;
+ char id;
+ char new_name[SLAB_NAME_SZ];
+ int ret=0;
+
+ /* we want to avoil large memory consumption and have an usable amount
+ * of data */
+ if ((data_max < DATA_MIN)|| (data_max > DATA_MAX))
+ return -EINVAL;
+
+ /* remove this reader from list to avoid urb_submit accessing slab
+ * cache*/
+ mutex_lock(&mon_lock);
+ mon_reader_del(rp->r.m_bus, &rp->r);
+ mutex_unlock(&mon_lock);
+
+ /* avoid trying to create two slab with same name: it will fail
+ * swap the last char from '0' to '1' and vice versa*/
+ strcpy(new_name, rp->slab_name);
+ pos = strlen(new_name) - 1;
+ id = new_name[pos];
+ new_name[pos] = (1 - (id - '0')) + '0';
+
+ if ((new_e_slab = kmem_cache_create(new_name,
+ sizeof(struct mon_event_list)+data_max, sizeof(long), 0,
+ mon_list_ctor, NULL)) == NULL) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ /* event list must be flushed because old entries have different size */
+ spin_lock_irqsave(&rp->r.m_bus->lock, flags);
+ INIT_LIST_HEAD(&rp->e_list);
+ rp->nevents = 0;
+ spin_unlock_irqrestore(&rp->r.m_bus->lock, flags);
+ rp->data_max = data_max;
+ strcpy(rp->slab_name, new_name);
+
+out:
+ mutex_lock(&mon_lock);
+ mon_reader_add(rp->r.m_bus, &rp->r);
+ mutex_unlock(&mon_lock);
+ return ret;
+}
+
+static inline struct mon_event_list * mon_event_alloc(
+ struct mon_reader_list *rp)
+{
+ struct mon_event_list *ep = kmem_cache_alloc(rp->e_slab, SLAB_ATOMIC);
+ if (!ep)
+ return 0;
+ ep->data = ((unsigned char*)ep) + sizeof(struct mon_event_list);
+ return ep;
+}
+
+/*
+ * Convert a pointer to u64, avoiding arch dependent issue
+ */
+static u64 mon_make_id(void* urb)
+{
+ union {
+ void * ptr;
+ u64 data;
+ } converter;
+ converter.data = 0;
+ converter.ptr = urb;
+ return converter.data;
+}
+
+static void mon_list_event(struct mon_reader_list *rp, struct urb *urb,
+ char ev_type)
+{
+ struct mon_event_list *ep;
+
+ if (rp->nevents >= EVENT_MAX ||
+ (ep = mon_event_alloc(rp)) == NULL) {
+ rp->r.m_bus->cnt_lost++;
+ return;
+ }
+
+ ep->hdr.type = ev_type;
+ ep->hdr.pipe = urb->pipe;
+ ep->hdr.id = mon_make_id(urb);
+ do_gettimeofday(&ep->hdr.tstamp);
+ ep->hdr.length = (ev_type == 'S') ?
+ urb->transfer_buffer_length : urb->actual_length;
+ /* Collecting status makes debugging sense for submits, too */
+ ep->hdr.status = urb->status;
+
+ ep->hdr.setup_flag = mon_list_get_setup(ep, urb, ev_type);
+ ep->hdr.data_flag = mon_list_get_data(ep, urb, ep->hdr.length, ev_type,
+ rp->data_max);
+
+ rp->nevents++;
+ list_add_tail(&ep->e_link, &rp->e_list);
+ wake_up(&rp->wait);
+}
+
+static void mon_list_submit(void *data, struct urb *urb)
+{
+ struct mon_reader_list *rp = data;
+ mon_list_event(rp, urb, 'S');
+}
+
+static void mon_list_complete(void *data, struct urb *urb)
+{
+ struct mon_reader_list *rp = data;
+ mon_list_event(rp, urb, 'C');
+}
+
+static void mon_list_error(void *data, struct urb *urb, int error)
+{
+ struct mon_reader_list *rp = data;
+ struct mon_event_list *ep;
+
+ if (rp->nevents >= EVENT_MAX ||
+ (ep = mon_event_alloc(rp)) == NULL) {
+ rp->r.m_bus->cnt_lost++;
+ return;
+ }
+
+ ep->hdr.type = 'E';
+ ep->hdr.pipe = urb->pipe;
+ ep->hdr.id = mon_make_id(urb);
+ ep->hdr.tstamp.tv_sec = 0;
+ ep->hdr.tstamp.tv_usec = 0;
+ ep->hdr.length = 0;
+ ep->hdr.status = error;
+
+ ep->hdr.setup_flag = '-';
+ ep->hdr.data_flag = 'E';
+
+ rp->nevents++;
+ list_add_tail(&ep->e_link, &rp->e_list);
+ wake_up(&rp->wait);
+}
+
+/*
+ * Fetch next event from the circular buffer.
+ */
+struct mon_event_list *mon_list_fetch(struct mon_reader_list *rp,
+ struct mon_bus *mbus)
+{
+ struct list_head *p;
+ unsigned long flags;
+
+ spin_lock_irqsave(&mbus->lock, flags);
+ if (list_empty(&rp->e_list)) {
+ spin_unlock_irqrestore(&mbus->lock, flags);
+ return NULL;
+ }
+ p = rp->e_list.next;
+ list_del(p);
+ --rp->nevents;
+ spin_unlock_irqrestore(&mbus->lock, flags);
+ return list_entry(p, struct mon_event_list, e_link);
+}
+
+/*
+ * Initialize list reader. Must be called with mon_lock hold and before
+ * mon_reader_add
+ */
+int mon_list_init(struct mon_reader_list* rp, struct mon_bus *mbus, char type)
+{
+ struct usb_bus *ubus = mbus->u_bus;
+
+ INIT_LIST_HEAD(&rp->e_list);
+ init_waitqueue_head(&rp->wait);
+
+ rp->r.m_bus = mbus;
+ rp->r.r_data = rp;
+ rp->r.rnf_submit = mon_list_submit;
+ rp->r.rnf_error = mon_list_error;
+ rp->r.rnf_complete = mon_list_complete;
+
+ snprintf(rp->slab_name, SLAB_NAME_SZ, "mon%d%c_%lx", ubus->busnum, type,
+ (long)rp);
+ rp->data_max = DATA_DFL;
+ rp->e_slab = kmem_cache_create(rp->slab_name,
+ sizeof(struct mon_event_list)+rp->data_max, sizeof(long), 0,
+ mon_list_ctor, NULL);
+ if (rp->e_slab == NULL)
+ return -ENOMEM;
+ return 0;
+}
+
+/*
+ * Destroy list reader. Must be called after mon_reader_del.
+ * rp must be deallocated explicitly out of here
+ */
+void mon_list_destroy(struct mon_reader_list* rp)
+{
+ struct list_head *p;
+ struct mon_event_list *ep;
+
+ /*
+ * In theory, e_list is protected by mbus->lock. However,
+ * after mon_reader_del has finished, the following is the case:
+ * - we are not on reader list anymore, so new events won't be added;
+ * - whole mbus may be dropped if it was orphaned.
+ * So, we better not touch mbus.
+ */
+ /* spin_lock_irqsave(&mbus->lock, flags); */
+ while (!list_empty(&rp->e_list)) {
+ p = rp->e_list.next;
+ ep = list_entry(p, struct mon_event_list, e_link);
+ list_del(p);
+ --rp->nevents;
+ kmem_cache_free(rp->e_slab, ep);
+ }
+ /* spin_unlock_irqrestore(&mbus->lock, flags); */
+
+ kmem_cache_destroy(rp->e_slab);
+}
+
+
+/*
+ * Slab interface: constructor.
+ */
+static void mon_list_ctor(void *mem, kmem_cache_t *slab, unsigned long sflags)
+{
+ /*
+ * Nothing to initialize. No, really!
+ * So, we fill it with garbage to emulate a reused object.
+ */
+ memset(mem, 0xe5, kmem_cache_size(slab));
+}
diff -uprN -X linux-2.6.18-vanilla/Documentation/dontdiff linux-2.6.18-vanilla/drivers/usb/mon/mon_main.c linux-2.6.18-monioctl/drivers/usb/mon/mon_main.c
--- linux-2.6.18-vanilla/drivers/usb/mon/mon_main.c 2006-09-20 05:42:06.000000000 +0200
+++ linux-2.6.18-monioctl/drivers/usb/mon/mon_main.c 2006-10-10 22:04:58.000000000 +0200
@@ -214,6 +214,7 @@ static void mon_bus_remove(struct usb_bu
list_del(&mbus->bus_link);
debugfs_remove(mbus->dent_t);
debugfs_remove(mbus->dent_s);
+ debugfs_remove(mbus->dent_b);
mon_dissolve(mbus, ubus);
kref_put(&mbus->ref, mon_bus_drop);
@@ -320,11 +321,22 @@ static void mon_bus_init(struct dentry *
goto err_create_s;
mbus->dent_s = d;
+ rc = snprintf(name, NAMESZ, "%db", ubus->busnum);
+ if (rc <= 0 || rc >= NAMESZ)
+ goto err_print_b;
+ d = debugfs_create_file(name, 0600, mondir, mbus, &mon_fops_binary);
+ if (d == NULL)
+ goto err_create_b;
+ mbus->dent_b = d;
+
mutex_lock(&mon_lock);
list_add_tail(&mbus->bus_link, &mon_buses);
mutex_unlock(&mon_lock);
return;
+err_create_b:
+err_print_b:
+ debugfs_remove(mbus->dent_s);
err_create_s:
err_print_s:
debugfs_remove(mbus->dent_t);
@@ -384,6 +396,7 @@ static void __exit mon_exit(void)
debugfs_remove(mbus->dent_t);
debugfs_remove(mbus->dent_s);
+ debugfs_remove(mbus->dent_b);
/*
* This never happens, because the open/close paths in
diff -uprN -X linux-2.6.18-vanilla/Documentation/dontdiff linux-2.6.18-vanilla/drivers/usb/mon/mon_stat.c linux-2.6.18-monioctl/drivers/usb/mon/mon_stat.c
--- linux-2.6.18-vanilla/drivers/usb/mon/mon_stat.c 2006-09-20 05:42:06.000000000 +0200
+++ linux-2.6.18-monioctl/drivers/usb/mon/mon_stat.c 2006-10-10 11:57:03.000000000 +0200
@@ -31,8 +31,8 @@ static int mon_stat_open(struct inode *i
mbus = inode->u.generic_ip;
sp->slen = snprintf(sp->str, STAT_BUF_SIZE,
- "nreaders %d events %u text_lost %u\n",
- mbus->nreaders, mbus->cnt_events, mbus->cnt_text_lost);
+ "nreaders %d events %u lost %u\n",
+ mbus->nreaders, mbus->cnt_events, mbus->cnt_lost);
file->private_data = sp;
return 0;
diff -uprN -X linux-2.6.18-vanilla/Documentation/dontdiff linux-2.6.18-vanilla/drivers/usb/mon/mon_text.c linux-2.6.18-monioctl/drivers/usb/mon/mon_text.c
--- linux-2.6.18-vanilla/drivers/usb/mon/mon_text.c 2006-09-20 05:42:06.000000000 +0200
+++ linux-2.6.18-monioctl/drivers/usb/mon/mon_text.c 2006-10-11 10:35:33.000000000 +0200
@@ -9,245 +9,54 @@
#include <linux/usb.h>
#include <linux/time.h>
#include <linux/mutex.h>
+#include <linux/ioctl.h>
#include <asm/uaccess.h>
#include "usb_mon.h"
-/*
- * No, we do not want arbitrarily long data strings.
- * Use the binary interface if you want to capture bulk data!
- */
-#define DATA_MAX 32
-
-/*
- * Defined by USB 2.0 clause 9.3, table 9.2.
- */
-#define SETUP_MAX 8
+#define PRINTF_DFL 160
+#define PRINTF_MIN 120
-/*
- * This limit exists to prevent OOMs when the user process stops reading.
- * If usbmon were available to unprivileged processes, it might be open
- * to a local DoS. But we have to keep to root in order to prevent
- * password sniffing from HID devices.
- */
-#define EVENT_MAX (2*PAGE_SIZE / sizeof(struct mon_event_text))
+/* ioctl macros */
+#define MON_IOC_MAGIC 0x92
-#define PRINTF_DFL 160
+#define MON_IOCT_DATA_MAX _IO(MON_IOC_MAGIC, 1)
+#define MON_IOCG_DATA_MAX _IOR(MON_IOC_MAGIC, 2, u32)
-struct mon_event_text {
- struct list_head e_link;
- int type; /* submit, complete, etc. */
- unsigned int pipe; /* Pipe */
- unsigned long id; /* From pointer, most of the time */
- unsigned int tstamp;
- int length; /* Depends on type: xfer length or act length */
- int status;
- char setup_flag;
- char data_flag;
- unsigned char setup[SETUP_MAX];
- unsigned char data[DATA_MAX];
-};
+#define MON_IOC_MAXNR 2
-#define SLAB_NAME_SZ 30
struct mon_reader_text {
- kmem_cache_t *e_slab;
- int nevents;
- struct list_head e_list;
- struct mon_reader r; /* In C, parent class can be placed anywhere */
-
- wait_queue_head_t wait;
+ struct mon_reader_list l;
int printf_size;
char *printf_buf;
struct mutex printf_lock;
-
- char slab_name[SLAB_NAME_SZ];
};
-static void mon_text_ctor(void *, kmem_cache_t *, unsigned long);
-
-/*
- * mon_text_submit
- * mon_text_complete
- *
- * May be called from an interrupt.
- *
- * This is called with the whole mon_bus locked, so no additional lock.
- */
-
-static inline char mon_text_get_setup(struct mon_event_text *ep,
- struct urb *urb, char ev_type)
-{
-
- if (!usb_pipecontrol(urb->pipe) || ev_type != 'S')
- return '-';
-
- if (urb->transfer_flags & URB_NO_SETUP_DMA_MAP)
- return mon_dmapeek(ep->setup, urb->setup_dma, SETUP_MAX);
- if (urb->setup_packet == NULL)
- return 'Z'; /* '0' would be not as pretty. */
-
- memcpy(ep->setup, urb->setup_packet, SETUP_MAX);
- return 0;
-}
-
-static inline char mon_text_get_data(struct mon_event_text *ep, struct urb *urb,
- int len, char ev_type)
-{
- int pipe = urb->pipe;
-
- if (len <= 0)
- return 'L';
- if (len >= DATA_MAX)
- len = DATA_MAX;
-
- if (usb_pipein(pipe)) {
- if (ev_type == 'S')
- return '<';
- } else {
- if (ev_type == 'C')
- return '>';
- }
-
- /*
- * The check to see if it's safe to poke at data has an enormous
- * number of corner cases, but it seems that the following is
- * more or less safe.
- *
- * We do not even try to look at transfer_buffer, because it can
- * contain non-NULL garbage in case the upper level promised to
- * set DMA for the HCD.
- */
- if (urb->transfer_flags & URB_NO_TRANSFER_DMA_MAP)
- return mon_dmapeek(ep->data, urb->transfer_dma, len);
-
- if (urb->transfer_buffer == NULL)
- return 'Z'; /* '0' would be not as pretty. */
-
- memcpy(ep->data, urb->transfer_buffer, len);
- return 0;
-}
-
-static inline unsigned int mon_get_timestamp(void)
+static inline unsigned int mon_get_timestamp(struct timeval* tval)
{
- struct timeval tval;
unsigned int stamp;
- do_gettimeofday(&tval);
- stamp = tval.tv_sec & 0xFFFF; /* 2^32 = 4294967296. Limit to 4096s. */
- stamp = stamp * 1000000 + tval.tv_usec;
+ stamp = tval->tv_sec & 0xFFFF; /* 2^32 = 4294967296. Limit to 4096s. */
+ stamp = stamp * 1000000 + tval->tv_usec;
return stamp;
}
-static void mon_text_event(struct mon_reader_text *rp, struct urb *urb,
- char ev_type)
-{
- struct mon_event_text *ep;
- unsigned int stamp;
-
- stamp = mon_get_timestamp();
-
- if (rp->nevents >= EVENT_MAX ||
- (ep = kmem_cache_alloc(rp->e_slab, SLAB_ATOMIC)) == NULL) {
- rp->r.m_bus->cnt_text_lost++;
- return;
- }
-
- ep->type = ev_type;
- ep->pipe = urb->pipe;
- ep->id = (unsigned long) urb;
- ep->tstamp = stamp;
- ep->length = (ev_type == 'S') ?
- urb->transfer_buffer_length : urb->actual_length;
- /* Collecting status makes debugging sense for submits, too */
- ep->status = urb->status;
-
- ep->setup_flag = mon_text_get_setup(ep, urb, ev_type);
- ep->data_flag = mon_text_get_data(ep, urb, ep->length, ev_type);
-
- rp->nevents++;
- list_add_tail(&ep->e_link, &rp->e_list);
- wake_up(&rp->wait);
-}
-
-static void mon_text_submit(void *data, struct urb *urb)
-{
- struct mon_reader_text *rp = data;
- mon_text_event(rp, urb, 'S');
-}
-
-static void mon_text_complete(void *data, struct urb *urb)
-{
- struct mon_reader_text *rp = data;
- mon_text_event(rp, urb, 'C');
-}
-
-static void mon_text_error(void *data, struct urb *urb, int error)
-{
- struct mon_reader_text *rp = data;
- struct mon_event_text *ep;
-
- if (rp->nevents >= EVENT_MAX ||
- (ep = kmem_cache_alloc(rp->e_slab, SLAB_ATOMIC)) == NULL) {
- rp->r.m_bus->cnt_text_lost++;
- return;
- }
-
- ep->type = 'E';
- ep->pipe = urb->pipe;
- ep->id = (unsigned long) urb;
- ep->tstamp = 0;
- ep->length = 0;
- ep->status = error;
-
- ep->setup_flag = '-';
- ep->data_flag = 'E';
-
- rp->nevents++;
- list_add_tail(&ep->e_link, &rp->e_list);
- wake_up(&rp->wait);
-}
-
-/*
- * Fetch next event from the circular buffer.
- */
-static struct mon_event_text *mon_text_fetch(struct mon_reader_text *rp,
- struct mon_bus *mbus)
-{
- struct list_head *p;
- unsigned long flags;
-
- spin_lock_irqsave(&mbus->lock, flags);
- if (list_empty(&rp->e_list)) {
- spin_unlock_irqrestore(&mbus->lock, flags);
- return NULL;
- }
- p = rp->e_list.next;
- list_del(p);
- --rp->nevents;
- spin_unlock_irqrestore(&mbus->lock, flags);
- return list_entry(p, struct mon_event_text, e_link);
-}
-
/*
*/
static int mon_text_open(struct inode *inode, struct file *file)
{
struct mon_bus *mbus;
- struct usb_bus *ubus;
struct mon_reader_text *rp;
int rc;
mutex_lock(&mon_lock);
mbus = inode->u.generic_ip;
- ubus = mbus->u_bus;
rp = kzalloc(sizeof(struct mon_reader_text), GFP_KERNEL);
if (rp == NULL) {
rc = -ENOMEM;
goto err_alloc;
}
- INIT_LIST_HEAD(&rp->e_list);
- init_waitqueue_head(&rp->wait);
mutex_init(&rp->printf_lock);
rp->printf_size = PRINTF_DFL;
@@ -256,24 +65,10 @@ static int mon_text_open(struct inode *i
rc = -ENOMEM;
goto err_alloc_pr;
}
+ if ((rc = mon_list_init(&rp->l, mbus, 't')) < 0)
+ goto err_list;
- rp->r.m_bus = mbus;
- rp->r.r_data = rp;
- rp->r.rnf_submit = mon_text_submit;
- rp->r.rnf_error = mon_text_error;
- rp->r.rnf_complete = mon_text_complete;
-
- snprintf(rp->slab_name, SLAB_NAME_SZ, "mon%dt_%lx", ubus->busnum,
- (long)rp);
- rp->e_slab = kmem_cache_create(rp->slab_name,
- sizeof(struct mon_event_text), sizeof(long), 0,
- mon_text_ctor, NULL);
- if (rp->e_slab == NULL) {
- rc = -ENOMEM;
- goto err_slab;
- }
-
- mon_reader_add(mbus, &rp->r);
+ mon_reader_add(mbus, &rp->l.r);
file->private_data = rp;
mutex_unlock(&mon_lock);
@@ -281,7 +76,7 @@ static int mon_text_open(struct inode *i
// err_busy:
// kmem_cache_destroy(rp->e_slab);
-err_slab:
+err_list:
kfree(rp->printf_buf);
err_alloc_pr:
kfree(rp);
@@ -300,20 +95,20 @@ static ssize_t mon_text_read(struct file
size_t nbytes, loff_t *ppos)
{
struct mon_reader_text *rp = file->private_data;
- struct mon_bus *mbus = rp->r.m_bus;
+ struct mon_bus *mbus = rp->l.r.m_bus;
DECLARE_WAITQUEUE(waita, current);
- struct mon_event_text *ep;
+ struct mon_event_list *ep;
int cnt, limit;
char *pbuf;
char udir, utype;
int data_len, i;
- add_wait_queue(&rp->wait, &waita);
+ add_wait_queue(&rp->l.wait, &waita);
set_current_state(TASK_INTERRUPTIBLE);
- while ((ep = mon_text_fetch(rp, mbus)) == NULL) {
+ while ((ep = mon_list_fetch(&rp->l, mbus)) == NULL) {
if (file->f_flags & O_NONBLOCK) {
set_current_state(TASK_RUNNING);
- remove_wait_queue(&rp->wait, &waita);
+ remove_wait_queue(&rp->l.wait, &waita);
return -EWOULDBLOCK; /* Same as EAGAIN in Linux */
}
/*
@@ -322,21 +117,21 @@ static ssize_t mon_text_read(struct file
*/
schedule();
if (signal_pending(current)) {
- remove_wait_queue(&rp->wait, &waita);
+ remove_wait_queue(&rp->l.wait, &waita);
return -EINTR;
}
set_current_state(TASK_INTERRUPTIBLE);
}
set_current_state(TASK_RUNNING);
- remove_wait_queue(&rp->wait, &waita);
+ remove_wait_queue(&rp->l.wait, &waita);
mutex_lock(&rp->printf_lock);
cnt = 0;
pbuf = rp->printf_buf;
limit = rp->printf_size;
- udir = usb_pipein(ep->pipe) ? 'i' : 'o';
- switch (usb_pipetype(ep->pipe)) {
+ udir = usb_pipein(ep->hdr.pipe) ? 'i' : 'o';
+ switch (usb_pipetype(ep->hdr.pipe)) {
case PIPE_ISOCHRONOUS: utype = 'Z'; break;
case PIPE_INTERRUPT: utype = 'I'; break;
case PIPE_CONTROL: utype = 'C'; break;
@@ -344,10 +139,11 @@ static ssize_t mon_text_read(struct file
}
cnt += snprintf(pbuf + cnt, limit - cnt,
"%lx %u %c %c%c:%03u:%02u",
- ep->id, ep->tstamp, ep->type,
- utype, udir, usb_pipedevice(ep->pipe), usb_pipeendpoint(ep->pipe));
+ (long unsigned) ep->hdr.id, mon_get_timestamp(&ep->hdr.tstamp),
+ ep->hdr.type, utype, udir, usb_pipedevice(ep->hdr.pipe),
+ usb_pipeendpoint(ep->hdr.pipe));
- if (ep->setup_flag == 0) { /* Setup packet is present and captured */
+ if (ep->hdr.setup_flag == 0) { /* Setup packet is present and captured */
cnt += snprintf(pbuf + cnt, limit - cnt,
" s %02x %02x %04x %04x %04x",
ep->setup[0],
@@ -355,19 +151,19 @@ static ssize_t mon_text_read(struct file
(ep->setup[3] << 8) | ep->setup[2],
(ep->setup[5] << 8) | ep->setup[4],
(ep->setup[7] << 8) | ep->setup[6]);
- } else if (ep->setup_flag != '-') { /* Unable to capture setup packet */
+ } else if (ep->hdr.setup_flag != '-') { /* Unable to capture setup packet */
cnt += snprintf(pbuf + cnt, limit - cnt,
- " %c __ __ ____ ____ ____", ep->setup_flag);
+ " %c __ __ ____ ____ ____", ep->hdr.setup_flag);
} else { /* No setup for this kind of URB */
- cnt += snprintf(pbuf + cnt, limit - cnt, " %d", ep->status);
+ cnt += snprintf(pbuf + cnt, limit - cnt, " %d", ep->hdr.status);
}
- cnt += snprintf(pbuf + cnt, limit - cnt, " %d", ep->length);
+ cnt += snprintf(pbuf + cnt, limit - cnt, " %d", ep->hdr.length);
- if ((data_len = ep->length) > 0) {
- if (ep->data_flag == 0) {
+ if ((data_len = ep->hdr.length) > 0) {
+ if (ep->hdr.data_flag == 0) {
cnt += snprintf(pbuf + cnt, limit - cnt, " =");
- if (data_len >= DATA_MAX)
- data_len = DATA_MAX;
+ if (data_len >= rp->l.data_max)
+ data_len = rp->l.data_max;
for (i = 0; i < data_len; i++) {
if (i % 4 == 0) {
cnt += snprintf(pbuf + cnt, limit - cnt,
@@ -379,7 +175,7 @@ static ssize_t mon_text_read(struct file
cnt += snprintf(pbuf + cnt, limit - cnt, "\n");
} else {
cnt += snprintf(pbuf + cnt, limit - cnt,
- " %c\n", ep->data_flag);
+ " %c\n", ep->hdr.data_flag);
}
} else {
cnt += snprintf(pbuf + cnt, limit - cnt, "\n");
@@ -388,7 +184,7 @@ static ssize_t mon_text_read(struct file
if (copy_to_user(buf, rp->printf_buf, cnt))
cnt = -EFAULT;
mutex_unlock(&rp->printf_lock);
- kmem_cache_free(rp->e_slab, ep);
+ kmem_cache_free(rp->l.e_slab, ep);
return cnt;
}
@@ -397,8 +193,6 @@ static int mon_text_release(struct inode
struct mon_reader_text *rp = file->private_data;
struct mon_bus *mbus;
/* unsigned long flags; */
- struct list_head *p;
- struct mon_event_text *ep;
mutex_lock(&mon_lock);
mbus = inode->u.generic_ip;
@@ -408,26 +202,10 @@ static int mon_text_release(struct inode
mutex_unlock(&mon_lock);
return 0;
}
- mon_reader_del(mbus, &rp->r);
+ mon_reader_del(mbus, &rp->l.r);
+
+ mon_list_destroy(&rp->l);
- /*
- * In theory, e_list is protected by mbus->lock. However,
- * after mon_reader_del has finished, the following is the case:
- * - we are not on reader list anymore, so new events won't be added;
- * - whole mbus may be dropped if it was orphaned.
- * So, we better not touch mbus.
- */
- /* spin_lock_irqsave(&mbus->lock, flags); */
- while (!list_empty(&rp->e_list)) {
- p = rp->e_list.next;
- ep = list_entry(p, struct mon_event_text, e_link);
- list_del(p);
- --rp->nevents;
- kmem_cache_free(rp->e_slab, ep);
- }
- /* spin_unlock_irqrestore(&mbus->lock, flags); */
-
- kmem_cache_destroy(rp->e_slab);
kfree(rp->printf_buf);
kfree(rp);
@@ -445,16 +223,3 @@ struct file_operations mon_fops_text = {
/* .ioctl = mon_text_ioctl, */
.release = mon_text_release,
};
-
-/*
- * Slab interface: constructor.
- */
-static void mon_text_ctor(void *mem, kmem_cache_t *slab, unsigned long sflags)
-{
- /*
- * Nothing to initialize. No, really!
- * So, we fill it with garbage to emulate a reused object.
- */
- memset(mem, 0xe5, sizeof(struct mon_event_text));
-}
-
diff -uprN -X linux-2.6.18-vanilla/Documentation/dontdiff linux-2.6.18-vanilla/drivers/usb/mon/usb_mon.h linux-2.6.18-monioctl/drivers/usb/mon/usb_mon.h
--- linux-2.6.18-vanilla/drivers/usb/mon/usb_mon.h 2006-09-20 05:42:06.000000000 +0200
+++ linux-2.6.18-monioctl/drivers/usb/mon/usb_mon.h 2006-10-11 10:34:48.000000000 +0200
@@ -19,6 +19,7 @@ struct mon_bus {
spinlock_t lock;
struct dentry *dent_s; /* Debugging file */
struct dentry *dent_t; /* Text interface file */
+ struct dentry *dent_b; /* Binary interface file */
struct usb_bus *u_bus;
/* Ref */
@@ -28,7 +29,7 @@ struct mon_bus {
/* Stats */
unsigned int cnt_events;
- unsigned int cnt_text_lost;
+ unsigned int cnt_lost;
};
/*
@@ -44,6 +45,48 @@ struct mon_reader {
void (*rnf_complete)(void *data, struct urb *urb);
};
+/*
+ * Defined by USB 2.0 clause 9.3, table 9.2.
+ */
+#define SETUP_MAX 8
+
+struct mon_event_hdr {
+ s32 type; /* submit, complete, etc. */
+ u32 pipe; /* Pipe */
+ u64 id; /* Incremental id */
+ struct timeval tstamp;
+ u32 length; /* Depends on type: xfer length or act length */
+ s32 status;
+ s8 setup_flag;
+ s8 data_flag;
+};
+
+struct mon_event_list {
+ struct list_head e_link;
+ struct mon_event_hdr hdr;
+ unsigned char setup[SETUP_MAX];
+ unsigned char* data;
+};
+
+#define SLAB_NAME_SZ 30
+struct mon_reader_list {
+ kmem_cache_t *e_slab;
+ unsigned data_max;
+ int nevents;
+ struct list_head e_list;
+ struct mon_reader r; /* In C, parent class can be placed anywhere */
+
+ wait_queue_head_t wait;
+ char slab_name[SLAB_NAME_SZ];
+};
+
+/* list reader operation */
+int mon_list_init(struct mon_reader_list* rp, struct mon_bus *mbus, char type);
+struct mon_event_list *mon_list_fetch(struct mon_reader_list *rp,
+ struct mon_bus *mbus);
+int mon_list_resize(struct mon_reader_list* rp, int data_max);
+void mon_list_destroy(struct mon_reader_list* rp);
+
void mon_reader_add(struct mon_bus *mbus, struct mon_reader *r);
void mon_reader_del(struct mon_bus *mbus, struct mon_reader *r);
@@ -53,6 +96,7 @@ extern char mon_dmapeek(unsigned char *d
extern struct mutex mon_lock;
+extern struct file_operations mon_fops_binary;
extern struct file_operations mon_fops_text;
extern struct file_operations mon_fops_stat;
--
Email.it, the professional e-mail, gratis per te: http://www.email.it/f
Sponsor:
Acquista i tuoi gioielli in tutta sicurezza ed a prezzi veramente imbattibili. Sfoglia il nostro catalogo on-line!
Clicca qui: http://adv.email.it/cgi-bin/foclick.cgi?mid=5634&d=11-10
--
Email.it, the professional e-mail, gratis per te: http://www.email.it/f
Sponsor:
Refill s.r.l. - Tutto per la tua stampante a prezzi incredibili: su cartucce, toner, inchiostri, carta speciale risparmi fino al 90%!
Clicca qui: http://adv.email.it/cgi-bin/foclick.cgi?mid=5189&d=11-10
-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@...r.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
Powered by blists - more mailing lists