USB: gadget: f_mass_storage: Defer handling interface changes until it is safe.
authorMike Lockwood <lockwood@android.com>
Thu, 15 Apr 2010 19:04:07 +0000 (15:04 -0400)
committerMike Lockwood <lockwood@android.com>
Thu, 15 Apr 2010 19:33:35 +0000 (15:33 -0400)
Pulling in some code from file_storage.c, we now handle interface changes
in do_set_config(), which is now not called until any pending requests have
been successfully completed or cancelled.

This fixes a race condition that resulted in usb_ep_free_request() being called
while the request is still busy.

Signed-off-by: Mike Lockwood <lockwood@android.com>
drivers/usb/gadget/f_mass_storage.c

index f4622b36baf702f1a3db1f54447ed1cb96f9b7e9..ca85cc01e9f873c0f42f8584321982daf3b1855c 100644 (file)
@@ -2261,18 +2261,19 @@ static int do_set_config(struct fsg_dev *fsg, u8 new_config)
 {
        int     rc = 0;
 
-       if (new_config == fsg->config)
-               return rc;
-
        /* Disable the single interface */
        if (fsg->config != 0) {
                DBG(fsg, "reset config\n");
                fsg->config = 0;
+               rc = do_set_interface(fsg, -1);
        }
 
        /* Enable the interface */
-       if (new_config != 0)
+       if (new_config != 0) {
                fsg->config = new_config;
+               if ((rc = do_set_interface(fsg, 0)) != 0)
+                       fsg->config = 0;        // Reset on errors
+       }
 
        switch_set_state(&fsg->sdev, new_config);
        adjust_wake_lock(fsg);
@@ -2287,6 +2288,7 @@ static void handle_exception(struct fsg_dev *fsg)
        siginfo_t               info;
        int                     sig;
        int                     i;
+       int                     num_active;
        struct fsg_buffhd       *bh;
        enum fsg_state          old_state;
        u8                      new_config;
@@ -2308,6 +2310,28 @@ static void handle_exception(struct fsg_dev *fsg)
                }
        }
 
+       /* Cancel all the pending transfers */
+       for (i = 0; i < NUM_BUFFERS; ++i) {
+               bh = &fsg->buffhds[i];
+               if (bh->inreq_busy)
+                       usb_ep_dequeue(fsg->bulk_in, bh->inreq);
+               if (bh->outreq_busy)
+                       usb_ep_dequeue(fsg->bulk_out, bh->outreq);
+       }
+
+       /* Wait until everything is idle */
+       for (;;) {
+               num_active = 0;
+               for (i = 0; i < NUM_BUFFERS; ++i) {
+                       bh = &fsg->buffhds[i];
+                       num_active += bh->outreq_busy;
+               }
+               if (num_active == 0)
+                       break;
+               if (sleep_thread(fsg))
+                       return;
+       }
+
        /*
        * Do NOT flush the fifo after set_interface()
        * Otherwise, it results in some data being lost
@@ -2378,10 +2402,6 @@ static void handle_exception(struct fsg_dev *fsg)
 
        case FSG_STATE_EXIT:
        case FSG_STATE_TERMINATED:
-               if (new_config)  {
-                       fsg->new_config = 0;
-                       do_set_interface(fsg, -1);
-               }
                do_set_config(fsg, 0);                  /* Free resources */
                spin_lock_irqsave(&fsg->lock, flags);
                fsg->state = FSG_STATE_TERMINATED;      /* Stop the thread */
@@ -2885,7 +2905,6 @@ static int fsg_function_set_alt(struct usb_function *f,
        struct fsg_dev  *fsg = func_to_dev(f);
        DBG(fsg, "fsg_function_set_alt intf: %d alt: %d\n", intf, alt);
        fsg->new_config = 1;
-       do_set_interface(fsg, 0);
        raise_exception(fsg, FSG_STATE_CONFIG_CHANGE);
        return 0;
 }
@@ -2894,8 +2913,6 @@ static void fsg_function_disable(struct usb_function *f)
 {
        struct fsg_dev  *fsg = func_to_dev(f);
        DBG(fsg, "fsg_function_disable\n");
-       if (fsg->new_config)
-               do_set_interface(fsg, -1);
        fsg->new_config = 0;
        raise_exception(fsg, FSG_STATE_CONFIG_CHANGE);
 }