[tip: irq/urgent] genirq: Fix software resend lockup and nested resend

From: tip-bot2 for Johan Hovold
Date: Sat Aug 26 2023 - 13:18:39 EST


The following commit has been merged into the irq/urgent branch of tip:

Commit-ID: 9f5deb551655a4cff04b21ecffdcdab75112da3a
Gitweb: https://git.kernel.org/tip/9f5deb551655a4cff04b21ecffdcdab75112da3a
Author: Johan Hovold <johan+linaro@xxxxxxxxxx>
AuthorDate: Sat, 26 Aug 2023 17:40:04 +02:00
Committer: Thomas Gleixner <tglx@xxxxxxxxxxxxx>
CommitterDate: Sat, 26 Aug 2023 19:14:31 +02:00

genirq: Fix software resend lockup and nested resend

The switch to using hlist for managing software resend of interrupts
broke resend in at least two ways:

First, unconditionally adding interrupt descriptors to the resend list can
corrupt the list when the descriptor in question has already been
added. This causes the resend tasklet to loop indefinitely with interrupts
disabled as was recently reported with the Lenovo ThinkPad X13s after
threaded NAPI was disabled in the ath11k WiFi driver.

This bug is easily fixed by restoring the old semantics of irq_sw_resend()
so that it can be called also for descriptors that have already been marked
for resend.

Second, the offending commit also broke software resend of nested
interrupts by simply discarding the code that made sure that such
interrupts are retriggered using the parent interrupt.

Add back the corresponding code that adds the parent descriptor to the
resend list.

Fixes: bc06a9e08742 ("genirq: Use hlist for managing resend handlers")
Signed-off-by: Johan Hovold <johan+linaro@xxxxxxxxxx>
Signed-off-by: Thomas Gleixner <tglx@xxxxxxxxxxxxx>
Reviewed-by: Marc Zyngier <maz@xxxxxxxxxx>
Link: https://lore.kernel.org/lkml/20230809073432.4193-1-johan+linaro@xxxxxxxxxx/
Link: https://lore.kernel.org/r/20230826154004.1417-1-johan+linaro@xxxxxxxxxx

---
kernel/irq/resend.c | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)

diff --git a/kernel/irq/resend.c b/kernel/irq/resend.c
index edec335..5f2c668 100644
--- a/kernel/irq/resend.c
+++ b/kernel/irq/resend.c
@@ -68,11 +68,16 @@ static int irq_sw_resend(struct irq_desc *desc)
*/
if (!desc->parent_irq)
return -EINVAL;
+
+ desc = irq_to_desc(desc->parent_irq);
+ if (!desc)
+ return -EINVAL;
}

/* Add to resend_list and activate the softirq: */
raw_spin_lock(&irq_resend_lock);
- hlist_add_head(&desc->resend_node, &irq_resend_list);
+ if (hlist_unhashed(&desc->resend_node))
+ hlist_add_head(&desc->resend_node, &irq_resend_list);
raw_spin_unlock(&irq_resend_lock);
tasklet_schedule(&resend_tasklet);
return 0;