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
MFC after:	3 days
This commit is contained in:
thompsa 2009-11-01 21:44:37 +00:00
parent 48cfddd128
commit 6f1a85be85
2 changed files with 20 additions and 1 deletions

View File

@ -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 */
};
/*

View File

@ -1797,8 +1797,18 @@ usbd_transfer_drain(struct usb_xfer *xfer)
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_queue *pq)
/* 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_queue *pq)
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.
*/