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

Reply via email to