USB: add binary API to usbmon

This patch adds a new, "binary" API in addition to the old, text API usbmon
had before. The new API allows for less CPU use, and it allows to capture
all data from a packet where old API only captured 32 bytes at most. There
are some limitations and conditions to this, e.g. in case someone constructs
a URB with 1GB of data, it's not likely to be captured, because even the
huge buffers of the new reader are finite. Nonetheless, I expect this new
capability to capture all data for all real life scenarios.

The downside is, a special user mode application is required where cat(1)
worked before. I have sample code at http://people.redhat.com/zaitcev/linux/
and Paolo Abeni is working on patching libpcap.

This patch was initially written by Paolo and later I tweaked it, and
we had a little back-and-forth. So this is a jointly authored patch, but
I am submitting this I am responsible for the bugs.

Signed-off-by: Paolo Abeni <paolo.abeni@email.it>
Signed-off-by: Pete Zaitcev <zaitcev@redhat.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
This commit is contained in:
Pete Zaitcev
2006-12-30 22:43:10 -08:00
committed by Greg Kroah-Hartman
parent a8ef36bc0a
commit 6f23ee1fef
7 changed files with 1502 additions and 57 deletions

View File

@ -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_bin.o mon_dma.o
# This does not use CONFIG_USB_MON because we want this to use a tristate.
obj-$(CONFIG_USB) += usbmon.o

1172
drivers/usb/mon/mon_bin.c Normal file

File diff suppressed because it is too large Load Diff

View File

@ -48,6 +48,36 @@ char mon_dmapeek(unsigned char *dst, dma_addr_t dma_addr, int len)
local_irq_restore(flags);
return 0;
}
void mon_dmapeek_vec(const struct mon_reader_bin *rp,
unsigned int offset, dma_addr_t dma_addr, unsigned int length)
{
unsigned long flags;
unsigned int step_len;
struct page *pg;
unsigned char *map;
unsigned long page_off, page_len;
local_irq_save(flags);
while (length) {
/* compute number of bytes we are going to copy in this page */
step_len = length;
page_off = dma_addr & (PAGE_SIZE-1);
page_len = PAGE_SIZE - page_off;
if (page_len < step_len)
step_len = page_len;
/* copy data and advance pointers */
pg = phys_to_page(dma_addr);
map = kmap_atomic(pg, KM_IRQ0);
offset = mon_copy_to_buff(rp, offset, map + page_off, step_len);
kunmap_atomic(map, KM_IRQ0);
dma_addr += step_len;
length -= step_len;
}
local_irq_restore(flags);
}
#endif /* __i386__ */
#ifndef MON_HAS_UNMAP
@ -55,4 +85,11 @@ char mon_dmapeek(unsigned char *dst, dma_addr_t dma_addr, int len)
{
return 'D';
}
#endif
void mon_dmapeek_vec(const struct mon_reader_bin *rp,
unsigned int offset, dma_addr_t dma_addr, unsigned int length)
{
;
}
#endif /* MON_HAS_UNMAP */

View File

