diff mbox series

Input: gpio-keys - fix a sleep while atomic with PREEMPT_RT

Message ID 20250526-gpio_keys_preempt_rt-v1-1-09ddadf8e19d@foss.st.com
State New
Headers show
Series Input: gpio-keys - fix a sleep while atomic with PREEMPT_RT | expand

Commit Message

Gatien CHEVALLIER May 26, 2025, 1:56 p.m. UTC
From: Fabrice Gasnier <fabrice.gasnier@foss.st.com>

When enabling PREEMPT_RT, the gpio_keys_irq_timer() callback runs in
hard irq context, but the input_event() takes a spin_lock, which isn't
allowed there as it is converted to a rt_spin_lock().

[ 4054.289999] BUG: sleeping function called from invalid context at kernel/locking/spinlock_rt.c:48
[ 4054.290028] in_atomic(): 1, irqs_disabled(): 1, non_block: 0, pid: 0, name: swapper/0
...
[ 4054.290195]  __might_resched+0x13c/0x1f4
[ 4054.290209]  rt_spin_lock+0x54/0x11c
[ 4054.290219]  input_event+0x48/0x80
[ 4054.290230]  gpio_keys_irq_timer+0x4c/0x78
[ 4054.290243]  __hrtimer_run_queues+0x1a4/0x438
[ 4054.290257]  hrtimer_interrupt+0xe4/0x240
[ 4054.290269]  arch_timer_handler_phys+0x2c/0x44
[ 4054.290283]  handle_percpu_devid_irq+0x8c/0x14c
[ 4054.290297]  handle_irq_desc+0x40/0x58
[ 4054.290307]  generic_handle_domain_irq+0x1c/0x28
[ 4054.290316]  gic_handle_irq+0x44/0xcc

Considering the gpio_keys_irq_isr() can run in any context, e.g. it can
be threaded, it seems there's no point in requesting the timer isr to
run in hard irq context.

So relax the hrtimer not to use the hard context. This requires the
spin_lock to be added back in gpio_keys_irq_timer().

Fixes: 019002f20cb5 ("Input: gpio-keys - use hrtimer for release timer")
Suggested-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
Signed-off-by: Fabrice Gasnier <fabrice.gasnier@foss.st.com>
Signed-off-by: Gatien Chevallier <gatien.chevallier@foss.st.com>
---
 drivers/input/keyboard/gpio_keys.c | 7 +++++--
 1 file changed, 5 insertions(+), 2 deletions(-)


---
base-commit: 0ff41df1cb268fc69e703a08a57ee14ae967d0ca
change-id: 20250526-gpio_keys_preempt_rt-10619c8fa916

Best regards,

Comments

Sebastian Andrzej Siewior May 26, 2025, 2:13 p.m. UTC | #1
On 2025-05-26 15:56:29 [+0200], Gatien Chevallier wrote:
> From: Fabrice Gasnier <fabrice.gasnier@foss.st.com>
> 
> When enabling PREEMPT_RT, the gpio_keys_irq_timer() callback runs in
> hard irq context, but the input_event() takes a spin_lock, which isn't
> allowed there as it is converted to a rt_spin_lock().
> 
> [ 4054.289999] BUG: sleeping function called from invalid context at kernel/locking/spinlock_rt.c:48
> [ 4054.290028] in_atomic(): 1, irqs_disabled(): 1, non_block: 0, pid: 0, name: swapper/0
> ...
> [ 4054.290195]  __might_resched+0x13c/0x1f4
> [ 4054.290209]  rt_spin_lock+0x54/0x11c
> [ 4054.290219]  input_event+0x48/0x80
> [ 4054.290230]  gpio_keys_irq_timer+0x4c/0x78
> [ 4054.290243]  __hrtimer_run_queues+0x1a4/0x438
> [ 4054.290257]  hrtimer_interrupt+0xe4/0x240
> [ 4054.290269]  arch_timer_handler_phys+0x2c/0x44
> [ 4054.290283]  handle_percpu_devid_irq+0x8c/0x14c
> [ 4054.290297]  handle_irq_desc+0x40/0x58
> [ 4054.290307]  generic_handle_domain_irq+0x1c/0x28
> [ 4054.290316]  gic_handle_irq+0x44/0xcc
> 
> Considering the gpio_keys_irq_isr() can run in any context, e.g. it can
> be threaded, it seems there's no point in requesting the timer isr to
> run in hard irq context.
> 
> So relax the hrtimer not to use the hard context. This requires the
> spin_lock to be added back in gpio_keys_irq_timer().

