svn commit: r198869 - stable/8/sys/dev/usb
Andrew Thompson
thompsa at FreeBSD.org
Wed Nov 4 01:50:26 UTC 2009
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.
*/
More information about the svn-src-all
mailing list