linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* My vision of usbmon
@ 2004-12-20  7:04 Pete Zaitcev
       [not found] ` <200412201255.59120.oliver@neukum.org>
                   ` (2 more replies)
  0 siblings, 3 replies; 15+ messages in thread
From: Pete Zaitcev @ 2004-12-20  7:04 UTC (permalink / raw)
  To: zaitcev; +Cc: linux-usb-devel, linux-kernel, laforge, greg

Hi, Guys:

This is usbmon which I cooked up because I got tired from adding dbg()'s
and polluting my dmesg. I use it to hunt bugs in USB storage devices so
far, and it's useful, although limited at this stage.

I looked at the Harding's USBmon patch, and I think he got a few things right.
The main of them is that I underestimated the benefits of placing the special
files into the filesystem namespace. When we discussed it with Greg in the
airport, we decided that having some sort of Netlink-style socket would be
the best option. I decided to make a u-turn and attach those sockets into
the namespace (currently under /dbg, but it can change). What this buys us is:

 1. cat(1): never bet against it. It's too handy. And netcat is just
    not the same.
 2. USBmon userland in Java. Just try to hack in JNI a little as I have
    and you'll see.

He also got some parts wrong. They are small things, but unfortunately,
pervasive. For example, he relies on urb->dev, which is not a good idea
in case of HCD which zero it far away from the completion call site, such
as usb-ohci in 2.4. And it's error-prone and a maintenance problem to
audit all HCDs and add usbmon calls. Races by design, too. Small things
like that, but many. Eventually, I wrote everything from scratch. It's
rather embarrassing that I could not save USBmon and gave in to NIH.

Since it's a big NIH, usbmon is not compatible with USBmon's userland.
It can be made compatible, but it needs a small adaptation layer,
because Harding aggregated at a device, and I do it on a bus
(I can explain why, but it's rather long; it has to do with hotplug
and races).

The architecture to support various output formats is present. Obvious
candidates are Old USBmon format and a Binary format. But it's not done.

Please ask if something is not obvious in the code.

Bye,
-- Pete

diff -urpN -X dontdiff linux-2.6.10-rc3/drivers/usb/Kconfig linux-2.6.10-rc3-usb/drivers/usb/Kconfig
--- linux-2.6.10-rc3/drivers/usb/Kconfig	2004-12-15 02:01:05.000000000 -0800
+++ linux-2.6.10-rc3-usb/drivers/usb/Kconfig	2004-12-15 13:36:33.000000000 -0800
@@ -56,6 +56,8 @@ source "drivers/usb/media/Kconfig"
 
 source "drivers/usb/net/Kconfig"
 
+source "drivers/usb/mon/Kconfig"
+
 comment "USB port drivers"
 	depends on USB
 
diff -urpN -X dontdiff linux-2.6.10-rc3/drivers/usb/Makefile linux-2.6.10-rc3-usb/drivers/usb/Makefile
--- linux-2.6.10-rc3/drivers/usb/Makefile	2004-12-15 02:01:05.000000000 -0800
+++ linux-2.6.10-rc3-usb/drivers/usb/Makefile	2004-12-15 13:49:26.000000000 -0800
@@ -6,6 +6,8 @@
 
 obj-$(CONFIG_USB)		+= core/
 
+obj-$(CONFIG_USB_MON)		+= mon/
+
 obj-$(CONFIG_USB_EHCI_HCD)	+= host/
 obj-$(CONFIG_USB_OHCI_HCD)	+= host/
 obj-$(CONFIG_USB_UHCI_HCD)	+= host/
