My fix for a problem in usb-ohci in 2.4.28 was simply broken.
The original problem was that the interrupt handler referenced an area below
a process' stack pointer, which worked everywhere except ia64. On ia86 such
practice causes a CPU trap.
I "fixed" it by moving the wait queue head to a place ... which was
freed immediately afterwards. I really have no idea what I was thinking.
It's not even a race of any sort, the code references a freed area
completely in sequence.
This patch is a sort of a two-step. First, it undoes what I did and returns
to the code in 2.4.27. Next, it puts a spinlock around the remove_wait_queue.
This ought to be safe for the vast majority of systems, and possibly fixes
the issue with ia64 (it was sort of hard to reproduce: it only happened on
an Altix).
My testing was light, but since the fix mostly reverts the broken patch,
I think it can go into the next test, -pre, or -rc release.
-- Pete
diff -urp -X dontdiff linux-2.4.32-rc1/drivers/usb/host/usb-ohci.c
linux-2.4.32-rc1-usb/drivers/usb/host/usb-ohci.c
--- linux-2.4.32-rc1/drivers/usb/host/usb-ohci.c 2005-01-25
11:17:24.000000000 -0800
+++ linux-2.4.32-rc1-usb/drivers/usb/host/usb-ohci.c 2005-10-22
12:45:12.000000000 -0700
@@ -677,7 +677,6 @@ static int sohci_submit_urb (struct urb
return -ENOMEM;
}
memset (urb_priv, 0, sizeof (urb_priv_t) + size * sizeof (td_t *));
- init_waitqueue_head (&urb_priv->wait);
/* fill the private part of the URB */
urb_priv->length = size;
@@ -824,10 +823,12 @@ static int sohci_unlink_urb (struct urb
urb_priv->ed->state |= ED_URB_DEL;
if (!(urb->transfer_flags & USB_ASYNC_UNLINK)) {
+ DECLARE_WAIT_QUEUE_HEAD (unlink_wakeup);
DECLARE_WAITQUEUE (wait, current);
int timeout = OHCI_UNLINK_TIMEOUT;
- add_wait_queue(&urb_priv->wait, &wait);
+ add_wait_queue (&unlink_wakeup, &wait);
+ urb_priv->wait = &unlink_wakeup;
spin_unlock_irqrestore(&ohci->ohci_lock, flags);
/* wait until all TDs are deleted */
@@ -840,12 +841,10 @@ static int sohci_unlink_urb (struct urb
/*
* A waitqueue head is self-locked, but we try
- * to interlock with the dl_del_urb() which may
- * be doing wake_up() right now, least
- * urb->complete poisons over the urb->wait.
+ * to interlock with the dl_del_urb().
*/
spin_lock_irqsave(&ohci->ohci_lock, flags);
- remove_wait_queue(&urb_priv->wait, &wait);
+ remove_wait_queue(&unlink_wakeup, &wait);
spin_unlock_irqrestore(&ohci->ohci_lock, flags);
if (urb->status == USB_ST_URB_PENDING) {
err ("unlink URB timeout");
@@ -1566,7 +1565,7 @@ static void dl_transfer_length(td_t * td
static void dl_del_urb (ohci_t *ohci, struct urb * urb)
{
- urb_priv_t * urb_priv = urb->hcpriv;
+ wait_queue_head_t * wait_head = ((urb_priv_t *)(urb->hcpriv))->wait;
urb_rm_priv_locked (urb);
@@ -1577,7 +1576,8 @@ static void dl_del_urb (ohci_t *ohci, st
urb->status = -ENOENT;
/* unblock sohci_unlink_urb */
- wake_up(&urb_priv->wait);
+ if (wait_head)
+ wake_up (wait_head);
}
}
diff -urp -X dontdiff linux-2.4.32-rc1/drivers/usb/host/usb-ohci.h
linux-2.4.32-rc1-usb/drivers/usb/host/usb-ohci.h
--- linux-2.4.32-rc1/drivers/usb/host/usb-ohci.h 2005-06-04
13:45:04.000000000 -0700
+++ linux-2.4.32-rc1-usb/drivers/usb/host/usb-ohci.h 2005-10-22
12:42:14.000000000 -0700
@@ -336,7 +336,7 @@ typedef struct
__u16 length; // number of tds associated with this request
__u16 td_cnt; // number of tds already serviced
int state;
- wait_queue_head_t wait;
+ wait_queue_head_t * wait;
td_t * td[0]; // list pointer to all corresponding TDs associated
with this request
} urb_priv_t;
-------------------------------------------------------
This SF.Net email is sponsored by the JBoss Inc.
Get Certified Today * Register for a JBoss Training Course
Free Certification Exam for All Training Attendees Through End of 2005
Visit http://www.jboss.com/services/certification for more information
_______________________________________________
[email protected]
To unsubscribe, use the last form field at:
https://lists.sourceforge.net/lists/listinfo/linux-usb-devel