From: Sven Schnelle svens@linux.ibm.com
commit c9b12b59e2ea4c3c7cedec7efb071b649652f3a9 upstream.
In the current code, when exiting from idle, rcu_irq_enter() is called twice during irq entry:
irq_entry_enter()-> rcu_irq_enter() irq_enter() -> rcu_irq_enter()
This may lead to wrong results from rcu_is_cpu_rrupt_from_idle() because of a wrong dynticks nmi nesting count. Fix this by only calling irq_enter_rcu().
Cc: stable@vger.kernel.org # 5.12+ Reported-by: Mark Rutland mark.rutland@arm.com Fixes: 56e62a737028 ("s390: convert to generic entry") Signed-off-by: Sven Schnelle svens@linux.ibm.com Signed-off-by: Heiko Carstens hca@linux.ibm.com Signed-off-by: Greg Kroah-Hartman gregkh@linuxfoundation.org --- arch/s390/kernel/irq.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-)
--- a/arch/s390/kernel/irq.c +++ b/arch/s390/kernel/irq.c @@ -138,7 +138,7 @@ void noinstr do_io_irq(struct pt_regs *r struct pt_regs *old_regs = set_irq_regs(regs); int from_idle;
- irq_enter(); + irq_enter_rcu();
if (user_mode(regs)) update_timer_sys(); @@ -155,7 +155,8 @@ void noinstr do_io_irq(struct pt_regs *r do_irq_async(regs, IO_INTERRUPT); } while (MACHINE_IS_LPAR && irq_pending(regs));
- irq_exit(); + irq_exit_rcu(); + set_irq_regs(old_regs); irqentry_exit(regs, state);
@@ -169,7 +170,7 @@ void noinstr do_ext_irq(struct pt_regs * struct pt_regs *old_regs = set_irq_regs(regs); int from_idle;
- irq_enter(); + irq_enter_rcu();
if (user_mode(regs)) update_timer_sys(); @@ -184,7 +185,7 @@ void noinstr do_ext_irq(struct pt_regs *
do_irq_async(regs, EXT_INTERRUPT);
- irq_exit(); + irq_exit_rcu(); set_irq_regs(old_regs); irqentry_exit(regs, state);