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:
committed by
Greg Kroah-Hartman
parent
a8ef36bc0a
commit
6f23ee1fef
@ -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
1172
drivers/usb/mon/mon_bin.c
Normal file
File diff suppressed because it is too large
Load Diff
@ -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 */
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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 */
|
||||
|
Reference in New Issue
Block a user