When an interrupt controller uses a function such as handle_level_irq() as an interrupt handler and the controller implements the irq_disable() callback, the following scenario will appear in the i2c-hid driver in the sleep scenario:
in the sleep flow, while the user is still triggering the i2c-hid interrupt, we get the following function call:
handle_level_irq() -> mask_ack_irq() -> mask_irq() i2c_hid_core_suspend() -> disable_irq() -> __irq_disable() -> irq_state_set_disabled() -> irq_state_set_masked()
irq_thread_fn() -> irq_finalize_oneshot() -> if (!desc->threads_oneshot && !irqd_irq_disabled() && irqd_irq_masked()) unmask_threaded_irq() -> unmask_irq()
That is, when __irq_disable() is called between mask_irq() and irq_finalize_oneshot(), the code in irq_finalize_oneshot() will cause the !irqd_irq_disabled() fails to enter the unmask_irq() branch, which causes mask_irq/unmask_irq to be called unpaired and the i2c-hid interrupt to be masked.
Since mask_irq/unmask_irq and irq_disabled() belong to two different hardware registers or policies, the !irqd_irq_disabled() assertion may not be used to determine whether unmask_irq() needs to be called.
Cc: stable@vger.kernel.org Signed-off-by: xiongxin xiongxin@kylinos.cn Signed-off-by: Riwen Lu luriwen@kylinos.cn
diff --git a/kernel/irq/manage.c b/kernel/irq/manage.c index 1782f90cd8c6..9160fc9170b3 100644 --- a/kernel/irq/manage.c +++ b/kernel/irq/manage.c @@ -1120,8 +1120,7 @@ static void irq_finalize_oneshot(struct irq_desc *desc,
desc->threads_oneshot &= ~action->thread_mask;
- if (!desc->threads_oneshot && !irqd_irq_disabled(&desc->irq_data) && - irqd_irq_masked(&desc->irq_data)) + if (!desc->threads_oneshot && irqd_irq_masked(&desc->irq_data)) unmask_threaded_irq(desc);
out_unlock: