summaryrefslogtreecommitdiff
path: root/kernel/irq/resend.c
diff options
context:
space:
mode:
authorJohan Hovold <johan+linaro@kernel.org>2023-08-26 17:40:04 +0200
committerThomas Gleixner <tglx@linutronix.de>2023-08-26 19:14:31 +0200
commit9f5deb551655a4cff04b21ecffdcdab75112da3a (patch)
tree59c207d9a1b28433cf6bebaa678f7e8aa5a90c1e /kernel/irq/resend.c
parentde990908521073d98a46ad0e29885df447e95242 (diff)
downloadlwn-9f5deb551655a4cff04b21ecffdcdab75112da3a.tar.gz
lwn-9f5deb551655a4cff04b21ecffdcdab75112da3a.zip
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@kernel.org> Signed-off-by: Thomas Gleixner <tglx@linutronix.de> Reviewed-by: Marc Zyngier <maz@kernel.org> Link: https://lore.kernel.org/lkml/20230809073432.4193-1-johan+linaro@kernel.org/ Link: https://lore.kernel.org/r/20230826154004.1417-1-johan+linaro@kernel.org
Diffstat (limited to 'kernel/irq/resend.c')
-rw-r--r--kernel/irq/resend.c7
1 files changed, 6 insertions, 1 deletions
diff --git a/kernel/irq/resend.c b/kernel/irq/resend.c
index edec335c0a7a..5f2c66860ac6 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;