ChangeSet 1.2223.2.6, 2004/11/24 15:13:17-08:00, [EMAIL PROTECTED]

[PATCH] USB: usb_sg_*() unlink deadlock fix

This would be rare with HCDs that maintain chains of DMA
transfers, except if the HC dies in the middle of an I/O
request; so no rush to merge this.  It'd happen in a PIO
based HCD though ... :)


Async unlink of an URB from an endpoint's I/O queue _normally_ involves a
delay from handshaking with the host controller, to be sure the DMA queue
is inactive.  So urb->complete() runs after usb_unlink_urb() returns, and
from a different context.  But not always...

The completion may run immediately whenever the HCD knows that HC isn't
busy with the URB.  Maybe that HCD is in a HALT state, or the endpoint
queue is is temporarily off-schedule (halted, or dead after PM resume
from D3cold, etc) ... or maybe the HCD doesn't use DMA, so most unlinks
just list_del_init() and return.

This makes usb_sg_cancel() and sg_complete() drop the io->lock when they
cancel active urbs, preventing potential self-deadlock when that completion
handler runs immediately.

Signed-off-by: David Brownell <[EMAIL PROTECTED]>
Signed-off-by: Greg Kroah-Hartman <[EMAIL PROTECTED]>


 drivers/usb/core/message.c |    7 ++++++-
 1 files changed, 6 insertions(+), 1 deletion(-)


diff -Nru a/drivers/usb/core/message.c b/drivers/usb/core/message.c
--- a/drivers/usb/core/message.c        2004-11-30 15:44:15 -08:00
+++ b/drivers/usb/core/message.c        2004-11-30 15:44:15 -08:00
@@ -243,14 +243,16 @@
                // BUG ();
        }
 
-       if (urb->status && urb->status != -ECONNRESET) {
+       if (io->status == 0 && urb->status && urb->status != -ECONNRESET) {
                int             i, found, status;
 
                io->status = urb->status;
 
                /* the previous urbs, and this one, completed already.
                 * unlink pending urbs so they won't rx/tx bad data.
+                * careful: unlink can sometimes be synchronous...
                 */
+               spin_unlock (&io->lock);
                for (i = 0, found = 0; i < io->entries; i++) {
                        if (!io->urbs [i] || !io->urbs [i]->dev)
                                continue;
@@ -263,6 +265,7 @@
                        } else if (urb == io->urbs [i])
                                found = 1;
                }
+               spin_lock (&io->lock);
        }
        urb->dev = NULL;
 
@@ -524,6 +527,7 @@
                int     i;
 
                io->status = -ECONNRESET;
+               spin_unlock (&io->lock);
                for (i = 0; i < io->entries; i++) {
                        int     retval;
 
@@ -534,6 +538,7 @@
                                dev_warn (&io->dev->dev, "%s, unlink --> %d\n",
                                        __FUNCTION__, retval);
                }
+               spin_lock (&io->lock);
        }
        spin_unlock_irqrestore (&io->lock, flags);
 }



-------------------------------------------------------
SF email is sponsored by - The IT Product Guide
Read honest & candid reviews on hundreds of IT Products from real users.
Discover which products truly live up to the hype. Start reading now.
http://productguide.itmanagersjournal.com/
_______________________________________________
[EMAIL PROTECTED]
To unsubscribe, use the last form field at:
https://lists.sourceforge.net/lists/listinfo/linux-usb-devel

Reply via email to