Source-Changes-HG archive

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

[src/netbsd-3-0]: src/sys/dev/usb Pull up following revision(s) (requested by...



details:   https://anonhg.NetBSD.org/src/rev/960ada94ee12
branches:  netbsd-3-0
changeset: 579273:960ada94ee12
user:      riz <riz%NetBSD.org@localhost>
date:      Fri Aug 11 04:24:50 2006 +0000

description:
Pull up following revision(s) (requested by abs in ticket #1459):
        sys/dev/usb/ehci.c: revision 1.96
        sys/dev/usb/uhci.c: revision 1.188
        sys/dev/usb/ohci.c: revision 1.159
        sys/dev/usb/usbdivar.h: revision 1.74
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 cfddfdd87311 -r 960ada94ee12 sys/dev/usb/ehci.c
--- a/sys/dev/usb/ehci.c        Thu Aug 10 12:31:43 2006 +0000
+++ b/sys/dev/usb/ehci.c        Fri Aug 11 04:24:50 2006 +0000
@@ -1,4 +1,4 @@
-/*     $NetBSD: ehci.c,v 1.91.2.7 2005/05/07 11:42:23 tron Exp $ */
+/*     $NetBSD: ehci.c,v 1.91.2.7.2.1 2006/08/11 04:24:50 riz Exp $ */
 
 /*
  * Copyright (c) 2004 The NetBSD Foundation, Inc.
@@ -65,7 +65,7 @@
 */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: ehci.c,v 1.91.2.7 2005/05/07 11:42:23 tron Exp $");
+__KERNEL_RCSID(0, "$NetBSD: ehci.c,v 1.91.2.7.2.1 2006/08/11 04:24:50 riz Exp $");
 
 #include "ohci.h"
 #include "uhci.h"
@@ -2457,6 +2457,7 @@
        u_int32_t qhstatus;
        int s;
        int hit;
+       int wake;
 
        DPRINTF(("ehci_abort_xfer: xfer=%p pipe=%p\n", xfer, epipe));
 
@@ -2474,6 +2475,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();
@@ -2535,7 +2556,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 cfddfdd87311 -r 960ada94ee12 sys/dev/usb/ohci.c
--- a/sys/dev/usb/ohci.c        Thu Aug 10 12:31:43 2006 +0000
+++ b/sys/dev/usb/ohci.c        Fri Aug 11 04:24:50 2006 +0000
@@ -1,4 +1,4 @@
-/*     $NetBSD: ohci.c,v 1.157 2005/03/11 19:25:22 mycroft Exp $       */
+/*     $NetBSD: ohci.c,v 1.157.4.1 2006/08/11 04:24:50 riz 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.157 2005/03/11 19:25:22 mycroft Exp $");
+__KERNEL_RCSID(0, "$NetBSD: ohci.c,v 1.157.4.1 2006/08/11 04:24:50 riz Exp $");
 
 #include <sys/param.h>
 #include <sys/systm.h>
@@ -2144,6 +2144,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));
 
@@ -2160,6 +2161,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();
@@ -2196,6 +2217,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;
@@ -2232,7 +2254,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 cfddfdd87311 -r 960ada94ee12 sys/dev/usb/uhci.c
--- a/sys/dev/usb/uhci.c        Thu Aug 10 12:31:43 2006 +0000
+++ b/sys/dev/usb/uhci.c        Fri Aug 11 04:24:50 2006 +0000
@@ -1,4 +1,4 @@
-/*     $NetBSD: uhci.c,v 1.185.2.1 2005/05/22 18:44:24 snj Exp $       */
+/*     $NetBSD: uhci.c,v 1.185.2.1.2.1 2006/08/11 04:24:50 riz 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.185.2.1 2005/05/22 18:44:24 snj Exp $");
+__KERNEL_RCSID(0, "$NetBSD: uhci.c,v 1.185.2.1.2.1 2006/08/11 04:24:50 riz 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 cfddfdd87311 -r 960ada94ee12 sys/dev/usb/usbdivar.h
--- a/sys/dev/usb/usbdivar.h    Thu Aug 10 12:31:43 2006 +0000
+++ b/sys/dev/usb/usbdivar.h    Fri Aug 11 04:24:50 2006 +0000
@@ -1,4 +1,4 @@
-/*     $NetBSD: usbdivar.h,v 1.73 2005/01/24 01:30:38 joff Exp $       */
+/*     $NetBSD: usbdivar.h,v 1.73.8.1 2006/08/11 04:24:51 riz 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