bitkeeper revision 1.1159.212.82 (4202dcc3h_cg7sFBNwdIyCk_2qkX0A)

Beginning a sprean clean of the USB code.

Lots of cleanups and fixes (thanks to Harry Butterworth for pointing out several
locking and allocation errors).

Signed-off-by: mark.williamson@cl.cam.ac.uk
This commit is contained in:
mwilli2@equilibrium.research committed 2005-02-04 02:24:03 +00:00
1 parent 58e8f23df1
commit 99be955033
1 file changed
+175 -141
@@ -4,7 +4,9 @@
* Backend for the Xen virtual USB driver - provides an abstraction of a
* USB host controller to the corresponding frontend driver.
*
* by Mark Williamson, Copyright (c) 2004 Intel Research Cambridge
* by Mark Williamson
* Copyright (c) 2004 Intel Research Cambridge
* Copyright (c) 2004, 2005 Mark Williamson
*
* Based on arch/xen/drivers/blkif/backend/main.c
* Copyright (c) 2003-2004, Keir Fraser & Steve Hand
@@ -39,7 +41,6 @@ static unsigned long mmap_vstart;
((_req) * MMAP_PAGES_PER_REQUEST * PAGE_SIZE) + \
((_seg) * PAGE_SIZE))
#define MIN(x,y) ( ( x < y ) ? x : y )
static spinlock_t owned_ports_lock;
LIST_HEAD(owned_ports);
@@ -83,7 +84,7 @@ typedef struct {
*/
static pending_req_t pending_reqs[MAX_PENDING_REQS];
static unsigned char pending_ring[MAX_PENDING_REQS];
static spinlock_t pend_prod_lock = SPIN_LOCK_UNLOCKED;
static spinlock_t pend_prod_lock;
/* NB. We use a different index type to differentiate from shared blk rings. */
typedef unsigned int PEND_RING_IDX;
@@ -100,20 +101,87 @@ static void dispatch_usb_io(usbif_priv_t *up, usbif_request_t *req);
static void dispatch_usb_reset(usbif_priv_t *up, unsigned long portid);
static owned_port_t *usbif_find_port(char *);
/******************************************************************
* PRIVATE DEBUG FUNCTIONS
*/
void dump_port(owned_port_t *p)
#undef DEBUG
#ifdef DEBUG
static void dump_port(owned_port_t *p)
{
printk("owned_port_t @ %p\n", p);
printk(" usbif_priv @ %p\n", p->usbif_priv);
printk(" path: %s\n", p->path);
printk(" guest_port: %d\n", p->guest_port);
printk(" guest_address: %ld\n", p->guest_address);
printk(" dev_present: %d\n", p->dev_present);
printk(" dev @ %p\n", p->dev);
printk(" ifaces: 0x%lx\n", p->ifaces);
printk(KERN_DEBUG "owned_port_t @ %p\n"
" usbif_priv @ %p\n"
" path: %s\n"
" guest_port: %d\n"
" guest_address: %ld\n"
" dev_present: %d\n"
" dev @ %p\n"
" ifaces: 0x%lx\n",
p, p->usbif_priv, p->path, p->guest_port, p->guest_address,
p->dev_present, p->dev, p->ifaces);
}
static void dump_request(usbif_request_t *req)
{
printk(KERN_DEBUG "id = 0x%lx\n"
"devnum %d\n"
"endpoint 0x%x\n"
"direction %d\n"
"speed %d\n"
"pipe_type 0x%x\n"
"transfer_buffer 0x%lx\n"
"length 0x%lx\n"
"transfer_flags 0x%lx\n"
"setup = { 0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x }\n"
"iso_schedule = 0x%lx\n"
"num_iso %ld\n",
req->id, req->devnum, req->endpoint, req->direction, req->speed,
req->pipe_type, req->transfer_buffer, req->length,
req->transfer_flags, req->setup[0], req->setup[1], req->setup[2],
req->setup[3], req->setup[4], req->setup[5], req->setup[6],
req->setup[7], req->iso_schedule, req->num_iso);
}
static void dump_urb(struct urb *urb)
{
printk(KERN_DEBUG "dumping urb @ %p\n", urb);
#define DUMP_URB_FIELD(name, format) \
printk(KERN_DEBUG " " # name " " format "\n", urb-> name)
DUMP_URB_FIELD(pipe, "0x%x");
DUMP_URB_FIELD(status, "%d");
DUMP_URB_FIELD(transfer_flags, "0x%x");
DUMP_URB_FIELD(transfer_buffer, "%p");
DUMP_URB_FIELD(transfer_buffer_length, "%d");
DUMP_URB_FIELD(actual_length, "%d");
}
static void dump_response(usbif_response_t *resp)
{
printk(KERN_DEBUG "usbback: Sending response:\n"
" id = 0x%x\n"
" op = %d\n"
" status = %d\n"
" data = %d\n"
" length = %d\n",
resp->id, resp->op, resp->status, resp->data, resp->length);
}
#else /* DEBUG */
#define dump_port(blah) ((void)0)
#define dump_request(blah) ((void)0)
#define dump_urb(blah) ((void)0)
#define dump_response(blah) ((void)0)
#endif /* DEBUG */
/******************************************************************
* MEMORY MANAGEMENT
*/
static void fast_flush_area(int idx, int nr_pages)
{
@@ -173,6 +241,15 @@ static void add_to_usbif_list_tail(usbif_priv_t *up)
spin_unlock_irqrestore(&usbio_schedule_list_lock, flags);
}
void free_pending(int pending_idx)
{
unsigned long flags;
/* Free the pending request. */
spin_lock_irqsave(&pend_prod_lock, flags);
pending_ring[MASK_PEND_IDX(pending_prod++)] = pending_idx;
spin_unlock_irqrestore(&pend_prod_lock, flags);
}
/******************************************************************
* COMPLETION CALLBACK -- Called as urb->complete()
@@ -182,17 +259,11 @@ static void maybe_trigger_usbio_schedule(void);
static void __end_usb_io_op(struct urb *purb)
{
unsigned long flags;
pending_req_t *pending_req;
int pending_idx;
pending_req = purb->context;
/* printk("Completed for id = %p to 0x%lx - 0x%lx\n", pending_req->id, */
/* virt_to_machine(purb->transfer_buffer), */
/* virt_to_machine(purb->transfer_buffer) */
/* + pending_req->nr_pages * PAGE_SIZE); */
pending_idx = pending_req - pending_reqs;
ASSERT(purb->actual_length <= purb->transfer_buffer_length);
@@ -201,7 +272,7 @@ static void __end_usb_io_op(struct urb *purb)
/* An error fails the entire request. */
if ( purb->status )
{
printk("URB @ %p failed. Status %d\n", purb, purb->status);
printk(KERN_WARNING "URB @ %p failed. Status %d\n", purb, purb->status);
}
if ( usb_pipetype(purb->pipe) == 0 )
@@ -211,8 +282,6 @@ static void __end_usb_io_op(struct urb *purb)
ASSERT(sched == pending_req->sched);
// printk("writing back schedule at %p\n", sched);
/* If we're dealing with an iso pipe, we need to copy back the schedule. */
for ( i = 0; i < purb->number_of_packets; i++ )
{
@@ -223,24 +292,18 @@ static void __end_usb_io_op(struct urb *purb)
}
}
// printk("Flushing %d pages\n", pending_req->nr_pages);
fast_flush_area(pending_req - pending_reqs, pending_req->nr_pages);
kfree(purb->setup_packet);
spin_lock_irqsave(&pending_req->usbif_priv->usb_ring_lock, flags);
make_response(pending_req->usbif_priv, pending_req->id,
pending_req->operation, pending_req->status, 0, purb->actual_length);
spin_unlock_irqrestore(&pending_req->usbif_priv->usb_ring_lock, flags);
usbif_put(pending_req->usbif_priv);
usb_free_urb(purb);
/* Free the pending request. */
spin_lock_irqsave(&pend_prod_lock, flags);
pending_ring[MASK_PEND_IDX(pending_prod++)] = pending_idx;
spin_unlock_irqrestore(&pend_prod_lock, flags);
free_pending(pending_idx);
rmb();
/* Check for anything still waiting in the rings, having freed a request... */
@@ -332,9 +395,6 @@ static int do_usb_io_op(usbif_priv_t *up, int max_to_do)
usbif_request_t *req;
USBIF_RING_IDX i, rp;
int more_to_do = 0;
unsigned long flags;
spin_lock_irqsave(&up->usb_ring_lock, flags);
rp = usb_ring->req_prod;
rmb(); /* Ensure we see queued requests up to 'rp'. */
@@ -377,8 +437,6 @@ static int do_usb_io_op(usbif_priv_t *up, int max_to_do)
up->usb_req_cons = i;
spin_unlock_irqrestore(&up->usb_ring_lock, flags);
return more_to_do;
}
@@ -412,11 +470,7 @@ static void dispatch_usb_reset(usbif_priv_t *up, unsigned long portid)
* than it's worth. We just fake it out in software but we will do a real
* reset when the interface is destroyed. */
#if 0
printk("Reset port %d\n", portid);
dump_port(port);
#endif
port->guest_address = 0;
/* If there's an attached device then the port is now enabled. */
@@ -438,8 +492,8 @@ static void dispatch_usb_probe(usbif_priv_t *up, unsigned long id, unsigned long
else
{
ret = -EINVAL;
printk("dispatch_usb_probe(): invalid port probe request (port %ld)\n",
portid);
printk(KERN_INFO "dispatch_usb_probe(): invalid port probe request "
"(port %ld)\n", portid);
}
/* Probe result is sent back in-band. Probes don't have an associated id
@@ -449,40 +503,6 @@ static void dispatch_usb_probe(usbif_priv_t *up, unsigned long id, unsigned long
owned_port_t *find_port_for_request(usbif_priv_t *up, usbif_request_t *req);
static void dump_request(usbif_request_t *req)
{
printk("id = 0x%lx\n", req->id);
printk("devnum %d\n", req->devnum);
printk("endpoint 0x%x\n", req->endpoint);
printk("direction %d\n", req->direction);
printk("speed %d\n", req->speed);
printk("pipe_type 0x%x\n", req->pipe_type);
printk("transfer_buffer 0x%lx\n", req->transfer_buffer);
printk("length 0x%lx\n", req->length);
printk("transfer_flags 0x%lx\n", req->transfer_flags);
printk("setup = { 0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x\n",
req->setup[0], req->setup[1], req->setup[2], req->setup[3],
req->setup[4], req->setup[5], req->setup[6], req->setup[7]);
printk("iso_schedule = 0x%lx\n", req->iso_schedule);
printk("num_iso %ld\n", req->num_iso);
}
void dump_urb(struct urb *urb)
{
printk("dumping urb @ %p\n", urb);
#define DUMP_URB_FIELD(name, format) printk(" " # name " " format "\n", urb-> name)
DUMP_URB_FIELD(pipe, "0x%x");
DUMP_URB_FIELD(status, "%d");
DUMP_URB_FIELD(transfer_flags, "0x%x");
DUMP_URB_FIELD(transfer_buffer, "%p");
DUMP_URB_FIELD(transfer_buffer_length, "%d");
DUMP_URB_FIELD(actual_length, "%d");
}
static void dispatch_usb_io(usbif_priv_t *up, usbif_request_t *req)
{
unsigned long buffer_mach;
@@ -495,27 +515,36 @@ static void dispatch_usb_io(usbif_priv_t *up, usbif_request_t *req)
owned_port_t *port;
unsigned char *setup;
// dump_request(req);
dump_request(req);
if ( NR_PENDING_REQS == MAX_PENDING_REQS )
{
printk("usbback: Max requests already queued. Now giving up!\n");
printk(KERN_WARNING "usbback: Max requests already queued. "
"Giving up!\n");
return;
}
port = find_port_for_request(up, req);
if(port == NULL)
if ( port == NULL )
{
printk("No such device! (%d)\n", req->devnum);
printk(KERN_WARNING "No such device! (%d)\n", req->devnum);
dump_request(req);
make_response(up, req->id, req->operation, -ENODEV, 0, 0);
return;
}
else if ( !port->dev_present )
{
/* In normal operation, we'll only get here if a device is unplugged
* and the frontend hasn't noticed yet. */
make_response(up, req->id, req->operation, -ENODEV, 0, 0);
return;
}
setup = kmalloc(8, GFP_ATOMIC | GFP_NOIO);
setup = kmalloc(8, GFP_KERNEL);
if ( setup == NULL )
goto no_mem;
@@ -552,7 +581,7 @@ static void dispatch_usb_io(usbif_priv_t *up, usbif_request_t *req)
/* Ignore configuration setting and hope that the host kernel
did it right. */
// usb_set_configuration(port->dev, setup[2]);
/* usb_set_configuration(port->dev, setup[2]); */
make_response(up, req->id, req->operation, 0, 0, 0);
@@ -579,7 +608,8 @@ static void dispatch_usb_io(usbif_priv_t *up, usbif_request_t *req)
+ req->length )
> MMAP_PAGES_PER_REQUEST * PAGE_SIZE )
{
printk("usbback: request of %d bytes too large, failing it\n", req->length);
printk(KERN_WARNING "usbback: request of %lu bytes too large\n",
req->length);
make_response(up, req->id, req->operation, -EINVAL, 0, 0);
kfree(setup);
return;
@@ -599,8 +629,6 @@ static void dispatch_usb_io(usbif_priv_t *up, usbif_request_t *req)
for ( i = 0, offset = 0; offset < req->length;
i++, offset += PAGE_SIZE )
{
// printk("length = %d, offset = %d, looping!\n", req->length, offset);
mcl[i].op = __HYPERVISOR_update_va_mapping_otherdomain;
mcl[i].args[0] = MMAP_VADDR(pending_idx, i) >> PAGE_SHIFT;
mcl[i].args[1] = ((buffer_mach & PAGE_MASK) + offset) | remap_prot;
@@ -609,7 +637,6 @@ static void dispatch_usb_io(usbif_priv_t *up, usbif_request_t *req)
phys_to_machine_mapping[__pa(MMAP_VADDR(pending_idx, i))>>PAGE_SHIFT] =
FOREIGN_FRAME((buffer_mach + offset) >> PAGE_SHIFT);
// printk("i = %d\n", i);
ASSERT(virt_to_machine(MMAP_VADDR(pending_idx, i))
== buffer_mach + i << PAGE_SHIFT);
@@ -617,7 +644,6 @@ static void dispatch_usb_io(usbif_priv_t *up, usbif_request_t *req)
if ( req->pipe_type == 0 && req->num_iso > 0 ) /* Maybe schedule ISO... */
{
// printk("for iso, i = %d\n", i);
/* Map in ISO schedule, if necessary. */
mcl[i].op = __HYPERVISOR_update_va_mapping_otherdomain;
mcl[i].args[0] = MMAP_VADDR(pending_idx, i) >> PAGE_SHIFT;
@@ -628,12 +654,9 @@ static void dispatch_usb_io(usbif_priv_t *up, usbif_request_t *req)
phys_to_machine_mapping[__pa(MMAP_VADDR(pending_idx, i))>>PAGE_SHIFT] =
FOREIGN_FRAME(req->iso_schedule >> PAGE_SHIFT);
// printk("Mapped iso at %p\n", MMAP_VADDR(pending_idx, i));
i++;
}
// printk("Well we got this far!\n");
if ( unlikely(HYPERVISOR_multicall(mcl, i) != 0) )
BUG();
@@ -643,9 +666,9 @@ static void dispatch_usb_io(usbif_priv_t *up, usbif_request_t *req)
{
if ( unlikely(mcl[j].args[5] != 0) )
{
printk("invalid buffer %d -- could not remap it\n", j);
printk(KERN_WARNING
"invalid buffer %d -- could not remap it\n", j);
fast_flush_area(pending_idx, i);
printk("sending invalid descriptor\n");
goto bad_descriptor;
}
}
@@ -663,8 +686,6 @@ static void dispatch_usb_io(usbif_priv_t *up, usbif_request_t *req)
pending_req->operation = req->operation;
pending_req->nr_pages = i;
pending_cons++;
usbif_get(up);
@@ -673,20 +694,22 @@ static void dispatch_usb_io(usbif_priv_t *up, usbif_request_t *req)
purb = usb_alloc_urb(req->num_iso);
if ( purb == NULL )
{
usbif_put(up);
free_pending(pending_idx);
goto no_mem;
}
purb->dev = port->dev;
purb->context = pending_req;
purb->transfer_buffer = (void *)MMAP_VADDR(pending_idx, 0) + (buffer_mach & ~PAGE_MASK);
purb->transfer_buffer =
(void *)MMAP_VADDR(pending_idx, 0) + (buffer_mach & ~PAGE_MASK);
if(buffer_mach == 0)
purb->transfer_buffer = NULL;
purb->complete = __end_usb_io_op;
purb->transfer_buffer_length = req->length;
purb->transfer_flags = req->transfer_flags;
/* if ( req->transfer_flags != 0 ) */
/* dump_request(req); */
purb->pipe = 0;
purb->pipe |= req->direction << 7;
purb->pipe |= port->dev->devnum << 8;
@@ -707,8 +730,6 @@ static void dispatch_usb_io(usbif_priv_t *up, usbif_request_t *req)
int j;
usbif_iso_t *iso_sched = (usbif_iso_t *)MMAP_VADDR(pending_idx, i - 1);
// printk("Reading iso sched at %p\n", iso_sched);
/* If we're dealing with an iso pipe, we need to copy in a schedule. */
for ( j = 0; j < req->num_iso; j++ )
{
@@ -720,13 +741,17 @@ static void dispatch_usb_io(usbif_priv_t *up, usbif_request_t *req)
}
{
int ret;
ret = usb_submit_urb(purb);
// dump_urb(purb);
if ( ret != 0 )
goto bad_descriptor; /* XXX free pending here! */
int ret;
ret = usb_submit_urb(purb);
dump_urb(purb);
if ( ret != 0 )
{
usbif_put(up);
free_pending(pending_idx);
goto bad_descriptor;
}
}
return;
@@ -759,15 +784,6 @@ static void make_response(usbif_priv_t *up, unsigned long id,
usbif_response_t *resp;
unsigned long flags;
#if 0
printk("usbback: Sending response:\n");
printk(" id = 0x%x\n", id);
printk(" op = %d\n", op);
printk(" status = %d\n", st);
printk(" data = %d\n", inband);
printk(" length = %d\n", length);
#endif
/* Place on the response ring for the relevant domain. */
spin_lock_irqsave(&up->usb_ring_lock, flags);
resp = &up->usb_ring_base->
@@ -778,6 +794,9 @@ static void make_response(usbif_priv_t *up, unsigned long id,
resp->data = inband;
resp->length = length;
wmb(); /* Ensure other side can see the response fields. */
dump_response(resp);
up->usb_ring_base->resp_prod = ++up->usb_resp_prod;
spin_unlock_irqrestore(&up->usb_ring_lock, flags);
@@ -798,16 +817,17 @@ int usbif_claim_port(usbif_be_claim_port_t *msg)
/* Sanity... */
if ( usbif_find_port(msg->path) != NULL )
{
printk("usbback: Attempted to claim USB port "
printk(KERN_WARNING "usbback: Attempted to claim USB port "
"we already own!\n");
return -EINVAL;
}
spin_lock_irq(&owned_ports_lock);
/* No need for a slab cache - this should be infrequent. */
o_p = kmalloc(sizeof(owned_port_t), GFP_KERNEL);
if ( o_p == NULL )
return -ENOMEM;
o_p->enabled = 0;
o_p->usbif_priv = usbif_find(msg->domid);
o_p->guest_port = msg->usbif_port;
@@ -816,13 +836,15 @@ int usbif_claim_port(usbif_be_claim_port_t *msg)
strcpy(o_p->path, msg->path);
spin_lock_irq(&owned_ports_lock);
list_add(&o_p->list, &owned_ports);
printk("usbback: Claimed USB port (%s) for %d.%d\n", o_p->path,
msg->domid, msg->usbif_port);
spin_unlock_irq(&owned_ports_lock);
printk(KERN_INFO "usbback: Claimed USB port (%s) for %d.%d\n", o_p->path,
msg->domid, msg->usbif_port);
/* Force a reprobe for unclaimed devices. */
usb_scan_devices();
@@ -843,11 +865,9 @@ owned_port_t *find_port_for_request(usbif_priv_t *up, usbif_request_t *req)
owned_port_t *p = list_entry(port, owned_port_t, list);
if(p->usbif_priv == up && p->guest_address == req->devnum && p->enabled )
{
#if 0
printk("Found port for devnum %d\n", req->devnum);
dump_port(p);
#endif
spin_unlock_irqrestore(&owned_ports_lock, flags);
return p;
}
}
@@ -856,29 +876,37 @@ owned_port_t *find_port_for_request(usbif_priv_t *up, usbif_request_t *req)
return NULL;
}
owned_port_t *usbif_find_port(char *path)
owned_port_t *__usbif_find_port(char *path)
{
struct list_head *port;
unsigned long flags;
spin_lock_irqsave(&owned_ports_lock, flags);
list_for_each(port, &owned_ports)
{
owned_port_t *p = list_entry(port, owned_port_t, list);
if(!strcmp(path, p->path))
{
spin_unlock_irqrestore(&owned_ports_lock, flags);
return p;
}
}
spin_unlock_irqrestore(&owned_ports_lock, flags);
return NULL;
}
owned_port_t *usbif_find_port(char *path)
{
owned_port_t *ret;
unsigned long flags;
spin_lock_irqsave(&owned_ports_lock, flags);
ret = __usbif_find_port(path);
spin_unlock_irqrestore(&owned_ports_lock, flags);
return ret;
}
static void *probe(struct usb_device *dev, unsigned iface,
const struct usb_device_id *id)
const struct usb_device_id *id)
{
owned_port_t *p;
@@ -887,7 +915,7 @@ static void *probe(struct usb_device *dev, unsigned iface,
* the device actually is ;-) */
if ( ( p = usbif_find_port(dev->devpath) ) != NULL )
{
printk("usbback: claimed device attached to owned port\n");
printk(KERN_INFO "usbback: claimed device attached to owned port\n");
p->dev_present = 1;
p->dev = dev;
@@ -896,7 +924,8 @@ static void *probe(struct usb_device *dev, unsigned iface,
return p->usbif_priv;
}
else
printk("usbback: hotplug for non-owned port (%s), ignoring\n", dev->devpath);
printk(KERN_INFO "usbback: hotplug for non-owned port (%s), ignoring\n",
dev->devpath);
return NULL;
@@ -938,6 +967,10 @@ void __usbif_release_port(owned_port_t *p)
* drivers in this kernel because we assume the device is completely under
* the control of ourselves (i.e. the guest!). This should ensure that the
* device is in a sane state for the next customer ;-) */
/* MAW NB: we're not resetting the real device here. This looks perfectly
* valid to me but it causes memory corruption. We seem to get away with not
* resetting for now, although it'd be nice to have this tracked down. */
/* if ( p->dev != NULL) */
/* usb_reset_device(p->dev); */
@@ -953,7 +986,7 @@ void usbif_release_port(usbif_be_release_port_t *msg)
owned_port_t *p;
spin_lock_irq(&owned_ports_lock);
p = usbif_find_port(msg->path);
p = __usbif_find_port(msg->path);
__usbif_release_port(p);
spin_unlock_irq(&owned_ports_lock);
}
@@ -981,12 +1014,6 @@ static int __init usbif_init(void)
!(xen_start_info.flags & SIF_USB_BE_DOMAIN) )
return 0;
INIT_LIST_HEAD(&owned_ports);
usb_register(&driver);
usbif_interface_init();
if ( (mmap_vstart = allocate_empty_lowmem_region(MMAP_PAGES)) == 0 )
BUG();
@@ -996,17 +1023,24 @@ static int __init usbif_init(void)
for ( i = 0; i < MAX_PENDING_REQS; i++ )
pending_ring[i] = i;
spin_lock_init(&pend_prod_lock);
spin_lock_init(&owned_ports_lock);
INIT_LIST_HEAD(&owned_ports);
spin_lock_init(&usbio_schedule_list_lock);
INIT_LIST_HEAD(&usbio_schedule_list);
if ( kernel_thread(usbio_schedule, 0, CLONE_FS | CLONE_FILES) < 0 )
BUG();
usbif_interface_init();
usbif_ctrlif_init();
spin_lock_init(&owned_ports_lock);
usb_register(&driver);
printk("Xen USB Backend Initialised");
printk(KERN_INFO "Xen USB Backend Initialised");
return 0;
}
Reference in new issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.