]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
rseq: Split up rseq_exit_to_user_mode()
authorThomas Gleixner <tglx@linutronix.de>
Mon, 27 Oct 2025 08:45:24 +0000 (09:45 +0100)
committerIngo Molnar <mingo@kernel.org>
Tue, 4 Nov 2025 07:35:30 +0000 (08:35 +0100)
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
include/linux/irq-entry-common.h
include/linux/rseq_entry.h

index 5ea61722bb708d75cb8b37dc361e860ebed4496d..bc5d178e0b912b79a34fb63a05bd859324f61212 100644 (file)
@@ -240,7 +240,7 @@ static __always_inline void __exit_to_user_mode_validate(void)
 static __always_inline void exit_to_user_mode_prepare_legacy(struct pt_regs *regs)
 {
        __exit_to_user_mode_prepare(regs);
-       rseq_exit_to_user_mode();
+       rseq_exit_to_user_mode_legacy();
        __exit_to_user_mode_validate();
 }
 
@@ -254,7 +254,7 @@ static __always_inline void exit_to_user_mode_prepare_legacy(struct pt_regs *reg
 static __always_inline void syscall_exit_to_user_mode_prepare(struct pt_regs *regs)
 {
        __exit_to_user_mode_prepare(regs);
-       rseq_exit_to_user_mode();
+       rseq_syscall_exit_to_user_mode();
        __exit_to_user_mode_validate();
 }
 
@@ -268,7 +268,7 @@ static __always_inline void syscall_exit_to_user_mode_prepare(struct pt_regs *re
 static __always_inline void irqentry_exit_to_user_mode_prepare(struct pt_regs *regs)
 {
        __exit_to_user_mode_prepare(regs);
-       rseq_exit_to_user_mode();
+       rseq_irqentry_exit_to_user_mode();
        __exit_to_user_mode_validate();
 }
 
index 3f13be7301fa82359f77bac9c14affa9b8b88094..958a63eeb2d3fb6038fd6ce0a8f21afc27dd9538 100644 (file)
@@ -521,7 +521,37 @@ static __always_inline bool rseq_exit_to_user_mode_restart(struct pt_regs *regs)
 static inline bool rseq_exit_to_user_mode_restart(struct pt_regs *regs) { return false; }
 #endif /* !CONFIG_GENERIC_ENTRY */
 
-static __always_inline void rseq_exit_to_user_mode(void)
+static __always_inline void rseq_syscall_exit_to_user_mode(void)
+{
+       struct rseq_event *ev = &current->rseq.event;
+
+       rseq_stat_inc(rseq_stats.exit);
+
+       /* Needed to remove the store for the !lockdep case */
+       if (IS_ENABLED(CONFIG_LOCKDEP)) {
+               WARN_ON_ONCE(ev->sched_switch);
+               ev->events = 0;
+       }
+}
+
+static __always_inline void rseq_irqentry_exit_to_user_mode(void)
+{
+       struct rseq_event *ev = &current->rseq.event;
+
+       rseq_stat_inc(rseq_stats.exit);
+
+       lockdep_assert_once(!ev->sched_switch);
+
+       /*
+        * Ensure that event (especially user_irq) is cleared when the
+        * interrupt did not result in a schedule and therefore the
+        * rseq processing could not clear it.
+        */
+       ev->events = 0;
+}
+
+/* Required to keep ARM64 working */
+static __always_inline void rseq_exit_to_user_mode_legacy(void)
 {
        struct rseq_event *ev = &current->rseq.event;
 
@@ -551,7 +581,9 @@ static inline bool rseq_exit_to_user_mode_restart(struct pt_regs *regs)
 {
        return false;
 }
-static inline void rseq_exit_to_user_mode(void) { }
+static inline void rseq_syscall_exit_to_user_mode(void) { }
+static inline void rseq_irqentry_exit_to_user_mode(void) { }
+static inline void rseq_exit_to_user_mode_legacy(void) { }
 static inline void rseq_debug_syscall_return(struct pt_regs *regs) { }
 #endif /* !CONFIG_RSEQ */