svn commit: r198930 - releng/8.0/sys/dev/usb
Andrew Thompson
thompsa at FreeBSD.org
Wed Nov 4 21:28:50 UTC 2009
Author: thompsa
Date: Wed Nov 4 21:28:50 2009
New Revision: 198930
URL: http://svn.freebsd.org/changeset/base/198930
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
Approved by: re (kib)
Modified:
releng/8.0/sys/dev/usb/usb_core.h
releng/8.0/sys/dev/usb/usb_transfer.c
Directory Properties:
releng/8.0/sys/ (props changed)
releng/8.0/sys/amd64/include/xen/ (props changed)
releng/8.0/sys/cddl/contrib/opensolaris/ (props changed)
releng/8.0/sys/contrib/dev/acpica/ (props changed)
releng/8.0/sys/contrib/pf/ (props changed)
releng/8.0/sys/dev/xen/xenpci/ (props changed)
Modified: releng/8.0/sys/dev/usb/usb_core.h
==============================================================================
--- releng/8.0/sys/dev/usb/usb_core.h Wed Nov 4 21:12:33 2009 (r198929)
+++ releng/8.0/sys/dev/usb/usb_core.h Wed Nov 4 21:28:50 2009 (r198930)
@@ -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: releng/8.0/sys/dev/usb/usb_transfer.c
==============================================================================
--- releng/8.0/sys/dev/usb/usb_transfer.c Wed Nov 4 21:12:33 2009 (r198929)
+++ releng/8.0/sys/dev/usb/usb_transfer.c Wed Nov 4 21:28:50 2009 (r198930)
@@ -1785,8 +1785,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 !
@@ -2031,6 +2041,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);
@@ -2083,12 +2096,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.
*/
More information about the svn-src-all
mailing list