@ -9,7 +9,6 @@
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/usb.h>
#include <linux/debugfs.h>
#include <linux/smp_lock.h>
#include <linux/notifier.h>
#include <linux/mutex.h>
@ -22,11 +21,10 @@ static void mon_complete(struct usb_bus *ubus, struct urb *urb);
static void mon_stop(struct mon_bus *mbus);
static void mon_dissolve(struct mon_bus *mbus, struct usb_bus *ubus);
static void mon_bus_drop(struct kref *r);
static void mon_bus_init(struct dentry *mondir, struct usb_bus *ubus);
static void mon_bus_init(struct usb_bus *ubus);
DEFINE_MUTEX(mon_lock);
static struct dentry *mon_dir; /* /dbg/usbmon */
static LIST_HEAD(mon_buses); /* All buses we know: struct mon_bus */
/*
@ -200,7 +198,7 @@ static void mon_stop(struct mon_bus *mbus)
*/
static void mon_bus_add(struct usb_bus *ubus)
{
mon_bus_init(mon_dir, ubus);
mon_bus_init(ubus);
}
/*
@ -212,8 +210,8 @@ static void mon_bus_remove(struct usb_bus *ubus)
mutex_lock(&mon_lock);
list_del(&mbus->bus_link);
debugfs_remove(mbus->dent_t);
debugfs_remove(mbus->dent_s);
if (mbus->text_inited)
mon_text_del(mbus);
mon_dissolve(mbus, ubus);
kref_put(&mbus->ref, mon_bus_drop);
@ -281,13 +279,9 @@ static void mon_bus_drop(struct kref *r)
* - refcount USB bus struct
* - link
*/
static void mon_bus_init(struct dentry *mondir, struct usb_bus *ubus)
static void mon_bus_init(struct usb_bus *ubus)
{
struct dentry *d;
struct mon_bus *mbus;
enum { NAMESZ = 10 };
char name[NAMESZ];
int rc;
if ((mbus = kzalloc(sizeof(struct mon_bus), GFP_KERNEL)) == NULL)
goto err_alloc;
@ -303,57 +297,54 @@ static void mon_bus_init(struct dentry *mondir, struct usb_bus *ubus)
ubus->mon_bus = mbus;
mbus->uses_dma = ubus->uses_dma;
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;
mbus->text_inited = mon_text_add(mbus, ubus);
// mon_bin_add(...)
mutex_lock(&mon_lock);
list_add_tail(&mbus->bus_link, &mon_buses);
mutex_unlock(&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;
}
/*
* Search a USB bus by number. Notice that USB bus numbers start from one,
* which we may later use to identify "all" with zero.
*
* This function must be called with mon_lock held.
*
* This is obviously inefficient and may be revised in the future.
*/
struct mon_bus *mon_bus_lookup(unsigned int num)
{
struct list_head *p;
struct mon_bus *mbus;
list_for_each (p, &mon_buses) {
mbus = list_entry(p, struct mon_bus, bus_link);
if (mbus->u_bus->busnum == num) {
return mbus;
}
}
return NULL;
}
static int __init mon_init(void)
{
struct usb_bus *ubus;
struct dentry *mondir;
int rc;
mondir = debugfs_create_dir("usbmon", NULL);
if (IS_ERR(mondir)) {
printk(KERN_NOTICE TAG ": debugfs is not available\n");
return -ENODEV;
}
if (mondir == NULL) {
printk(KERN_NOTICE TAG ": unable to create usbmon directory\n");
return -ENODEV;
}
mon_dir = mondir;
if ((rc = mon_text_init()) != 0)
goto err_text;
if ((rc = mon_bin_init()) != 0)
goto err_bin;
if (usb_mon_register(&mon_ops_0) != 0) {
printk(KERN_NOTICE TAG ": unable to register with the core\n");
debugfs_remove(mondir);
return -ENODEV;
rc = -ENODEV;
goto err_reg;
}
// MOD_INC_USE_COUNT(which_module?);
@ -361,10 +352,17 @@ static int __init mon_init(void)
mutex_lock(&usb_bus_list_lock);
list_for_each_entry (ubus, &usb_bus_list, bus_list) {
mon_bus_init(mondir, ubus);
mon_bus_init(ubus);
}
mutex_unlock(&usb_bus_list_lock);
return 0;
err_reg:
mon_bin_exit();
err_bin:
mon_text_exit();
err_text:
return rc;
}
static void __exit mon_exit(void)
@ -381,8 +379,8 @@ static void __exit mon_exit(void)
mbus = list_entry(p, struct mon_bus, bus_link);
list_del(p);
debugfs_remove(mbus->dent_t);
debugfs_remove(mbus->dent_s);
if (mbus->text_inited)
mon_text_del(mbus);
/*
* This never happens, because the open/close paths in
@ -401,7 +399,8 @@ static void __exit mon_exit(void)
}
mutex_unlock(&mon_lock);
debugfs_remove(mon_dir);
mon_text_exit();
mon_bin_exit();
}
module_init(mon_init);

View File

@ -9,6 +9,7 @@
#include <linux/usb.h>
#include <linux/time.h>
#include <linux/mutex.h>
#include <linux/debugfs.h>
#include <asm/uaccess.h>
#include "usb_mon.h"
@ -63,6 +64,8 @@ struct mon_reader_text {
char slab_name[SLAB_NAME_SZ];
};
static struct dentry *mon_dir; /* Usually /sys/kernel/debug/usbmon */
static void mon_text_ctor(void *, struct kmem_cache *, unsigned long);
/*
@ -436,7 +439,7 @@ static int mon_text_release(struct inode *inode, struct file *file)
return 0;
}
const struct file_operations mon_fops_text = {
static const struct file_operations mon_fops_text = {
.owner = THIS_MODULE,
.open = mon_text_open,
.llseek = no_llseek,
@ -447,6 +450,47 @@ const struct file_operations mon_fops_text = {
.release = mon_text_release,
};
int mon_text_add(struct mon_bus *mbus, const struct usb_bus *ubus)
{
struct dentry *d;
enum { NAMESZ = 10 };
char name[NAMESZ];
int rc;
rc = snprintf(name, NAMESZ, "%dt", ubus->busnum);
if (rc <= 0 || rc >= NAMESZ)
goto err_print_t;
d = debugfs_create_file(name, 0600, mon_dir, mbus, &mon_fops_text);
if (d == NULL)
goto err_create_t;
mbus->dent_t = d;
/* XXX The stats do not belong to here (text API), but oh well... */
rc = snprintf(name, NAMESZ, "%ds", ubus->busnum);
if (rc <= 0 || rc >= NAMESZ)
goto err_print_s;
d = debugfs_create_file(name, 0600, mon_dir, mbus, &mon_fops_stat);
if (d == NULL)
goto err_create_s;
mbus->dent_s = d;
return 1;
err_create_s:
err_print_s:
debugfs_remove(mbus->dent_t);
mbus->dent_t = NULL;
err_create_t:
err_print_t:
return 0;
}
void mon_text_del(struct mon_bus *mbus)
{
debugfs_remove(mbus->dent_t);
debugfs_remove(mbus->dent_s);
}
/*
* Slab interface: constructor.
*/
@ -459,3 +503,24 @@ static void mon_text_ctor(void *mem, struct kmem_cache *slab, unsigned long sfla
memset(mem, 0xe5, sizeof(struct mon_event_text));
}
int __init mon_text_init(void)
{
struct dentry *mondir;
mondir = debugfs_create_dir("usbmon", NULL);
if (IS_ERR(mondir)) {
printk(KERN_NOTICE TAG ": debugfs is not available\n");
return -ENODEV;
}
if (mondir == NULL) {
printk(KERN_NOTICE TAG ": unable to create usbmon directory\n");
return -ENODEV;
}
mon_dir = mondir;
return 0;
}
void __exit mon_text_exit(void)
{
debugfs_remove(mon_dir);
}

