Source-Changes-HG archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

[src/trunk]: src/sys/dev/usb Fix a race condition in xfer abort. Derived fro...



details:   https://anonhg.NetBSD.org/src/rev/c99ab29b79d6
branches:  trunk
changeset: 580584:c99ab29b79d6
user:      augustss <augustss%NetBSD.org@localhost>
date:      Sat Apr 30 14:38:40 2005 +0000

description:
Fix a race condition in xfer abort.  Derived from a FreeBSD patch.

An xfer could be aborted twice (which means that the second abort might
access deallocated memory).  This happened when an xfer timed out and
the timeout started an abort.  While that abort was taking place the
xfer could be cancelled (usually by closing the pipe), causing a second
abort to begin.
This is now handled by having flags indicating the abort state of an xfer.

Hopefully this will fix the occasional crashes when printing.

diffstat:

 sys/dev/usb/ehci.c     |  29 +++++++++++++++++++++++++++--
 sys/dev/usb/ohci.c     |  30 ++++++++++++++++++++++++++++--
 sys/dev/usb/uhci.c     |  29 +++++++++++++++++++++++++++--
 sys/dev/usb/usbdivar.h |   7 +++++--
 4 files changed, 87 insertions(+), 8 deletions(-)

diffs (231 lines):

diff -r be2b05207a31 -r c99ab29b79d6 sys/dev/usb/ehci.c
--- a/sys/dev/usb/ehci.c        Sat Apr 30 14:26:38 2005 +0000
+++ b/sys/dev/usb/ehci.c        Sat Apr 30 14:38:40 2005 +0000
@@ -1,4 +1,4 @@
-/*     $NetBSD: ehci.c,v 1.95 2005/04/27 23:39:54 augustss Exp $ */
+/*     $NetBSD: ehci.c,v 1.96 2005/04/30 14:38:40 augustss Exp $ */
 
 /*
  * Copyright (c) 2004 The NetBSD Foundation, Inc.
@@ -65,7 +65,7 @@
 */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: ehci.c,v 1.95 2005/04/27 23:39:54 augustss Exp $");
+__KERNEL_RCSID(0, "$NetBSD: ehci.c,v 1.96 2005/04/30 14:38:40 augustss Exp $");
 
 #include "ohci.h"
 #include "uhci.h"
@@ -2443,6 +2443,7 @@
        u_int32_t qhstatus;
        int s;
        int hit;
+       int wake;
 
        DPRINTF(("ehci_abort_xfer: xfer=%p pipe=%p\n", xfer, epipe));
 
@@ -2460,6 +2461,26 @@
                panic("ehci_abort_xfer: not in process context");
 
        /*
+        * If an abort is already in progress then just wait for it to
+        * complete and return.
+        */
+       if (xfer->hcflags & UXFER_ABORTING) {
+               DPRINTFN(2, ("ehci_abort_xfer: already aborting\n"));
+#ifdef DIAGNOSTIC
+               if (status == USBD_TIMEOUT)
+                       printf("ehci_abort_xfer: TIMEOUT while aborting\n");
+#endif
+               /* Override the status which might be USBD_TIMEOUT. */
+               xfer->status = status;
+               DPRINTFN(2, ("ehci_abort_xfer: waiting for abort to finish\n"));
+               xfer->hcflags |= UXFER_ABORTWAIT;
+               while (xfer->hcflags & UXFER_ABORTING)
+                       tsleep(&xfer->hcflags, PZERO, "ehciaw", 0);
+               return;
+       }
+       xfer->hcflags |= UXFER_ABORTING;
+
+       /*
         * Step 1: Make interrupt routine and hardware ignore xfer.
         */
        s = splusb();
@@ -2521,7 +2542,11 @@
 #ifdef DIAGNOSTIC
        exfer->isdone = 1;
 #endif
+       wake = xfer->hcflags & UXFER_ABORTWAIT;
+       xfer->hcflags &= ~(UXFER_ABORTING | UXFER_ABORTWAIT);
        usb_transfer_complete(xfer);
