Skip site navigation (1)Skip section navigation (2)
Date:      Mon, 28 Sep 2009 07:46:23 +0000 (UTC)
From:      Andrew Thompson <thompsa@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org
Subject:   svn commit: r197562 - head/sys/dev/usb
Message-ID:  <200909280746.n8S7kNm5047169@svn.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: thompsa
Date: Mon Sep 28 07:46:22 2009
New Revision: 197562
URL: http://svn.freebsd.org/changeset/base/197562

Log:
  Add extra safety locking when clobbering xfer->flags_int.started in start and
  stop functions, because xfer->flags_int is also updated by the USB controller,
  under the controller lock.
  
  Submitted by:	Hans Petter Selasky

Modified:
  head/sys/dev/usb/usb_transfer.c

Modified: head/sys/dev/usb/usb_transfer.c
==============================================================================
--- head/sys/dev/usb/usb_transfer.c	Mon Sep 28 07:42:59 2009	(r197561)
+++ head/sys/dev/usb/usb_transfer.c	Mon Sep 28 07:46:22 2009	(r197562)
@@ -1332,7 +1332,9 @@ usbd_setup_ctrl_transfer(struct usb_xfer
 	/* check if there is a length mismatch */
 
 	if (len > xfer->flags_int.control_rem) {
-		DPRINTFN(0, "Length greater than remaining length!\n");
+		DPRINTFN(0, "Length (%d) greater than "
+		    "remaining length (%d)!\n", len,
+		    xfer->flags_int.control_rem);
 		goto error;
 	}
 	/* check if we are doing a short transfer */
@@ -1620,7 +1622,10 @@ usbd_transfer_start(struct usb_xfer *xfe
 	/* mark the USB transfer started */
 
 	if (!xfer->flags_int.started) {
+		/* lock the BUS lock to avoid races updating flags_int */
+		USB_BUS_LOCK(xfer->xroot->bus);
 		xfer->flags_int.started = 1;
+		USB_BUS_UNLOCK(xfer->xroot->bus);
 	}
 	/* check if the USB transfer callback is already transferring */
 
@@ -1655,14 +1660,21 @@ usbd_transfer_stop(struct usb_xfer *xfer
 	/* check if the USB transfer was ever opened */
 
 	if (!xfer->flags_int.open) {
-		/* nothing to do except clearing the "started" flag */
-		xfer->flags_int.started = 0;
+		if (xfer->flags_int.started) {
+			/* nothing to do except clearing the "started" flag */
+			/* lock the BUS lock to avoid races updating flags_int */
+			USB_BUS_LOCK(xfer->xroot->bus);
+			xfer->flags_int.started = 0;
+			USB_BUS_UNLOCK(xfer->xroot->bus);
+		}
 		return;
 	}
 	/* try to stop the current USB transfer */
 
 	USB_BUS_LOCK(xfer->xroot->bus);
-	xfer->error = USB_ERR_CANCELLED;/* override any previous error */
+	/* override any previous error */
+	xfer->error = USB_ERR_CANCELLED;
+
 	/*
 	 * Clear "open" and "started" when both private and USB lock
 	 * is locked so that we don't get a race updating "flags_int"
@@ -2121,9 +2133,6 @@ usb_dma_delay_done_cb(void *arg)
 
 	DPRINTFN(3, "Completed %p\n", xfer);
 
-	/* only delay once */
-	xfer->flags_int.did_dma_delay = 1;
-
 	/* queue callback for execution, again */
 	usbd_transfer_done(xfer, 0);
 }
@@ -2193,6 +2202,8 @@ usbd_transfer_done(struct usb_xfer *xfer
 	 */
 	if (!xfer->flags_int.transferring) {
 		DPRINTF("not transferring\n");
+		/* end of control transfer, if any */
+		xfer->flags_int.control_act = 0;
 		return;
 	}
 	/* only set transfer error if not already set */
@@ -2491,6 +2502,9 @@ usbd_callback_wrapper_sub(struct usb_xfe
 
 		usb_timeout_t temp;
 
+		/* only delay once */
+		xfer->flags_int.did_dma_delay = 1;
+
 		/* we can not cancel this delay */
 		xfer->flags_int.can_cancel_immed = 0;
 



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?200909280746.n8S7kNm5047169>