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:
parent
48cfddd128
commit
6f1a85be85
@ -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 */
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -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.
|
||||
*/
|
||||
|
Loading…
Reference in New Issue
Block a user