Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

rseq: Split up rseq_exit_to_user_mode()

Separate the interrupt and syscall exit handling. Syscall exit does not
require to clear the user_irq bit as it can't be set. On interrupt exit it
can be set when the interrupt did not result in a scheduling event and
therefore the return path did not invoke the TIF work handling, which would
have cleared it.

The debug check for the event state is also not really required even when
debug mode is enabled via the static key. Debug mode is largely aiding user
space by enabling a larger amount of validation checks, which cause a
segfault when a malformed critical section is detected. In production mode
the critical section handling takes the content mostly as is and lets user
space keep the pieces when it screwed up.

On kernel changes in that area the state check is useful, but that can be
done when lockdep is enabled, which is anyway a required test scenario for
fundamental changes.

Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Signed-off-by: Ingo Molnar <mingo@kernel.org>
Reviewed-by: Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
Link: https://patch.msgid.link/20251027084307.842785700@linutronix.de

authored by

Thomas Gleixner and committed by
Ingo Molnar
7a5201ea 70fe25a3

+37 -5
+3 -3
include/linux/irq-entry-common.h
··· 240 240 static __always_inline void exit_to_user_mode_prepare_legacy(struct pt_regs *regs) 241 241 { 242 242 __exit_to_user_mode_prepare(regs); 243 - rseq_exit_to_user_mode(); 243 + rseq_exit_to_user_mode_legacy(); 244 244 __exit_to_user_mode_validate(); 245 245 } 246 246 ··· 254 254 static __always_inline void syscall_exit_to_user_mode_prepare(struct pt_regs *regs) 255 255 { 256 256 __exit_to_user_mode_prepare(regs); 257 - rseq_exit_to_user_mode(); 257 + rseq_syscall_exit_to_user_mode(); 258 258 __exit_to_user_mode_validate(); 259 259 } 260 260 ··· 268 268 static __always_inline void irqentry_exit_to_user_mode_prepare(struct pt_regs *regs) 269 269 { 270 270 __exit_to_user_mode_prepare(regs); 271 - rseq_exit_to_user_mode(); 271 + rseq_irqentry_exit_to_user_mode(); 272 272 __exit_to_user_mode_validate(); 273 273 } 274 274
+34 -2
include/linux/rseq_entry.h
··· 521 521 static inline bool rseq_exit_to_user_mode_restart(struct pt_regs *regs) { return false; } 522 522 #endif /* !CONFIG_GENERIC_ENTRY */ 523 523 524 - static __always_inline void rseq_exit_to_user_mode(void) 524 + static __always_inline void rseq_syscall_exit_to_user_mode(void) 525 + { 526 + struct rseq_event *ev = &current->rseq.event; 527 + 528 + rseq_stat_inc(rseq_stats.exit); 529 + 530 + /* Needed to remove the store for the !lockdep case */ 531 + if (IS_ENABLED(CONFIG_LOCKDEP)) { 532 + WARN_ON_ONCE(ev->sched_switch); 533 + ev->events = 0; 534 + } 535 + } 536 + 537 + static __always_inline void rseq_irqentry_exit_to_user_mode(void) 538 + { 539 + struct rseq_event *ev = &current->rseq.event; 540 + 541 + rseq_stat_inc(rseq_stats.exit); 542 + 543 + lockdep_assert_once(!ev->sched_switch); 544 + 545 + /* 546 + * Ensure that event (especially user_irq) is cleared when the 547 + * interrupt did not result in a schedule and therefore the 548 + * rseq processing could not clear it. 549 + */ 550 + ev->events = 0; 551 + } 552 + 553 + /* Required to keep ARM64 working */ 554 + static __always_inline void rseq_exit_to_user_mode_legacy(void) 525 555 { 526 556 struct rseq_event *ev = &current->rseq.event; 527 557 ··· 581 551 { 582 552 return false; 583 553 } 584 - static inline void rseq_exit_to_user_mode(void) { } 554 + static inline void rseq_syscall_exit_to_user_mode(void) { } 555 + static inline void rseq_irqentry_exit_to_user_mode(void) { } 556 + static inline void rseq_exit_to_user_mode_legacy(void) { } 585 557 static inline void rseq_debug_syscall_return(struct pt_regs *regs) { } 586 558 #endif /* !CONFIG_RSEQ */ 587 559