From: Jiazi Liu <jiazi.liu1984@gmail.com>
To: Thinh.Nguyen@synopsys.com
Cc: gregkh@linuxfoundation.org, linux-usb@vger.kernel.org,
linux-kernel@vger.kernel.org, stable@vger.kernel.org,
Jiazi Liu <liujiazi@amazon.com>
Subject: [PATCH 1/1] usb: dwc3: gadget: fix IRQ storm on invalid event buffer count
Date: Mon, 27 Jul 2026 17:40:15 +0800 [thread overview]
Message-ID: <20260727094015.5101-1-liujiazi@amazon.com> (raw)
When dwc3_check_event_buf() reads a GEVNTCOUNT value exceeding the
event buffer length, commit 63ccd26cd1f6 ("usb: dwc3: gadget: check
that event count does not exceed event buffer length") returns IRQ_NONE
without writing back GEVNTCOUNT. Since the DWC3 interrupt is
level-triggered, the uncleared IRQ source keeps the line asserted,
causing a tight IRQ storm that accumulates 99,900 unhandled interrupts
and triggers spurious.c:184 BUG -> kernel panic.
The resulting call stack:
__report_bad_irq+0xac/0xc8
note_interrupt+0x340/0x468
handle_irq_event+0xac/0xc0
handle_fasteoi_irq+0x120/0x228
gic_handle_irq+0x68/0x108
...
kernel BUG at kernel/irq/spurious.c:184
To reproduce, write a bogus value exceeding the event buffer length
directly to the GEVNTCOUNT register:
devmem <DWC3_BASE + 0xc40c> 4 0x1004
Write the bogus count back to GEVNTCOUNT to clear the IRQ source,
consistent with the stale event clearing pattern in
dwc3_event_buffers_setup(), and schedule a soft disconnect via
softcon_work to recover the controller state.
Fixes: 63ccd26cd1f6 ("usb: dwc3: gadget: check that event count does not exceed event buffer length")
Cc: stable@vger.kernel.org
Signed-off-by: Jiazi Liu <liujiazi@amazon.com>
---
drivers/usb/dwc3/core.h | 2 ++
drivers/usb/dwc3/gadget.c | 23 ++++++++++++++++++++++-
2 files changed, 24 insertions(+), 1 deletion(-)
diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h
index 608daeb7ef10..0881ed7e956e 100644
--- a/drivers/usb/dwc3/core.h
+++ b/drivers/usb/dwc3/core.h
@@ -1004,6 +1004,7 @@ struct dwc3_glue_ops {
/**
* struct dwc3 - representation of our controller
* @drd_work: workqueue used for role swapping
+ * @softcon_work: workqueue used for soft connect/disconnect recovery
* @ep0_trb: trb which is used for the ctrl_req
* @bounce: address of bounce buffer
* @setup_buf: used while precessing STD USB requests
@@ -1189,6 +1190,7 @@ struct dwc3_glue_ops {
*/
struct dwc3 {
struct work_struct drd_work;
+ struct work_struct softcon_work;
struct dwc3_trb *ep0_trb;
void *bounce;
u8 *setup_buf;
diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
index fa0f16ffafef..9b24259272a4 100644
--- a/drivers/usb/dwc3/gadget.c
+++ b/drivers/usb/dwc3/gadget.c
@@ -4669,7 +4669,18 @@ static irqreturn_t dwc3_check_event_buf(struct dwc3_event_buffer *evt)
if (count > evt->length) {
dev_err_ratelimited(dwc->dev, "invalid count(%u) > evt->length(%u)\n",
count, evt->length);
- return IRQ_NONE;
+ /*
+ * The DWC3 interrupt is level-triggered. Returning IRQ_NONE
+ * without clearing the IRQ source leaves the line asserted,
+ * causing a tight IRQ storm that triggers spurious.c:184 BUG.
+ * Write the bogus count back to GEVNTCOUNT to clear the source,
+ * consistent with the stale event clearing in
+ * dwc3_event_buffers_setup(), then schedule a soft disconnect
+ * to recover the controller state.
+ */
+ dwc3_writel(dwc->regs, DWC3_GEVNTCOUNT(0), count);
+ schedule_work(&dwc->softcon_work);
+ return IRQ_HANDLED;
}
evt->count = count;
@@ -4729,6 +4740,14 @@ static void dwc_gadget_release(struct device *dev)
kfree(gadget);
}
+static void dwc3_softcon_work(struct work_struct *work)
+{
+ struct dwc3 *dwc = container_of(work, struct dwc3, softcon_work);
+
+ dev_err(dwc->dev, "event buffer error: performing soft disconnect\n");
+ dwc3_gadget_soft_disconnect(dwc);
+}
+
/**
* dwc3_gadget_init - initializes gadget related registers
* @dwc: pointer to our controller context structure
@@ -4772,6 +4791,7 @@ int dwc3_gadget_init(struct dwc3 *dwc)
}
init_completion(&dwc->ep0_in_setup);
+ INIT_WORK(&dwc->softcon_work, dwc3_softcon_work);
dwc->gadget = kzalloc_obj(struct usb_gadget);
if (!dwc->gadget) {
ret = -ENOMEM;
@@ -4868,6 +4888,7 @@ void dwc3_gadget_exit(struct dwc3 *dwc)
if (!dwc->gadget)
return;
+ cancel_work_sync(&dwc->softcon_work);
dwc3_enable_susphy(dwc, true);
usb_del_gadget(dwc->gadget);
dwc3_gadget_free_endpoints(dwc);
--
2.50.1 (Apple Git-155)
next reply other threads:[~2026-07-27 9:40 UTC|newest]
Thread overview: 5+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-07-27 9:40 Jiazi Liu [this message]
2026-08-04 23:04 ` [PATCH 1/1] usb: dwc3: gadget: fix IRQ storm on invalid event buffer count Thinh Nguyen
2026-08-06 9:03 ` kernel test robot
2026-08-07 11:04 ` Liu Jiazi
2026-08-07 23:09 ` Thinh Nguyen
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260727094015.5101-1-liujiazi@amazon.com \
--to=jiazi.liu1984@gmail.com \
--cc=Thinh.Nguyen@synopsys.com \
--cc=gregkh@linuxfoundation.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-usb@vger.kernel.org \
--cc=liujiazi@amazon.com \
--cc=stable@vger.kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.