+       if (wake)
+               wakeup(&xfer->hcflags);
 
        splx(s);
 #undef exfer
diff -r be2b05207a31 -r c99ab29b79d6 sys/dev/usb/ohci.c
--- a/sys/dev/usb/ohci.c        Sat Apr 30 14:26:38 2005 +0000
+++ b/sys/dev/usb/ohci.c        Sat Apr 30 14:38:40 2005 +0000
@@ -1,4 +1,4 @@
-/*     $NetBSD: ohci.c,v 1.158 2005/04/17 14:46:49 toshii Exp $        */
+/*     $NetBSD: ohci.c,v 1.159 2005/04/30 14:38:40 augustss Exp $      */
 /*     $FreeBSD: src/sys/dev/usb/ohci.c,v 1.22 1999/11/17 22:33:40 n_hibma Exp $       */
 
 /*
@@ -48,7 +48,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: ohci.c,v 1.158 2005/04/17 14:46:49 toshii Exp $");
+__KERNEL_RCSID(0, "$NetBSD: ohci.c,v 1.159 2005/04/30 14:38:40 augustss Exp $");
 
 #include <sys/param.h>
 #include <sys/systm.h>
@@ -2146,6 +2146,7 @@
        ohci_soft_td_t *p, *n;
        ohci_physaddr_t headp;
        int s, hit;
+       int wake;
 
        DPRINTF(("ohci_abort_xfer: xfer=%p pipe=%p sed=%p\n", xfer, opipe,sed));
 
@@ -2162,6 +2163,26 @@
                panic("ohci_abort_xfer: not in process context");
 
        /*
+        * If an abort is already in progress then just wait for it to
+        * complete and return.
+        */
+       if (xfer->hcflags & UXFER_ABORTING) {
+               DPRINTFN(2, ("ohci_abort_xfer: already aborting\n"));
+#ifdef DIAGNOSTIC
+               if (status == USBD_TIMEOUT)
+                       printf("0hci_abort_xfer: TIMEOUT while aborting\n");
+#endif
+               /* Override the status which might be USBD_TIMEOUT. */
+               xfer->status = status;
+               DPRINTFN(2, ("ohci_abort_xfer: waiting for abort to finish\n"));
+               xfer->hcflags |= UXFER_ABORTWAIT;
+               while (xfer->hcflags & UXFER_ABORTING)
+                       tsleep(&xfer->hcflags, PZERO, "ohciaw", 0);
+               return;
+       }
+       xfer->hcflags |= UXFER_ABORTING;
+
+       /*
         * Step 1: Make interrupt routine and hardware ignore xfer.
         */
        s = splusb();
@@ -2198,6 +2219,7 @@
        p = xfer->hcpriv;
 #ifdef DIAGNOSTIC
        if (p == NULL) {
+               xfer->hcflags &= ~UXFER_ABORTING; /* XXX */
                splx(s);
                printf("ohci_abort_xfer: hcpriv is NULL\n");
                return;
@@ -2234,7 +2256,11 @@
        /*
         * Step 5: Execute callback.
         */
+       wake = xfer->hcflags & UXFER_ABORTWAIT;
+       xfer->hcflags &= ~(UXFER_ABORTING | UXFER_ABORTWAIT);
        usb_transfer_complete(xfer);
+       if (wake)
+               wakeup(&xfer->hcflags);
 
        splx(s);
 }