View File

@ -17,9 +17,11 @@
struct mon_bus {
struct list_head bus_link;
spinlock_t lock;
struct usb_bus *u_bus;
int text_inited;
struct dentry *dent_s; /* Debugging file */
struct dentry *dent_t; /* Text interface file */
struct usb_bus *u_bus;
int uses_dma;
/* Ref */
@ -48,13 +50,35 @@ struct mon_reader {
void mon_reader_add(struct mon_bus *mbus, struct mon_reader *r);
void mon_reader_del(struct mon_bus *mbus, struct mon_reader *r);
struct mon_bus *mon_bus_lookup(unsigned int num);
int /*bool*/ mon_text_add(struct mon_bus *mbus, const struct usb_bus *ubus);
void mon_text_del(struct mon_bus *mbus);
// void mon_bin_add(struct mon_bus *);
int __init mon_text_init(void);
void __exit mon_text_exit(void);
int __init mon_bin_init(void);
void __exit mon_bin_exit(void);
/*
*/
* DMA interface.
*
* XXX The vectored side needs a serious re-thinking. Abstracting vectors,
* like in Paolo's original patch, produces a double pkmap. We need an idea.
*/
extern char mon_dmapeek(unsigned char *dst, dma_addr_t dma_addr, int len);
struct mon_reader_bin;
extern void mon_dmapeek_vec(const struct mon_reader_bin *rp,
unsigned int offset, dma_addr_t dma_addr, unsigned int len);
extern unsigned int mon_copy_to_buff(const struct mon_reader_bin *rp,
unsigned int offset, const unsigned char *from, unsigned int len);
/*
*/
extern struct mutex mon_lock;
extern const struct file_operations mon_fops_text;
extern const struct file_operations mon_fops_stat;
#endif /* __USB_MON_H */