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.
         */
_______________________________________________
svn-src-all@freebsd.org mailing list
http://lists.freebsd.org/mailman/listinfo/svn-src-all
To unsubscribe, send any mail to "svn-src-all-unsubscr...@freebsd.org"

Reply via email to