diff -r be2b05207a31 -r c99ab29b79d6 sys/dev/usb/uhci.c
--- a/sys/dev/usb/uhci.c        Sat Apr 30 14:26:38 2005 +0000
+++ b/sys/dev/usb/uhci.c        Sat Apr 30 14:38:40 2005 +0000
@@ -1,4 +1,4 @@
-/*     $NetBSD: uhci.c,v 1.187 2005/04/29 19:15:13 skrll Exp $ */
+/*     $NetBSD: uhci.c,v 1.188 2005/04/30 14:38:40 augustss Exp $      */
 /*     $FreeBSD: src/sys/dev/usb/uhci.c,v 1.33 1999/11/17 22:33:41 n_hibma Exp $       */
 
 /*
@@ -49,7 +49,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: uhci.c,v 1.187 2005/04/29 19:15:13 skrll Exp $");
+__KERNEL_RCSID(0, "$NetBSD: uhci.c,v 1.188 2005/04/30 14:38:40 augustss Exp $");
 
 #include <sys/param.h>
 #include <sys/systm.h>
@@ -1939,6 +1939,7 @@
        uhci_softc_t *sc = (uhci_softc_t *)upipe->pipe.device->bus;
        uhci_soft_td_t *std;
        int s;
+       int wake;
 
        DPRINTFN(1,("uhci_abort_xfer: xfer=%p, status=%d\n", xfer, status));
 
@@ -1955,6 +1956,26 @@
                panic("uhci_abort_xfer: not in process context");
 
        /*
+        * If an abort is already in progress then just wait for it to
+        * complete and return.
+        */
+       if (xfer->hcflags & UXFER_ABORTING) {
+               DPRINTFN(2, ("uhci_abort_xfer: already aborting\n"));
+#ifdef DIAGNOSTIC
+               if (status == USBD_TIMEOUT)
+                       printf("uhci_abort_xfer: TIMEOUT while aborting\n");
+#endif
+               /* Override the status which might be USBD_TIMEOUT. */
+               xfer->status = status;
+               DPRINTFN(2, ("uhci_abort_xfer: waiting for abort to finish\n"));
+               xfer->hcflags |= UXFER_ABORTWAIT;
+               while (xfer->hcflags & UXFER_ABORTING)
+                       tsleep(&xfer->hcflags, PZERO, "uhciaw", 0);
+               return;
+       }
+       xfer->hcflags |= UXFER_ABORTING;
+
+       /*
         * Step 1: Make interrupt routine and hardware ignore xfer.
         */
        s = splusb();
@@ -1990,7 +2011,11 @@
 #ifdef DIAGNOSTIC
        ii->isdone = 1;
 #endif
+       wake = xfer->hcflags & UXFER_ABORTWAIT;
+       xfer->hcflags &= ~(UXFER_ABORTING | UXFER_ABORTWAIT);
        usb_transfer_complete(xfer);
+       if (wake)
+               wakeup(&xfer->hcflags);
        splx(s);
 }
 
diff -r be2b05207a31 -r c99ab29b79d6 sys/dev/usb/usbdivar.h
--- a/sys/dev/usb/usbdivar.h    Sat Apr 30 14:26:38 2005 +0000
+++ b/sys/dev/usb/usbdivar.h    Sat Apr 30 14:38:40 2005 +0000
@@ -1,4 +1,4 @@
-/*     $NetBSD: usbdivar.h,v 1.73 2005/01/24 01:30:38 joff Exp $       */
+/*     $NetBSD: usbdivar.h,v 1.74 2005/04/30 14:38:40 augustss Exp $   */
 /*     $FreeBSD: src/sys/dev/usb/usbdivar.h,v 1.11 1999/11/17 22:33:51 n_hibma Exp $   */
 
 /*
@@ -217,7 +217,7 @@
        struct usbd_device     *device;
        usb_dma_t               dmabuf;
 
-       int                     rqflags;
+       u_int8_t                rqflags;
 #define URQ_REQUEST    0x01
 #define URQ_AUTO_DMABUF        0x10
 #define URQ_DEV_DMABUF 0x20
@@ -225,6 +225,9 @@
        SIMPLEQ_ENTRY(usbd_xfer) next;
 
        void                   *hcpriv; /* private use by the HC driver */
+       u_int8_t                hcflags; /* private use by the HC driver */
+#define UXFER_ABORTING 0x01    /* xfer is aborting. */
+#define UXFER_ABORTWAIT        0x02    /* abort completion is being awaited. */
 
        usb_callout_t           timeout_handle;
 };



Home | Main Index | Thread Index | Old Index