Module Name: src Committed By: riz Date: Mon Feb 18 18:13:06 UTC 2013
Modified Files: src/sys/dev/usb [netbsd-6]: dwc_otg.c dwc_otgvar.h Log Message: Pull up following revision(s) (requested by skrll in ticket #827): sys/dev/usb/dwc_otg.c: revision 1.46 sys/dev/usb/dwc_otgvar.h: revision 1.12 Track transfer state better to avoid races between the workqueue and aborting. To generate a diff of this commit: cvs rdiff -u -r1.45.2.3 -r1.45.2.4 src/sys/dev/usb/dwc_otg.c cvs rdiff -u -r1.11.2.3 -r1.11.2.4 src/sys/dev/usb/dwc_otgvar.h Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/sys/dev/usb/dwc_otg.c diff -u src/sys/dev/usb/dwc_otg.c:1.45.2.3 src/sys/dev/usb/dwc_otg.c:1.45.2.4 --- src/sys/dev/usb/dwc_otg.c:1.45.2.3 Fri Feb 15 08:21:21 2013 +++ src/sys/dev/usb/dwc_otg.c Mon Feb 18 18:13:05 2013 @@ -1,4 +1,4 @@ -/* $NetBSD: dwc_otg.c,v 1.45.2.3 2013/02/15 08:21:21 msaitoh Exp $ */ +/* $NetBSD: dwc_otg.c,v 1.45.2.4 2013/02/18 18:13:05 riz 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.2.3 2013/02/15 08:21:21 msaitoh Exp $"); +__KERNEL_RCSID(0, "$NetBSD: dwc_otg.c,v 1.45.2.4 2013/02/18 18:13:05 riz Exp $"); #include <sys/param.h> #include <sys/systm.h> @@ -411,13 +411,20 @@ dwc_otg_softintr(void *v) 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) { wakeup(&dxfer->xfer.hcflags); 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); @@ -616,9 +623,23 @@ dwc_otg_abort_xfer(usbd_xfer_handle xfer 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); @@ -626,10 +647,11 @@ dwc_otg_abort_xfer(usbd_xfer_handle xfer dwc_otg_host_channel_free(dxfer->td_transfer_cache); } - while (dxfer->queued) { - wakeup(&xfer->hcflags); + while (dxfer->state != DXFER_ABORTING) { + tsleep(&xfer->hcflags, PZERO, "dotgxw", 0); } + dxfer->state = DXFER_DONE; /* * Step 2: Execute callback. */ @@ -1598,15 +1620,15 @@ dwc_otg_worker(struct work *wk, void *pr 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; + wakeup(&xfer->hcflags); } - dxfer->queued = false; - wakeup(&xfer->hcflags); } - } int dwc_otg_intr(void *p) @@ -3778,7 +3800,7 @@ dwc_otg_setup_standard_chain(usbd_xfer_h // DPRINTF(("%s: xfer->length %d\n", __func__, xfer->length)); - dxfer->queued = false; + dxfer->state = DXFER_INIT; /* get first again */ td = dxfer->td_transfer_first; @@ -3887,11 +3909,13 @@ dwc_otg_start_standard_chain(usbd_xfer_h /* 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)) { /* put transfer on interrupt queue */ - dxfer->active = true; + dxfer->state = DXFER_ACTIVE; TAILQ_INSERT_TAIL(&sc->sc_active, dxfer, xnext); /* start timeout, if any */ @@ -3980,12 +4004,12 @@ dwc_otg_standard_done(usbd_xfer_handle x 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); @@ -4324,8 +4348,8 @@ dwc_otg_xfer_start(usbd_xfer_handle xfer 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); } } Index: src/sys/dev/usb/dwc_otgvar.h diff -u src/sys/dev/usb/dwc_otgvar.h:1.11.2.3 src/sys/dev/usb/dwc_otgvar.h:1.11.2.4 --- src/sys/dev/usb/dwc_otgvar.h:1.11.2.3 Thu Feb 14 21:50:41 2013 +++ src/sys/dev/usb/dwc_otgvar.h Mon Feb 18 18:13:05 2013 @@ -1,4 +1,4 @@ -/* $NetBSD: dwc_otgvar.h,v 1.11.2.3 2013/02/14 21:50:41 riz Exp $ */ +/* $NetBSD: dwc_otgvar.h,v 1.11.2.4 2013/02/18 18:13:05 riz 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 dwc_otg_xfer { 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;