s390/entry: fix duplicate tracking of irq nesting level

commit c9b12b59e2 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: 56e62a7370 ("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>
This commit is contained in:
Sven Schnelle 2021-12-06 11:50:16 +01:00 коммит произвёл Greg Kroah-Hartman
Родитель b08b3bfcc7
Коммит 2eeff00926
1 изменённых файлов: 5 добавлений и 4 удалений

Просмотреть файл

@ -138,7 +138,7 @@ void noinstr do_io_irq(struct pt_regs *regs)
struct pt_regs *old_regs = set_irq_regs(regs); struct pt_regs *old_regs = set_irq_regs(regs);
int from_idle; int from_idle;
irq_enter(); irq_enter_rcu();
if (user_mode(regs)) if (user_mode(regs))
update_timer_sys(); update_timer_sys();
@ -155,7 +155,8 @@ void noinstr do_io_irq(struct pt_regs *regs)
do_irq_async(regs, IO_INTERRUPT); do_irq_async(regs, IO_INTERRUPT);
} while (MACHINE_IS_LPAR && irq_pending(regs)); } while (MACHINE_IS_LPAR && irq_pending(regs));
irq_exit(); irq_exit_rcu();
set_irq_regs(old_regs); set_irq_regs(old_regs);
irqentry_exit(regs, state); irqentry_exit(regs, state);
@ -169,7 +170,7 @@ void noinstr do_ext_irq(struct pt_regs *regs)
struct pt_regs *old_regs = set_irq_regs(regs); struct pt_regs *old_regs = set_irq_regs(regs);
int from_idle; int from_idle;
irq_enter(); irq_enter_rcu();
if (user_mode(regs)) if (user_mode(regs))
update_timer_sys(); update_timer_sys();
@ -184,7 +185,7 @@ void noinstr do_ext_irq(struct pt_regs *regs)
do_irq_async(regs, EXT_INTERRUPT); do_irq_async(regs, EXT_INTERRUPT);
irq_exit(); irq_exit_rcu();
set_irq_regs(old_regs); set_irq_regs(old_regs);
irqentry_exit(regs, state); irqentry_exit(regs, state);