diff -urpN -X dontdiff linux-2.6.10-rc3/drivers/usb/mon/Kconfig linux-2.6.10-rc3-usb/drivers/usb/mon/Kconfig
--- linux-2.6.10-rc3/drivers/usb/mon/Kconfig	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.6.10-rc3-usb/drivers/usb/mon/Kconfig	2004-12-15 19:56:54.000000000 -0800
@@ -0,0 +1,21 @@
+#
+# USB Monitor configuration
+#
+
+# In normal life, it makes little sense to have usbmon as a module, and in fact
+# it is harmful, because there is no way to autoload the module.
+# The 'm' option is allowed for hackers who debug the usbmon itself,
+# and for those who have usbcore as a module.
+config USB_MON
+	tristate "USB Monitor"
+	depends on USB && SYSFS
+	default y
+	help
+	  If you say Y here, a component which captures the USB traffic
+	  between peripheral-specific drivers and HC drivers will be built.
+	  The USB_MON is similar in spirit and may be compatible with Dave
+	  Harding's USBMon.
+
+	  This is somewhat experimental at this time, but it should be safe.
+
+	  If unsure, say Y.
diff -urpN -X dontdiff linux-2.6.10-rc3/drivers/usb/mon/Makefile linux-2.6.10-rc3-usb/drivers/usb/mon/Makefile
--- linux-2.6.10-rc3/drivers/usb/mon/Makefile	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.6.10-rc3-usb/drivers/usb/mon/Makefile	2004-12-17 18:33:05.000000000 -0800
@@ -0,0 +1,7 @@
+#
+# Makefile for USB Core files and filesystem
+#
+
+usbmon-objs	:= mon_main.o mon_stat.o mon_text.o
+
+obj-$(CONFIG_USB_MON)	+= usbmon.o
diff -urpN -X dontdiff linux-2.6.10-rc3/drivers/usb/mon/mon_main.c linux-2.6.10-rc3-usb/drivers/usb/mon/mon_main.c
--- linux-2.6.10-rc3/drivers/usb/mon/mon_main.c	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.6.10-rc3-usb/drivers/usb/mon/mon_main.c	2004-12-19 21:04:13.852433525 -0800
@@ -0,0 +1,306 @@
+/*
+ * The USB Monitor, inspired by Dave Harding's USBMon.
+ *
+ * mon_main.c: Main file, module initiation and exit, registrations, etc.
+ *
+ * - If an URB was submitted before a reader opened a pipe, it's not tracked.
+ *   The only solution is to reject intercept-on-open and do intercept-always.
+ * - Hotplugging HCs will cause usbmon to crash on unload
+ * - Type files as fifos, sockets, or character specials? /proc/kmsg is regular.
+ * - Awaken rmmod when outstanding URBs and opens are gone.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/usb.h>
+#include <linux/debugfs.h>
+#include <linux/smp_lock.h>
+
+#include "usb_mon.h"
+
+/* XXX Currently in drivers/usb/core/hcd.h */
+extern struct list_head usb_bus_list;
+extern struct semaphore usb_bus_list_lock;
+
+static int mon_submit(struct urb *urb, int mem_flags);
+static void mon_complete(struct urb *urb, struct pt_regs *regs);
+static void mon_cleanup(struct mon_bus *mbus);
+
+DECLARE_MUTEX(mon_lock);
+
+static struct dentry *mon_dir;		/* /dbg/usbmon */
+static LIST_HEAD(mon_buses);		/* All buses we know: struct mon_bus */
+
+/*
+ * Link a reader into the bus.
+ */
+void mon_reader_add(struct mon_bus *mbus, struct mon_reader *r)
+{
+	unsigned long flags;
+	struct usb_bus *ubus;
+
+	spin_lock_irqsave(&mbus->lock, flags);
+	if (mbus->nreaders == 0 && mbus->nurbs == 0) {
+		ubus = mbus->u_bus;
+		if (ubus->monitored) {
+			/*
+			 * Something is really broken, refuse to go on and
+			 * possibly corrupt ops pointers or worse.
+			 */
+			printk(KERN_ERR TAG ": bus %d is already monitored\n",
+			    ubus->busnum);
+			spin_unlock_irqrestore(&mbus->lock, flags);
+			return;
+		}
+		memcpy(&mbus->shim_ops, ubus->op, sizeof(struct usb_operations));
+		mbus->shim_ops.submit_urb = mon_submit;
+		mbus->saved_op = ubus->op;
+		ubus->op = &mbus->shim_ops;
+		ubus->monitored = 1;
+	}
+	mbus->nreaders++;
+
+	list_add_tail(&r->r_link, &mbus->r_list);
+	spin_unlock_irqrestore(&mbus->lock, flags);
+}
+
+/*
+ * Unlink reader from the bus.
+ */
+void mon_reader_del(struct mon_bus *mbus, struct mon_reader *r)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&mbus->lock, flags);
+	list_del(&r->r_link);
+	--mbus->nreaders;
+	if (mbus->nreaders == 0 && mbus->nurbs == 0)
+		mon_cleanup(mbus);
+	spin_unlock_irqrestore(&mbus->lock, flags);
+}
+
+/*
+ */
+static int mon_submit(struct urb *urb, int mem_flags)
+{
+	struct mon_bus *mbus;
+	unsigned long flags;
+	struct list_head *pos;
+	struct mon_reader *r;
+
+	mbus = urb->dev->bus->mon_bus;
+	if (mbus == NULL)
+		goto out_unlocked;
+
+	spin_lock_irqsave(&mbus->lock, flags);
+	if (mbus->nreaders == 0)
+		goto out_locked;
+
+	urb->mon_bus = mbus;
+	urb->mon_saved_complete = urb->complete;
+	urb->complete = mon_complete;
+	mbus->nurbs++;
+
+	list_for_each (pos, &mbus->r_list) {
+		r = list_entry(pos, struct mon_reader, r_link);
+		r->rnf_submit(r->r_data, urb);
+	}
+out_locked:
+	spin_unlock_irqrestore(&mbus->lock, flags);
+out_unlocked:
+	return mbus->saved_op->submit_urb(urb, mem_flags);
+	/* Woops, we forgot to deliver success or failure to the reader */
+}
+
+/*
+ * This is called by an HC driver directly.
+ */
+static void mon_complete(struct urb *urb, struct pt_regs *regs)
+{
+	struct mon_bus *mbus;
+	unsigned long flags;
+	struct list_head *pos;
+	struct mon_reader *r;
+
+	mbus = urb->mon_bus;
+	if (mbus == NULL) {
+		/*
+		 * This should not happen.
+		 * At this point we do not even know the bus number...
+		 */
+		printk(KERN_ERR TAG ": Null mon bus in URB, pipe 0x%x\n",
+		    urb->pipe);
+		return;
+	}
+
+	spin_lock_irqsave(&mbus->lock, flags);
+	list_for_each (pos, &mbus->r_list) {
+		r = list_entry(pos, struct mon_reader, r_link);
+		r->rnf_complete(r->r_data, urb);
+	}
+
+	/*
+	 * Drop the URB from tracking.
+	 * XXX ISOs and Interrupt get lost after the first complete this way.
+	 */
+	urb->complete = urb->mon_saved_complete;
+	urb->mon_bus = NULL;
+	--mbus->nurbs;
+	if (mbus->nurbs == 0 && mbus->nreaders == 0)
+		mon_cleanup(mbus);
+	spin_unlock_irqrestore(&mbus->lock, flags);
+
+	(*urb->complete)(urb, regs);
+}
+
+/* int (*unlink_urb) (struct urb *urb, int status); */
+
+/*
+ * Stop monitoring.
+ * Obviously this must be well locked, so no need to play with mb's.
+ */
+static void mon_cleanup(struct mon_bus *mbus)
+{
+	struct usb_bus *ubus = mbus->u_bus;
+
+	ubus->op = mbus->saved_op;
+	ubus->monitored = 0;
+	mbus->saved_op = NULL;
+}
+
+/*
+ * Initialize a bus for us:
+ *  - allocate mon_bus
+ *  - refcount USB bus struct
+ *  - link
+ */
+static void mon_bus_init(struct dentry *mondir, struct usb_bus *ubus)
+{
+	struct dentry *d;
+	struct mon_bus *mbus;
+	enum { NAMESZ = 10 };
+	char name[NAMESZ];
+	int rc;
+
+	if ((mbus = kmalloc(sizeof(struct mon_bus), GFP_KERNEL)) == NULL)
+		goto err_alloc;
+	memset(mbus, 0, sizeof(struct mon_bus));
+	spin_lock_init(&mbus->lock);
+	INIT_LIST_HEAD(&mbus->r_list);
+
+	/* XXX Is this how I pin struct bus? Ask linux-usb-devel */
+	kobject_get(&ubus->class_dev.kobj);
+	mbus->u_bus = ubus;
+	ubus->mon_bus = mbus;
+
+	rc = snprintf(name, NAMESZ, "%dt", ubus->busnum);
+	if (rc <= 0 || rc >= NAMESZ)
+		goto err_print_t;
+	d = debugfs_create_file(name, 0600, mondir, mbus, &mon_fops_text);
+	if (d == NULL)
+		goto err_create_t;
+	mbus->dent_t = d;
+
+	rc = snprintf(name, NAMESZ, "%ds", ubus->busnum);
+	if (rc <= 0 || rc >= NAMESZ)
+		goto err_print_s;
+	d = debugfs_create_file(name, 0600, mondir, mbus, &mon_fops_stat);
+	if (d == NULL)
+		goto err_create_s;
+	mbus->dent_s = d;
+
+	down(&mon_lock);
+	list_add_tail(&mbus->bus_link, &mon_buses);
+	up(&mon_lock);
+	return;
+
+err_create_s:
+err_print_s:
+	debugfs_remove(mbus->dent_t);
+err_create_t:
+err_print_t:
+	kfree(mbus);
+err_alloc:
+	return;
+}
+
+static int __init mon_init(void)
+{
+	struct usb_bus *ubus;
+	struct dentry *mondir;
+
+	mondir = debugfs_create_dir("usbmon", NULL);
+	if (IS_ERR(mondir)) {
+		printk(KERN_NOTICE TAG ": debugs is not available\n");
+		return -ENODEV;
+	}
+	if (mondir == NULL) {
+		printk(KERN_NOTICE TAG ": unable to create usbmon directory\n");
+		return -ENODEV;
+	}
+	mon_dir = mondir;
+
+	down(&usb_bus_list_lock);
+	list_for_each_entry (ubus, &usb_bus_list, bus_list) {
+		mon_bus_init(mondir, ubus);
+	}
+	up(&usb_bus_list_lock);
+	return 0;
+}
+
+static void __exit mon_exit(void)
+{
+	struct mon_bus *mbus;
+	struct usb_bus *ubus;
+	struct list_head *p;
+
+	down(&usb_bus_list_lock);	/* Needed for kobject_put's ? */
+	down(&mon_lock);
+	while (!list_empty(&mon_buses)) {
+		p = mon_buses.next;
+		mbus = list_entry(p, struct mon_bus, bus_link);
+		list_del(p);
+
+		if (mbus->nurbs) {
+			printk(KERN_ERR TAG
+			    ": Stuck URBs (%d) on usb%d, hanging...\n",
+			    mbus->nurbs, mbus->u_bus->busnum);
+			up(&mon_lock);
+			up(&usb_bus_list_lock);
+			for (;;) {
+				set_current_state(TASK_UNINTERRUPTIBLE);
+				schedule();
+			}
+		}
+		if (mbus->nreaders) {
+			printk(KERN_ERR TAG
+			    ": Outstanding opens (%d) on usb%d, hanging...\n",
+			    mbus->nreaders, mbus->u_bus->busnum);
+			up(&mon_lock);
+			up(&usb_bus_list_lock);
+			for (;;) {
+				set_current_state(TASK_UNINTERRUPTIBLE);
+				schedule();
+			}
+		}
+
+		ubus = mbus->u_bus;
+		ubus->mon_bus = NULL;
+		mb();
+
+		/* XXX This also should be done in hot-remove path */
+		debugfs_remove(mbus->dent_t);
+		debugfs_remove(mbus->dent_s);
+		kobject_put(&ubus->class_dev.kobj);
+		kfree(mbus);
+	}
+	up(&mon_lock);
+	up(&usb_bus_list_lock);
+
+	debugfs_remove(mon_dir);
+}
+
+module_init(mon_init);
+module_exit(mon_exit);
+
+MODULE_LICENSE("GPL");
diff -urpN -X dontdiff linux-2.6.10-rc3/drivers/usb/mon/mon_stat.c linux-2.6.10-rc3-usb/drivers/usb/mon/mon_stat.c
--- linux-2.6.10-rc3/drivers/usb/mon/mon_stat.c	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.6.10-rc3-usb/drivers/usb/mon/mon_stat.c	2004-12-19 14:06:03.306311947 -0800
@@ -0,0 +1,75 @@
+/*
+ * The USB Monitor, inspired by Dave Harding's USBMon.
+ *
+ * This is the 's' or 'stat' reader which debugs usbmon itself.
+ * Note that this code blows through locks, so make sure that
+ * /dbg/usbmon/0s is well protected from non-root users.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/usb.h>
+#include <asm/uaccess.h>
+
+#include "usb_mon.h"
+
+#define STAT_BUF_SIZE  80
+
+struct snap {
+	int slen;
+	char str[STAT_BUF_SIZE];
+};
+
+static int mon_stat_open(struct inode *inode, struct file *file)
+{
+	struct mon_bus *mbus;
+	struct snap *sp;
+
+	if ((sp = kmalloc(sizeof(struct snap), GFP_KERNEL)) == NULL)
+		return -ENOMEM;
+
+	mbus = inode->u.generic_ip;
+
+	sp->slen = snprintf(sp->str, STAT_BUF_SIZE,
+	    "nreaders %d saved_op 0x%lx nurbs %d text_lost %u\n",
+	    mbus->nreaders, (long) mbus->saved_op, mbus->nurbs,
+	    mbus->cnt_text_lost);
+
+	file->private_data = sp;
+	return 0;
+}
+
+static ssize_t mon_stat_read(struct file *file, char __user *buf,
+				size_t nbytes, loff_t *ppos)
+{
+	struct snap *sp = file->private_data;
+	loff_t pos = *ppos;
+	int cnt;
+
+	if (pos < 0 || pos >= sp->slen)
+		return 0;
+	if (nbytes == 0)
+		return 0;
+	if ((cnt = sp->slen - pos) > nbytes)
+		cnt = nbytes;
+	if (copy_to_user(buf, sp->str + pos, cnt))
+		return -EFAULT;
+	*ppos = pos + cnt;
+	return cnt;
+}
+
+static int mon_stat_release(struct inode *inode, struct file *file)
+{
+	return 0;
+}
+
+struct file_operations mon_fops_stat = {
+	.open =		mon_stat_open,
+	.llseek =	no_llseek,
+	.read =		mon_stat_read,
+	/* .write =	mon_stat_write, */
+	/* .poll =		mon_stat_poll, */
+	/* .ioctl =	mon_stat_ioctl, */
+	.release =	mon_stat_release,
+};
diff -urpN -X dontdiff linux-2.6.10-rc3/drivers/usb/mon/mon_text.c linux-2.6.10-rc3-usb/drivers/usb/mon/mon_text.c
--- linux-2.6.10-rc3/drivers/usb/mon/mon_text.c	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.6.10-rc3-usb/drivers/usb/mon/mon_text.c	2004-12-19 21:14:55.726702896 -0800
@@ -0,0 +1,295 @@
+/*
+ * The USB Monitor, inspired by Dave Harding's USBMon.
+ *
+ * This is a text format reader.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/list.h>
+#include <linux/usb.h>
+#include <asm/uaccess.h>
+
+#include "usb_mon.h"
+
+/*
+ * The limit exists to prevent OOMs when the user process stops reading.
+ */
+#define EVENT_MAX  100
+
+#define PRINTF_DFL  100
+
+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 */
+	/* To add: timestamp */
+	int length;		/* Depends on type: xfer length or act length */
+	int status;
+};
+
+#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;
+	int printf_size;
+	char *printf_buf;
+	struct semaphore printf_lock;
+
+	char slab_name[SLAB_NAME_SZ];
+};
+
+static void mon_text_ctor(void *, kmem_cache_t *, unsigned long);
+static void mon_text_dtor(void *, kmem_cache_t *, unsigned long);
+
+/*
+ * May be called from an interrupt.
+ *
+ * This is called with the whole mon_bus locked, so no additional lock.
+ */
+static void mon_text_submit(void *data, struct urb *urb)
+{
+	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 = 'S';
+	ep->pipe = urb->pipe;
+	ep->id = (unsigned long) urb;
+	ep->length = urb->transfer_buffer_length;
+	ep->status = urb->status;	/* Let's watch what dirt ULPs supply */
+
+	rp->nevents++;
+	list_add_tail(&ep->e_link, &rp->e_list);
+	wake_up(&rp->wait);
+}
+
+/*
+ * May be called from an interrupt.
+ *
+ * This is called with the whole mon_bus locked, so no additional lock.
+ */
+static void mon_text_complete(void *data, struct urb *urb)
+{
+	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 = 'C';
+	ep->pipe = urb->pipe;
+	ep->id = (unsigned long) urb;
+	ep->length = urb->actual_length;
+	ep->status = urb->status;
+
+	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;
+
+	down(&mon_lock);
+	mbus = inode->u.generic_ip;
+	ubus = mbus->u_bus;
+
+	rp = kmalloc(sizeof(struct mon_reader_text), GFP_KERNEL);
+	if (rp == NULL) {
+		rc = -ENOMEM;
+		goto err_alloc;
+	}
+	memset(rp, 0, sizeof(struct mon_reader_text));
+	INIT_LIST_HEAD(&rp->e_list);
+	init_waitqueue_head(&rp->wait);
+	init_MUTEX(&rp->printf_lock);
+
+	rp->printf_size = PRINTF_DFL;
+	rp->printf_buf = kmalloc(rp->printf_size, GFP_KERNEL);
+	if (rp->printf_buf == NULL) {
+		rc = -ENOMEM;
+		goto err_alloc_pr;
+	}
+
+	rp->r.m_bus = mbus;
+	rp->r.r_data = rp;
+	rp->r.rnf_submit = mon_text_submit;
+	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, mon_text_dtor);
+	if (rp->e_slab == NULL) {
+		rc = -ENOMEM;
+		goto err_slab;
+	}
+
+	mon_reader_add(mbus, &rp->r);
+
+	file->private_data = rp;
+	up(&mon_lock);
+	return 0;
+
+// err_busy:
+//	kmem_cache_destroy(rp->e_slab);
+err_slab:
+	kfree(rp->printf_buf);
+err_alloc_pr:
+	kfree(rp);
+err_alloc:
+	up(&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_text_read(struct file *file, char __user *buf,
+				size_t nbytes, loff_t *ppos)
+{
+	struct mon_reader_text *rp = file->private_data;
+	struct mon_bus *mbus = rp->r.m_bus;
+	DECLARE_WAITQUEUE(waita, current);
+	struct mon_event_text *ep;
+	int cnt;
+
+	add_wait_queue(&rp->wait, &waita);
+	while ((ep = mon_text_fetch(rp, mbus)) == NULL) {
+		if (file->f_flags & O_NONBLOCK) {
+			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.
+		 */
+		set_current_state(TASK_INTERRUPTIBLE);
+		schedule();
+		if (signal_pending(current)) {
+			remove_wait_queue(&rp->wait, &waita);
+			return -EINTR;
+		}
+	}
+	remove_wait_queue(&rp->wait, &waita);
+
+	down(&rp->printf_lock);
+	cnt = snprintf(rp->printf_buf, rp->printf_size,
+	    "%lx 0 %c %08x %d %d\n",
+	    ep->id, ep->type, ep->pipe, ep->status, ep->length);
+	if (copy_to_user(buf, rp->printf_buf, cnt))
+		cnt = -EFAULT;
+	up(&rp->printf_lock);
+	kmem_cache_free(rp->e_slab, ep);
+	return cnt;
+}
+
+static int mon_text_release(struct inode *inode, struct file *file)
+{
+	struct mon_reader_text *rp = file->private_data;
+	struct mon_bus *mbus;
+	struct usb_bus *ubus;
+	unsigned long flags;
+	struct list_head *p;
+	struct mon_event_text *ep;
+
+	down(&mon_lock);
+	mbus = inode->u.generic_ip;
+	ubus = mbus->u_bus;
+
+	if (mbus->nreaders <= 0) {
+		printk(KERN_ERR TAG ": consistency error on close\n");
+		up(&mon_lock);
+		return 0;
+	}
+	mon_reader_del(mbus, &rp->r);
+
+	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);
+
+	up(&mon_lock);
+	return 0;
+}
+
+struct file_operations mon_fops_text = {
+	.open =		mon_text_open,
+	.llseek =	no_llseek,
+	.read =		mon_text_read,
+	/* .write =	mon_text_write, */
+	/* .poll =		mon_text_poll, */
+	/* .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));
+}
+
+static void mon_text_dtor(void *mem, kmem_cache_t *slab, unsigned long sflags)
+{
+	;
+}
diff -urpN -X dontdiff linux-2.6.10-rc3/drivers/usb/mon/usb_mon.h linux-2.6.10-rc3-usb/drivers/usb/mon/usb_mon.h
--- linux-2.6.10-rc3/drivers/usb/mon/usb_mon.h	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.6.10-rc3-usb/drivers/usb/mon/usb_mon.h	2004-12-19 14:07:51.351399563 -0800
@@ -0,0 +1,59 @@
+/*
+ * The USB Monitor, inspired by Dave Harding's USBMon.
+ */
+
+#ifndef __USB_MON_H
+#define __USB_MON_H
+
+#include <linux/list.h>
+#include <linux/slab.h>
+/* #include <linux/usb.h> */
+#include "../core/hcd.h"   /* XXX Move mon to core or move this to include/ */
+
+#define TAG "usbmon"
+
+struct mon_urb {
+	struct list_head link;
+	struct urb *urb;
+	// struct usb_dev *dev;
+};
+
+struct mon_bus {
+	struct list_head bus_link;
+	spinlock_t lock;
+	int nurbs;			/* Number of tracked URBs */
+	struct dentry *dent_s;		/* Debugging file */
+	struct dentry *dent_t;		/* Text interface file */
+	struct usb_bus *u_bus;
+
+	/* Ref */
+	int nreaders;
+	struct list_head r_list;	/* Chain of readers (usually one) */
+	struct usb_operations *saved_op;
+	struct usb_operations shim_ops;
+
+	/* Stats */
+	unsigned int cnt_text_lost;
+};
+
+/*
+ * An instance of a process which opened a file (but can fork later)
+ */
+struct mon_reader {
+	struct list_head r_link;
+	struct mon_bus *m_bus;
+	void *r_data;		/* Use container_of instead? */
+
+	void (*rnf_submit)(void *data, struct urb *urb);
+	void (*rnf_complete)(void *data, struct urb *urb);
+};
+
+void mon_reader_add(struct mon_bus *mbus, struct mon_reader *r);
+void mon_reader_del(struct mon_bus *mbus, struct mon_reader *r);
+
+extern struct semaphore mon_lock;
+
+extern struct file_operations mon_fops_text;
+extern struct file_operations mon_fops_stat;
+
+#endif /* __USB_MON_H */
diff -urpN -X dontdiff linux-2.6.10-rc3/include/linux/usb.h linux-2.6.10-rc3-usb/include/linux/usb.h
--- linux-2.6.10-rc3/include/linux/usb.h	2004-12-15 02:01:18.000000000 -0800
+++ linux-2.6.10-rc3-usb/include/linux/usb.h	2004-12-19 15:13:03.289200450 -0800
@@ -254,6 +254,7 @@ struct usb_bus {
 	u8 otg_port;			/* 0, or number of OTG/HNP port */
 	unsigned is_b_host:1;		/* true during some HNP roleswitches */
 	unsigned b_hnp_enable:1;	/* OTG: did A-Host enable HNP? */
+	unsigned monitored:1;		/* usbmon is active on this bus */
 
 	int devnum_next;		/* Next open device number in round-robin allocation */
 
@@ -277,6 +278,9 @@ struct usb_bus {
 
 	struct class_device class_dev;	/* class device for this bus */
 	void (*release)(struct usb_bus *bus);	/* function to destroy this bus's memory */
+#if defined(CONFIG_USB_MON) || defined(CONFIG_USB_MON_MODULE)
+	struct mon_bus *mon_bus;
+#endif
 };
 #define	to_usb_bus(d) container_of(d, struct usb_bus, class_dev)
 
@@ -804,6 +808,10 @@ struct urb
 	int error_count;		/* (return) number of ISO errors */
 	void *context;			/* (in) context for completion */
 	usb_complete_t complete;	/* (in) completion routine */
+#if defined(CONFIG_USB_MON) || defined(CONFIG_USB_MON_MODULE)
+	usb_complete_t mon_saved_complete;	/* (core private) */
+	struct mon_bus *mon_bus;		/* (core private) */
+#endif
 	struct usb_iso_packet_descriptor iso_frame_desc[0];	/* (in) ISO ONLY */
 };
 

^ permalink raw reply	[flat|nested] 15+ messages in thread
* RE: My vision of usbmon
@ 2004-12-23 21:02 Chad Kitching
  2005-01-11  1:36 ` Greg KH
  0 siblings, 1 reply; 15+ messages in thread
From: Chad Kitching @ 2004-12-23 21:02 UTC (permalink / raw)
  To: Greg KH, Pete Zaitcev; +Cc: linux-usb-devel, linux-kernel, laforge

> -----Original Message-----
> From: Greg KH [mailto:greg@kroah.com]
> Sent: December 21, 2004 11:11 PM
> Subject: Re: My vision of usbmon
> 
> -/* exported only within usbcore */
> -struct usb_bus *usb_bus_get (struct usb_bus *bus)
> +struct usb_bus *usb_bus_get(struct usb_bus *bus)
>  {
> -	struct class_device *tmp;
> -
> -	if (!bus)
> -		return NULL;
> -
> -	tmp = class_device_get(&bus->class_dev);
> -	if (tmp)        
> -		return to_usb_bus(tmp);
> -	else
> -		return NULL;
> +	if (bus)
> +		class_device_get(&bus->class_dev);
> +	return bus; 
>  }
> +EXPORT_SYMBOL_GPL(usb_bus_get);
  
I'm not familiar with this code, but if the replacement code is 
equivalent, is there any point to the return usb_bus pointer?  With 
the replacement, you should always get the same pointer you put 
into it.  If that is the case, why not remove the return value, and 
change drivers/usb/core/usb.c to match?

Chad Kitching

^ permalink raw reply	[flat|nested] 15+ messages in thread

end of thread, other threads:[~2005-01-11  1:40 UTC | newest]

Thread overview: 15+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2004-12-20  7:04 My vision of usbmon Pete Zaitcev
     [not found] ` <200412201255.59120.oliver@neukum.org>
2004-12-20 12:21   ` [linux-usb-devel] " Pete Zaitcev
2004-12-22  0:57 ` Greg KH
2004-12-22  1:29   ` Pete Zaitcev
2004-12-22  5:06     ` Greg KH
2004-12-22  5:10       ` Greg KH
2004-12-22  1:38   ` Nick Piggin
2004-12-22  5:04     ` Greg KH
2004-12-22  5:34       ` Jeff Garzik
2004-12-22  6:43         ` Greg KH
     [not found] ` <200412201525.52149.oliver@neukum.org>
2004-12-22  2:25   ` Pete Zaitcev
2004-12-22  5:03     ` Greg KH
2004-12-22 20:46     ` Oliver Neukum
2004-12-23 21:02 Chad Kitching
2005-01-11  1:36 ` Greg KH

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).