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;

Reply via email to