Why does it? This needs to be explained or it deserves an independent
patch/ fix. This flag change makes not difference on !PREEMPT_RT and so
should be the requirements for locking here.

> Fixes: 019002f20cb5 ("Input: gpio-keys - use hrtimer for release timer")
> Suggested-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
> Signed-off-by: Fabrice Gasnier <fabrice.gasnier@foss.st.com>
> Signed-off-by: Gatien Chevallier <gatien.chevallier@foss.st.com>

Sebastian
Gatien CHEVALLIER May 27, 2025, 1:36 p.m. UTC | #2
Hello Sebastian,

On 5/26/25 16:13, Sebastian Andrzej Siewior wrote:
> On 2025-05-26 15:56:29 [+0200], Gatien Chevallier wrote:
>> From: Fabrice Gasnier <fabrice.gasnier@foss.st.com>
>>
>> When enabling PREEMPT_RT, the gpio_keys_irq_timer() callback runs in
>> hard irq context, but the input_event() takes a spin_lock, which isn't
>> allowed there as it is converted to a rt_spin_lock().
>>
>> [ 4054.289999] BUG: sleeping function called from invalid context at kernel/locking/spinlock_rt.c:48
>> [ 4054.290028] in_atomic(): 1, irqs_disabled(): 1, non_block: 0, pid: 0, name: swapper/0
>> ...
>> [ 4054.290195]  __might_resched+0x13c/0x1f4
>> [ 4054.290209]  rt_spin_lock+0x54/0x11c
>> [ 4054.290219]  input_event+0x48/0x80
>> [ 4054.290230]  gpio_keys_irq_timer+0x4c/0x78
>> [ 4054.290243]  __hrtimer_run_queues+0x1a4/0x438
>> [ 4054.290257]  hrtimer_interrupt+0xe4/0x240
>> [ 4054.290269]  arch_timer_handler_phys+0x2c/0x44
>> [ 4054.290283]  handle_percpu_devid_irq+0x8c/0x14c
>> [ 4054.290297]  handle_irq_desc+0x40/0x58
>> [ 4054.290307]  generic_handle_domain_irq+0x1c/0x28
>> [ 4054.290316]  gic_handle_irq+0x44/0xcc
>>
>> Considering the gpio_keys_irq_isr() can run in any context, e.g. it can
>> be threaded, it seems there's no point in requesting the timer isr to
>> run in hard irq context.
>>
>> So relax the hrtimer not to use the hard context. This requires the
>> spin_lock to be added back in gpio_keys_irq_timer().
> 
> Why does it? This needs to be explained or it deserves an independent
> patch/ fix. This flag change makes not difference on !PREEMPT_RT and so
> should be the requirements for locking here.
> 

Can you elaborate on "This flag change makes not difference on
!PREEMPT_RT" please? IIUC,this makes the callback not run in hard IRQ
context, even in !PREEMPT_RT, no?

Regarding the need of the spin_lock: gpio_keys_irq_timer() and
gpio_keys_irq_isr() appear to access the same resources. Can't we
have a concurrent access on it from:
HR timer interrupt // GPIO interrupt?

But looking back at the patch, this situation does not depend on
the HRTIMER_MODE_REL_HARD flag. So maybe it should be addressed
separately.

On the other hand, I should use the new
guard(spinlock_irqsave)(&bdata->lock);

>> Fixes: 019002f20cb5 ("Input: gpio-keys - use hrtimer for release timer")
>> Suggested-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
>> Signed-off-by: Fabrice Gasnier <fabrice.gasnier@foss.st.com>
>> Signed-off-by: Gatien Chevallier <gatien.chevallier@foss.st.com>
> 
> Sebastian
Sebastian Andrzej Siewior May 27, 2025, 2:41 p.m. UTC | #3
On 2025-05-27 15:36:37 [+0200], Gatien CHEVALLIER wrote:
> Hello Sebastian,
Hello Gatien,

> Can you elaborate on "This flag change makes not difference on
> !PREEMPT_RT" please? IIUC,this makes the callback not run in hard IRQ
> context, even in !PREEMPT_RT, no?

If you set
- HRTIMER_MODE_REL_HARD
  then the callback runs in
  - hardirq context on !PREEMPT_RT
  - hardirq context on PREEMPT_RT.

- HRTIMER_MODE_REL
  then the callback runs in
  - hardirq context on !PREEMPT_RT
  - preemptible softirq on PREEMPT_RT.

- HRTIMER_MODE_REL_SOFT
  then the callback runs in
  - softirq context on !PREEMPT_RT
  - preemptible softirq on PREEMPT_RT.

