USB: UDC: Implement udc_async_callbacks in net2280
authorAlan Stern <stern@rowland.harvard.edu>
Thu, 20 May 2021 20:22:00 +0000 (16:22 -0400)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Fri, 4 Jun 2021 11:52:06 +0000 (13:52 +0200)
This patch adds a udc_async_callbacks handler to the net2280 UDC
driver, which will prevent a theoretical race during gadget unbinding.

The net2280 driver is sufficiently complicated that I didn't want to
mess around with IRQ settings.  Instead, the patch simply adds a new
flag to control async callbacks, and checks the flag before issuing
any of them.

Acked-by: Felipe Balbi <balbi@kernel.org>
Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
Link: https://lore.kernel.org/r/20210520202200.GE1216852@rowland.harvard.edu
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/usb/gadget/udc/net2280.c
drivers/usb/gadget/udc/net2280.h

index fc9f99f..0e0458e 100644 (file)
@@ -1617,6 +1617,7 @@ static struct usb_ep *net2280_match_ep(struct usb_gadget *_gadget,
 static int net2280_start(struct usb_gadget *_gadget,
                struct usb_gadget_driver *driver);
 static int net2280_stop(struct usb_gadget *_gadget);
+static void net2280_async_callbacks(struct usb_gadget *_gadget, bool enable);
 
 static const struct usb_gadget_ops net2280_ops = {
        .get_frame      = net2280_get_frame,
@@ -1625,6 +1626,7 @@ static const struct usb_gadget_ops net2280_ops = {
        .pullup         = net2280_pullup,
        .udc_start      = net2280_start,
        .udc_stop       = net2280_stop,
+       .udc_async_callbacks = net2280_async_callbacks,
        .match_ep       = net2280_match_ep,
 };
 
@@ -2472,7 +2474,7 @@ static void stop_activity(struct net2280 *dev, struct usb_gadget_driver *driver)
                nuke(&dev->ep[i]);
 
        /* report disconnect; the driver is already quiesced */
-       if (driver) {
+       if (dev->async_callbacks && driver) {
                spin_unlock(&dev->lock);
                driver->disconnect(&dev->gadget);
                spin_lock(&dev->lock);
@@ -2502,6 +2504,15 @@ static int net2280_stop(struct usb_gadget *_gadget)
        return 0;
 }
 
+static void net2280_async_callbacks(struct usb_gadget *_gadget, bool enable)
+{
+       struct net2280  *dev = container_of(_gadget, struct net2280, gadget);
+
+       spin_lock_irq(&dev->lock);
+       dev->async_callbacks = enable;
+       spin_unlock_irq(&dev->lock);
+}
+
 /*-------------------------------------------------------------------------*/
 
 /* handle ep0, ep-e, ep-f with 64 byte packets: packet per irq.
@@ -3042,9 +3053,11 @@ usb3_delegate:
                                readl(&ep->cfg->ep_cfg));
 
                ep->responded = 0;
-               spin_unlock(&dev->lock);
-               tmp = dev->driver->setup(&dev->gadget, &r);
-               spin_lock(&dev->lock);
+               if (dev->async_callbacks) {
+                       spin_unlock(&dev->lock);
+                       tmp = dev->driver->setup(&dev->gadget, &r);
+                       spin_lock(&dev->lock);
+               }
        }
 do_stall3:
        if (tmp < 0) {
@@ -3284,9 +3297,11 @@ delegate:
                                w_value, w_index, w_length,
                                readl(&ep->cfg->ep_cfg));
                        ep->responded = 0;
-                       spin_unlock(&dev->lock);
-                       tmp = dev->driver->setup(&dev->gadget, &u.r);
-                       spin_lock(&dev->lock);
+                       if (dev->async_callbacks) {
+                               spin_unlock(&dev->lock);
+                               tmp = dev->driver->setup(&dev->gadget, &u.r);
+                               spin_lock(&dev->lock);
+                       }
                }
 
                /* stall ep0 on error */
@@ -3391,14 +3406,14 @@ __acquires(dev->lock)
                        if (disconnect || reset) {
                                stop_activity(dev, dev->driver);
                                ep0_start(dev);
-                               spin_unlock(&dev->lock);
-                               if (reset)
-                                       usb_gadget_udc_reset
-                                               (&dev->gadget, dev->driver);
-                               else
-                                       (dev->driver->disconnect)
-                                               (&dev->gadget);
-                               spin_lock(&dev->lock);
+                               if (dev->async_callbacks) {
+                                       spin_unlock(&dev->lock);
+                                       if (reset)
+                                               usb_gadget_udc_reset(&dev->gadget, dev->driver);
+                                       else
+                                               (dev->driver->disconnect)(&dev->gadget);
+                                       spin_lock(&dev->lock);
+                               }
                                return;
                        }
                }
@@ -3419,12 +3434,12 @@ __acquires(dev->lock)
                writel(tmp, &dev->regs->irqstat1);
                spin_unlock(&dev->lock);
                if (stat & BIT(SUSPEND_REQUEST_INTERRUPT)) {
-                       if (dev->driver->suspend)
+                       if (dev->async_callbacks && dev->driver->suspend)
                                dev->driver->suspend(&dev->gadget);
                        if (!enable_suspend)
                                stat &= ~BIT(SUSPEND_REQUEST_INTERRUPT);
                } else {
-                       if (dev->driver->resume)
+                       if (dev->async_callbacks && dev->driver->resume)
                                dev->driver->resume(&dev->gadget);
                        /* at high speed, note erratum 0133 */
                }
index 7da3dc1..34716a9 100644 (file)
@@ -162,6 +162,7 @@ struct net2280 {
                                        ltm_enable:1,
                                        wakeup_enable:1,
                                        addressed_state:1,
+                                       async_callbacks:1,
                                        bug7734_patched:1;
        u16                             chiprev;
        int enhanced_mode;