Skip site navigation (1)Skip section navigation (2)
Date:      Wed, 4 Nov 2009 01:50:25 +0000 (UTC)
From:      Andrew Thompson <thompsa@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-stable@freebsd.org, svn-src-stable-8@freebsd.org
Subject:   svn commit: r198869 - stable/8/sys/dev/usb
Message-ID:  <200911040150.nA41oPZ3013287@svn.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: thompsa
Date: Wed Nov  4 01:50:25 2009
New Revision: 198869
URL: http://svn.freebsd.org/changeset/base/198869

Log:
  MFC r198775
  
   Fix a corner case where usbd_transfer_drain() can return too early if the
   callback has dropped the mutex, leading to a panic.
  
  Submitted by:	HPS

Modified:
  stable/8/sys/dev/usb/usb_core.h
  stable/8/sys/dev/usb/usb_transfer.c
Directory Properties:
  stable/8/sys/   (props changed)
  stable/8/sys/amd64/include/xen/   (props changed)
  stable/8/sys/cddl/contrib/opensolaris/   (props changed)
  stable/8/sys/contrib/dev/acpica/   (props changed)
  stable/8/sys/contrib/pf/   (props changed)
  stable/8/sys/dev/xen/xenpci/   (props changed)

Modified: stable/8/sys/dev/usb/usb_core.h
==============================================================================
--- stable/8/sys/dev/usb/usb_core.h	Wed Nov  4 01:32:59 2009	(r198868)
+++ stable/8/sys/dev/usb/usb_core.h	Wed Nov  4 01:50:25 2009	(r198869)
@@ -112,6 +112,7 @@ struct usb_xfer_flags_int {
 	uint8_t	curr_dma_set:1;		/* used by USB HC/DC driver */
 	uint8_t	can_cancel_immed:1;	/* set if USB transfer can be
 					 * cancelled immediately */
+	uint8_t	doing_callback:1;	/* set if executing the callback */
 };
 
 /*

Modified: stable/8/sys/dev/usb/usb_transfer.c
==============================================================================
--- stable/8/sys/dev/usb/usb_transfer.c	Wed Nov  4 01:32:59 2009	(r198868)
+++ stable/8/sys/dev/usb/usb_transfer.c	Wed Nov  4 01:50:25 2009	(r198869)
@@ -1797,8 +1797,18 @@ usbd_transfer_drain(struct usb_xfer *xfe
 
 	usbd_transfer_stop(xfer);
 
-	while (usbd_transfer_pending(xfer)) {
+	while (usbd_transfer_pending(xfer) || 
+	    xfer->flags_int.doing_callback) {
+
+		/* 
+		 * It is allowed that the callback can drop its
+		 * transfer mutex. In that case checking only
+		 * "usbd_transfer_pending()" is not enough to tell if
+		 * the USB transfer is fully drained. We also need to
+		 * check the internal "doing_callback" flag.
+		 */
 		xfer->flags_int.draining = 1;
+
 		/*
 		 * Wait until the current outstanding USB
 		 * transfer is complete !
@@ -2043,6 +2053,9 @@ usbd_callback_wrapper(struct usb_xfer_qu
 	/* get next USB transfer in the queue */
 	info->done_q.curr = NULL;
 
+	/* set flag in case of drain */
+	xfer->flags_int.doing_callback = 1;
+
 	USB_BUS_UNLOCK(info->bus);
 	USB_BUS_LOCK_ASSERT(info->bus, MA_NOTOWNED);
 
@@ -2095,12 +2108,17 @@ usbd_callback_wrapper(struct usb_xfer_qu
 	if ((!xfer->flags_int.open) &&
 	    (xfer->flags_int.started) &&
 	    (xfer->usb_state == USB_ST_ERROR)) {
+		/* clear flag in case of drain */
+		xfer->flags_int.doing_callback = 0;
 		/* try to loop, but not recursivly */
 		usb_command_wrapper(&info->done_q, xfer);
 		return;
 	}
 
 done:
+	/* clear flag in case of drain */
+	xfer->flags_int.doing_callback = 0;
+
 	/*
 	 * Check if we are draining.
 	 */



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