Therefore if you switch HRTIMER_MODE_REL_HARD -> HRTIMER_MODE_REL then
it is a nop on !PREEMPT_RT.

> Regarding the need of the spin_lock: gpio_keys_irq_timer() and
> gpio_keys_irq_isr() appear to access the same resources. Can't we
> have a concurrent access on it from:
> HR timer interrupt // GPIO interrupt?

Yes, it could.

> But looking back at the patch, this situation does not depend on
> the HRTIMER_MODE_REL_HARD flag. So maybe it should be addressed
> separately.

Yes, please.

> On the other hand, I should use the new
> guard(spinlock_irqsave)(&bdata->lock);

Yes, please. The other instance already does so.

Sebastian
Gatien CHEVALLIER May 28, 2025, 7:55 a.m. UTC | #4
Hello Sebastian,

On 5/27/25 16:41, Sebastian Andrzej Siewior wrote:
> On 2025-05-27 15:36:37 [+0200], Gatien CHEVALLIER wrote:
>> Hello Sebastian,
> Hello Gatien,
> 
>> Can you elaborate on "This flag change makes not difference on
>> !PREEMPT_RT" please? IIUC,this makes the callback not run in hard IRQ
>> context, even in !PREEMPT_RT, no?
> 
> If you set
> - HRTIMER_MODE_REL_HARD
>    then the callback runs in
>    - hardirq context on !PREEMPT_RT
>    - hardirq context on PREEMPT_RT.
> 
> - HRTIMER_MODE_REL
>    then the callback runs in
>    - hardirq context on !PREEMPT_RT
>    - preemptible softirq on PREEMPT_RT.
> 
> - HRTIMER_MODE_REL_SOFT
>    then the callback runs in
>    - softirq context on !PREEMPT_RT
>    - preemptible softirq on PREEMPT_RT.
> 
> Therefore if you switch HRTIMER_MODE_REL_HARD -> HRTIMER_MODE_REL then
> it is a nop on !PREEMPT_RT.
> 

Thank you for the details.

>> Regarding the need of the spin_lock: gpio_keys_irq_timer() and
>> gpio_keys_irq_isr() appear to access the same resources. Can't we
>> have a concurrent access on it from:
>> HR timer interrupt // GPIO interrupt?
> 
> Yes, it could.
> 
>> But looking back at the patch, this situation does not depend on
>> the HRTIMER_MODE_REL_HARD flag. So maybe it should be addressed
>> separately.
> 
> Yes, please.
> 

Ok, I will do that in V2

>> On the other hand, I should use the new
>> guard(spinlock_irqsave)(&bdata->lock);
> 
> Yes, please. The other instance already does so.
> 
> Sebastian

Ok

Best regards,
Gatien
diff mbox series

Patch

diff --git a/drivers/input/keyboard/gpio_keys.c b/drivers/input/keyboard/gpio_keys.c
index 5c39a217b94c8ad03a8542380eed741fccdca5da..99fc7d3c29ea5809604915782525fecacb151612 100644
--- a/drivers/input/keyboard/gpio_keys.c
+++ b/drivers/input/keyboard/gpio_keys.c
@@ -448,12 +448,15 @@  static enum hrtimer_restart gpio_keys_irq_timer(struct hrtimer *t)
 						      struct gpio_button_data,
 						      release_timer);
 	struct input_dev *input = bdata->input;
+	unsigned long flags;
 
+	spin_lock_irqsave(&bdata->lock, flags);
 	if (bdata->key_pressed) {
 		input_report_key(input, *bdata->code, 0);
 		input_sync(input);
 		bdata->key_pressed = false;
 	}
+	spin_unlock_irqrestore(&bdata->lock, flags);
 
 	return HRTIMER_NORESTART;
 }
@@ -486,7 +489,7 @@  static irqreturn_t gpio_keys_irq_isr(int irq, void *dev_id)
 	if (bdata->release_delay)
 		hrtimer_start(&bdata->release_timer,
 			      ms_to_ktime(bdata->release_delay),
-			      HRTIMER_MODE_REL_HARD);
+			      HRTIMER_MODE_REL);
 out:
 	return IRQ_HANDLED;
 }
@@ -628,7 +631,7 @@  static int gpio_keys_setup_key(struct platform_device *pdev,
 
 		bdata->release_delay = button->debounce_interval;
 		hrtimer_setup(&bdata->release_timer, gpio_keys_irq_timer,
-			      CLOCK_REALTIME, HRTIMER_MODE_REL_HARD);
+			      CLOCK_REALTIME, HRTIMER_MODE_REL);
 
 		isr = gpio_keys_irq_isr;
 		irqflags = 0;