Source-Changes-HG archive

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

[src/trunk]: src/sys/dev/usb Track transfer state better to avoid races betwe...



details:   https://anonhg.NetBSD.org/src/rev/5d0367cf0d1f
branches:  trunk
changeset: 784959:5d0367cf0d1f
user:      skrll <skrll%NetBSD.org@localhost>
date:      Fri Feb 15 17:07:09 2013 +0000

description:
Track transfer state better to avoid races between the workqueue and
aborting.

diffstat:

 sys/dev/usb/dwc_otg.c    |  57 ++++++++++++++++++++++++++++++++++-------------
 sys/dev/usb/dwc_otgvar.h |  12 +++++++--
 2 files changed, 50 insertions(+), 19 deletions(-)

diffs (175 lines):

diff -r fd213a02b231 -r 5d0367cf0d1f sys/dev/usb/dwc_otg.c
--- a/sys/dev/usb/dwc_otg.c     Fri Feb 15 17:04:15 2013 +0000
+++ b/sys/dev/usb/dwc_otg.c     Fri Feb 15 17:07:09 2013 +0000
@@ -1,4 +1,4 @@
-/*     $NetBSD: dwc_otg.c,v 1.45 2013/02/04 21:37:05 skrll Exp $       */
+/*     $NetBSD: dwc_otg.c,v 1.46 2013/02/15 17:07:09 skrll Exp $       */
 
 /*-
  * Copyright (c) 2012 Hans Petter Selasky. All rights reserved.
@@ -60,7 +60,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: dwc_otg.c,v 1.45 2013/02/04 21:37:05 skrll Exp $");
+__KERNEL_RCSID(0, "$NetBSD: dwc_otg.c,v 1.46 2013/02/15 17:07:09 skrll Exp $");
 
 #include <sys/param.h>
 #include <sys/systm.h>
@@ -423,13 +423,20 @@
 
        mutex_spin_enter(&sc->sc_intr_lock);
        while ((dxfer = TAILQ_FIRST(&sc->sc_complete)) != NULL) {
-               TAILQ_REMOVE(&sc->sc_complete, dxfer, xnext);
-
+               KASSERT(dxfer->state == DXFER_COMPLETING);
+
+               /*
+                * dwc_otg_abort_xfer will remove this transfer from the
+                * sc_complete queue
+                */
                if (dxfer->xfer.hcflags & UXFER_ABORTING) {
                        cv_broadcast(&dxfer->xfer.hccv);
                        continue;
                }
 
+               TAILQ_REMOVE(&sc->sc_complete, dxfer, xnext);
+               dxfer->state = DXFER_DONE;
+
                mutex_spin_exit(&sc->sc_intr_lock);
                usb_transfer_complete(&dxfer->xfer);
                mutex_spin_enter(&sc->sc_intr_lock);
@@ -641,9 +648,23 @@
        xfer->status = status;  /* make software ignore it */
        callout_stop(&xfer->timeout_handle);
 
-       if (dxfer->active) {
+       switch (dxfer->state) {
+       case DXFER_INIT:
+               dxfer->state = DXFER_ABORTING;
+               break;
+       case DXFER_WORKQ:
+               /* Give the workqueue a chance */
+               break;
+       case DXFER_ACTIVE:
                TAILQ_REMOVE(&sc->sc_active, dxfer, xnext);
-               dxfer->active = false;
+               dxfer->state = DXFER_ABORTING;
+               break;
+       case DXFER_COMPLETING:
+               TAILQ_REMOVE(&sc->sc_complete, dxfer, xnext);
+               dxfer->state = DXFER_ABORTING;
+               break;
+       default:
+               KASSERT(false);
        }
        mutex_spin_exit(&sc->sc_intr_lock);
 
@@ -651,10 +672,11 @@
                dwc_otg_host_channel_free(dxfer->td_transfer_cache);
        }
 
