Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit d078f295 authored by Alan Stern's avatar Alan Stern Committed by Greg Kroah-Hartman
Browse files

USB: net2280: Fix erroneous synchronization change



commit dec3c23c9aa1815f07d98ae0375b4cbc10971e13 upstream.

Commit f16443a0 ("USB: gadgetfs, dummy-hcd, net2280: fix locking
for callbacks") was based on a serious misunderstanding.  It
introduced regressions into both the dummy-hcd and net2280 drivers.

The problem in dummy-hcd was fixed by commit 7dbd8f4c ("USB:
dummy-hcd: Fix erroneous synchronization change"), but the problem in
net2280 remains.  Namely: the ->disconnect(), ->suspend(), ->resume(),
and ->reset() callbacks must be invoked without the private lock held;
otherwise a deadlock will occur when the callback routine tries to
interact with the UDC driver.

This patch largely is a reversion of the relevant parts of
f16443a0.  It also drops the private lock around the calls to
->suspend() and ->resume() (something the earlier patch forgot to do).
This is safe from races with device interrupts because it occurs
within the interrupt handler.

Finally, the patch changes where the ->disconnect() callback is
invoked when net2280_pullup() turns the pullup off.  Rather than
making the callback from within stop_activity() at a time when dropping
the private lock could be unsafe, the callback is moved to a point
after the lock has already been dropped.

Signed-off-by: default avatarAlan Stern <stern@rowland.harvard.edu>
Fixes: f16443a0 ("USB: gadgetfs, dummy-hcd, net2280: fix locking for callbacks")
Reported-by: default avatarD. Ziesche <dziesche@zes.com>
Tested-by: default avatarD. Ziesche <dziesche@zes.com>
CC: <stable@vger.kernel.org>
Signed-off-by: default avatarFelipe Balbi <felipe.balbi@linux.intel.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 3afbeb5c
Loading
Loading
Loading
Loading
+14 −2
Original line number Original line Diff line number Diff line
@@ -1549,11 +1549,14 @@ static int net2280_pullup(struct usb_gadget *_gadget, int is_on)
		writel(tmp | BIT(USB_DETECT_ENABLE), &dev->usb->usbctl);
		writel(tmp | BIT(USB_DETECT_ENABLE), &dev->usb->usbctl);
	} else {
	} else {
		writel(tmp & ~BIT(USB_DETECT_ENABLE), &dev->usb->usbctl);
		writel(tmp & ~BIT(USB_DETECT_ENABLE), &dev->usb->usbctl);
		stop_activity(dev, dev->driver);
		stop_activity(dev, NULL);
	}
	}


	spin_unlock_irqrestore(&dev->lock, flags);
	spin_unlock_irqrestore(&dev->lock, flags);


	if (!is_on && dev->driver)
		dev->driver->disconnect(&dev->gadget);

	return 0;
	return 0;
}
}


@@ -2470,8 +2473,11 @@ static void stop_activity(struct net2280 *dev, struct usb_gadget_driver *driver)
		nuke(&dev->ep[i]);
		nuke(&dev->ep[i]);


	/* report disconnect; the driver is already quiesced */
	/* report disconnect; the driver is already quiesced */
	if (driver)
	if (driver) {
		spin_unlock(&dev->lock);
		driver->disconnect(&dev->gadget);
		driver->disconnect(&dev->gadget);
		spin_lock(&dev->lock);
	}


	usb_reinit(dev);
	usb_reinit(dev);
}
}
@@ -3345,6 +3351,8 @@ static void handle_stat0_irqs(struct net2280 *dev, u32 stat)
		BIT(PCI_RETRY_ABORT_INTERRUPT))
		BIT(PCI_RETRY_ABORT_INTERRUPT))


static void handle_stat1_irqs(struct net2280 *dev, u32 stat)
static void handle_stat1_irqs(struct net2280 *dev, u32 stat)
__releases(dev->lock)
__acquires(dev->lock)
{
{
	struct net2280_ep	*ep;
	struct net2280_ep	*ep;
	u32			tmp, num, mask, scratch;
	u32			tmp, num, mask, scratch;
@@ -3385,12 +3393,14 @@ static void handle_stat1_irqs(struct net2280 *dev, u32 stat)
			if (disconnect || reset) {
			if (disconnect || reset) {
				stop_activity(dev, dev->driver);
				stop_activity(dev, dev->driver);
				ep0_start(dev);
				ep0_start(dev);
				spin_unlock(&dev->lock);
				if (reset)
				if (reset)
					usb_gadget_udc_reset
					usb_gadget_udc_reset
						(&dev->gadget, dev->driver);
						(&dev->gadget, dev->driver);
				else
				else
					(dev->driver->disconnect)
					(dev->driver->disconnect)
						(&dev->gadget);
						(&dev->gadget);
				spin_lock(&dev->lock);
				return;
				return;
			}
			}
		}
		}
@@ -3409,6 +3419,7 @@ static void handle_stat1_irqs(struct net2280 *dev, u32 stat)
	tmp = BIT(SUSPEND_REQUEST_CHANGE_INTERRUPT);
	tmp = BIT(SUSPEND_REQUEST_CHANGE_INTERRUPT);
	if (stat & tmp) {
	if (stat & tmp) {
		writel(tmp, &dev->regs->irqstat1);
		writel(tmp, &dev->regs->irqstat1);
		spin_unlock(&dev->lock);
		if (stat & BIT(SUSPEND_REQUEST_INTERRUPT)) {
		if (stat & BIT(SUSPEND_REQUEST_INTERRUPT)) {
			if (dev->driver->suspend)
			if (dev->driver->suspend)
				dev->driver->suspend(&dev->gadget);
				dev->driver->suspend(&dev->gadget);
@@ -3419,6 +3430,7 @@ static void handle_stat1_irqs(struct net2280 *dev, u32 stat)
				dev->driver->resume(&dev->gadget);
				dev->driver->resume(&dev->gadget);
			/* at high speed, note erratum 0133 */
			/* at high speed, note erratum 0133 */
		}
		}
		spin_lock(&dev->lock);
		stat &= ~tmp;
		stat &= ~tmp;
	}
	}