Skip to contentSkip to conversation

USB: chipidea: fix interrupt deadlock

PATCH New Johan Hovold 1 participant 1 message Nov 15, 2021

Subsystems (2)
lore
Share & tools
lore
#0 lore raw

PATCH 1/1 USB: chipidea: fix interrupt deadlock

commit 9aaa81c3366e8393a62374e3a1c67c69edc07b8a upstream.

Chipidea core was calling the interrupt handler from non-IRQ context with interrupts enabled, something which can lead to a deadlock if there's an actual interrupt trying to take a lock that's already held (e.g. the controller lock in udc_irq()).

Add a wrapper that can be used to fake interrupts instead of calling the handler directly.

Fixes 3ecb3e09b042 ("usb: chipidea: Use extcon framework for VBUS and ID detect")
Fixes 876d4e1e8298 ("usb: chipidea: core: add wakeup support for extcon")
Cc Peter Chen <[email protected]>
[ johan: backport to 4.19; adjust context ]

---

This one should apply 4.14 as well.

Johan

 drivers/usb/chipidea/core.c | 19 ++++++++++++++-----
 1 file changed, 14 insertions(+), 5 deletions(-)
drivers/usb/chipidea/core.c +14 -5
index befd9235bcad..c13f9a153a5c 100644
@@ -532,7 +532,7 @@ int hw_device_reset(struct ci_hdrc *ci)
 	return 0;
 }
 
-static irqreturn_t ci_irq(int irq, void *data)
+static irqreturn_t ci_irq_handler(int irq, void *data)
 {
 	struct ci_hdrc *ci = data;
 	irqreturn_t ret = IRQ_NONE;
@@ -585,6 +585,15 @@ static irqreturn_t ci_irq(int irq, void *data)
 	return ret;
 }
 
+static void ci_irq(struct ci_hdrc *ci)
+{
+	unsigned long flags;
+
+	local_irq_save(flags);
+	ci_irq_handler(ci->irq, ci);
+	local_irq_restore(flags);
+}
+
 static int ci_cable_notifier(struct notifier_block *nb, unsigned long event,
 			     void *ptr)
 {
@@ -594,7 +603,7 @@ static int ci_cable_notifier(struct notifier_block *nb, unsigned long event,
 	cbl->connected = event;
 	cbl->changed = true;
 
-	ci_irq(ci->irq, ci);
+	ci_irq(ci);
 	return NOTIFY_DONE;
 }
 
@@ -1048,7 +1057,7 @@ static int ci_hdrc_probe(struct platform_device *pdev)
 		}
 	}
 
-	ret = devm_request_irq(dev, ci->irq, ci_irq, IRQF_SHARED,
+	ret = devm_request_irq(dev, ci->irq, ci_irq_handler, IRQF_SHARED,
 			ci->platdata->name, ci);
 	if (ret)
 		goto stop;
@@ -1170,11 +1179,11 @@ static void ci_extcon_wakeup_int(struct ci_hdrc *ci)
 
 	if (!IS_ERR(cable_id->edev) && ci->is_otg &&
 		(otgsc & OTGSC_IDIE) && (otgsc & OTGSC_IDIS))
-		ci_irq(ci->irq, ci);
+		ci_irq(ci);
 
 	if (!IS_ERR(cable_vbus->edev) && ci->is_otg &&
 		(otgsc & OTGSC_BSVIE) && (otgsc & OTGSC_BSVIS))
-		ci_irq(ci->irq, ci);
+		ci_irq(ci);
 }
 
 static int ci_controller_resume(struct device *dev)
Signature · 1 line
2.32.0