-       while (dxfer->queued) {
+       while (dxfer->state != DXFER_ABORTING) {
                cv_wait(&xfer->hccv, &sc->sc_lock);
        }
 
+       dxfer->state = DXFER_DONE;
        /*
         * Step 2: Execute callback.
         */
@@ -1671,13 +1693,14 @@
                dwc_otg_timer(sc);
        } else {
                KASSERT(dwork->xfer != NULL);
-               KASSERT(dxfer->queued == true);
+               KASSERT(dxfer->state == DXFER_WORKQ);
 
                if (!(xfer->hcflags & UXFER_ABORTING)) {
                        dwc_otg_start_standard_chain(xfer);
+               } else {
+                       dxfer->state = DXFER_ABORTING;
+                       cv_broadcast(&xfer->hccv);
                }
-               dxfer->queued = false;
-               cv_broadcast(&xfer->hccv);
        }
        mutex_exit(&sc->sc_lock);
 }
@@ -3853,7 +3876,7 @@
 
 //     DPRINTF(("%s: xfer->length %d\n", __func__, xfer->length));
 
-       dxfer->queued = false;
+       dxfer->state = DXFER_INIT;
 
        /* get first again */
        td = dxfer->td_transfer_first;
@@ -3962,12 +3985,14 @@
 
        /* poll one time - will turn on interrupts */
        mutex_spin_enter(&sc->sc_intr_lock);
+       dxfer->state = DXFER_STARTED;
+
        if (dwc_otg_xfer_do_fifo(xfer)) {
 
                KASSERT(mutex_owned(&sc->sc_lock));
                /* put transfer on interrupt queue */
 
-               dxfer->active = true;
+               dxfer->state = DXFER_ACTIVE;
                TAILQ_INSERT_TAIL(&sc->sc_active, dxfer, xnext);
 
                /* start timeout, if any */
@@ -4059,12 +4084,12 @@
                dwc_otg_host_channel_free(td);
 
        xfer->status = err;
-       if (dxfer->active) {
+       if (dxfer->state == DXFER_ACTIVE) {
                TAILQ_REMOVE(&sc->sc_active, dxfer, xnext);
-               dxfer->active = false;
        }
        callout_stop(&xfer->timeout_handle);
 
+       dxfer->state = DXFER_COMPLETING;
        TAILQ_INSERT_TAIL(&sc->sc_complete, dxfer, xnext);
 
        usb_schedsoftintr(&sc->sc_bus);
@@ -4406,8 +4431,8 @@
        if (sc->sc_bus.use_polling) {
                dwc_otg_start_standard_chain(xfer);
        } else {
-               KASSERT(dxfer->queued == false);
-               dxfer->queued = true;
+               KASSERT(dxfer->state == DXFER_INIT);
+               dxfer->state = DXFER_WORKQ;
                workqueue_enqueue(sc->sc_wq, (struct work *)&dxfer->work, NULL);
        }
 }
diff -r fd213a02b231 -r 5d0367cf0d1f sys/dev/usb/dwc_otgvar.h
--- a/sys/dev/usb/dwc_otgvar.h  Fri Feb 15 17:04:15 2013 +0000
+++ b/sys/dev/usb/dwc_otgvar.h  Fri Feb 15 17:07:09 2013 +0000
@@ -1,4 +1,4 @@
-/*     $NetBSD: dwc_otgvar.h,v 1.11 2013/02/04 21:29:14 skrll Exp $ */
+/*     $NetBSD: dwc_otgvar.h,v 1.12 2013/02/15 17:07:09 skrll Exp $ */
 
 /* $FreeBSD: src/sys/dev/usb/controller/dwc_otg.h,v 1.12 2012/09/27 15:23:38 hselasky Exp $ */
 /*-
@@ -158,8 +158,14 @@
        struct usbd_xfer xfer;                  /* Needs to be first */
        struct usb_task abort_task;
        TAILQ_ENTRY(dwc_otg_xfer) xnext;        /* list of active/complete xfers */
-       bool            queued;                 /* pending workqueue */
-       bool            active;                 /* still active */
+       int     state;
+#define DXFER_INIT             0
+#define DXFER_WORKQ            1
+#define DXFER_STARTED          2
+#define DXFER_ACTIVE           3
+#define DXFER_COMPLETING       4
+#define DXFER_ABORTING         5
+#define DXFER_DONE             6
 
        void            *td_start[1];
        dwc_otg_td_t    *td_transfer_first;



Home | Main Index | Thread Index | Old Index