]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
rseq: Add prctl() to enable time slice extensions
authorThomas Gleixner <tglx@linutronix.de>
Mon, 15 Dec 2025 16:52:12 +0000 (17:52 +0100)
committerPeter Zijlstra <peterz@infradead.org>
Thu, 22 Jan 2026 10:11:17 +0000 (11:11 +0100)
Implement a prctl() so that tasks can enable the time slice extension
mechanism. This fails, when time slice extensions are disabled at compile
time or on the kernel command line and when no rseq pointer is registered
in the kernel.

That allows to implement a single trivial check in the exit to user mode
hotpath, to decide whether the whole mechanism needs to be invoked.

Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Link: https://patch.msgid.link/20251215155708.858717691@linutronix.de
include/linux/rseq.h
include/uapi/linux/prctl.h
kernel/rseq.c
kernel/sys.c

index 2266f4dc77b6c5ef9ec33859405e935f72ffb544..3c194a02ad0a82c07359d80934221e90d8898043 100644 (file)
@@ -163,4 +163,13 @@ void rseq_syscall(struct pt_regs *regs);
 static inline void rseq_syscall(struct pt_regs *regs) { }
 #endif /* !CONFIG_DEBUG_RSEQ */
 
+#ifdef CONFIG_RSEQ_SLICE_EXTENSION
+int rseq_slice_extension_prctl(unsigned long arg2, unsigned long arg3);
+#else /* CONFIG_RSEQ_SLICE_EXTENSION */
+static inline int rseq_slice_extension_prctl(unsigned long arg2, unsigned long arg3)
+{
+       return -ENOTSUPP;
+}
+#endif /* !CONFIG_RSEQ_SLICE_EXTENSION */
+
 #endif /* _LINUX_RSEQ_H */
index 51c4e8c82b1e98899c00d2e4609ca35432176533..79944b7ae50aa8bc9b5918a9b4b81b325f7e25cd 100644 (file)
@@ -386,4 +386,14 @@ struct prctl_mm_map {
 # define PR_FUTEX_HASH_SET_SLOTS       1
 # define PR_FUTEX_HASH_GET_SLOTS       2
 
+/* RSEQ time slice extensions */
+#define PR_RSEQ_SLICE_EXTENSION                        79
+# define PR_RSEQ_SLICE_EXTENSION_GET           1
+# define PR_RSEQ_SLICE_EXTENSION_SET           2
+/*
+ * Bits for RSEQ_SLICE_EXTENSION_GET/SET
+ * PR_RSEQ_SLICE_EXT_ENABLE:   Enable
+ */
+# define PR_RSEQ_SLICE_EXT_ENABLE              0x01
+
 #endif /* _LINUX_PRCTL_H */
index 415d75b6df2c473615bf8697cbffcf042fa020ad..09848bb14ec2bd3e451195d9f9eb0d9aee88d40f 100644 (file)
@@ -71,6 +71,7 @@
 #define RSEQ_BUILD_SLOW_PATH
 
 #include <linux/debugfs.h>
+#include <linux/prctl.h>
 #include <linux/ratelimit.h>
 #include <linux/rseq_entry.h>
 #include <linux/sched.h>
@@ -501,6 +502,57 @@ efault:
 #ifdef CONFIG_RSEQ_SLICE_EXTENSION
 DEFINE_STATIC_KEY_TRUE(rseq_slice_extension_key);
 
+int rseq_slice_extension_prctl(unsigned long arg2, unsigned long arg3)
+{
+       switch (arg2) {
+       case PR_RSEQ_SLICE_EXTENSION_GET:
+               if (arg3)
+                       return -EINVAL;
+               return current->rseq.slice.state.enabled ? PR_RSEQ_SLICE_EXT_ENABLE : 0;
+
+       case PR_RSEQ_SLICE_EXTENSION_SET: {
+               u32 rflags, valid = RSEQ_CS_FLAG_SLICE_EXT_AVAILABLE;
+               bool enable = !!(arg3 & PR_RSEQ_SLICE_EXT_ENABLE);
+
+               if (arg3 & ~PR_RSEQ_SLICE_EXT_ENABLE)
+                       return -EINVAL;
+               if (!rseq_slice_extension_enabled())
+                       return -ENOTSUPP;
+               if (!current->rseq.usrptr)
+                       return -ENXIO;
+
+               /* No change? */
+               if (enable == !!current->rseq.slice.state.enabled)
+                       return 0;
+
+               if (get_user(rflags, &current->rseq.usrptr->flags))
+                       goto die;
+
+               if (current->rseq.slice.state.enabled)
+                       valid |= RSEQ_CS_FLAG_SLICE_EXT_ENABLED;
+
+               if ((rflags & valid) != valid)
+                       goto die;
+
+               rflags &= ~RSEQ_CS_FLAG_SLICE_EXT_ENABLED;
+               rflags |= RSEQ_CS_FLAG_SLICE_EXT_AVAILABLE;
+               if (enable)
+                       rflags |= RSEQ_CS_FLAG_SLICE_EXT_ENABLED;
+
+               if (put_user(rflags, &current->rseq.usrptr->flags))
+                       goto die;
+
+               current->rseq.slice.state.enabled = enable;
+               return 0;
+       }
+       default:
+               return -EINVAL;
+       }
+die:
+       force_sig(SIGSEGV);
+       return -EFAULT;
+}
+
 static int __init rseq_slice_cmdline(char *str)
 {
        bool on;
index 8b58eece4e580b883d19bb1336aff627ae783a4d..af71987df81ce626d0922511816b49c9de1b1a2e 100644 (file)
@@ -53,6 +53,7 @@
 #include <linux/time_namespace.h>
 #include <linux/binfmts.h>
 #include <linux/futex.h>
+#include <linux/rseq.h>
 
 #include <linux/sched.h>
 #include <linux/sched/autogroup.h>
@@ -2868,6 +2869,11 @@ SYSCALL_DEFINE5(prctl, int, option, unsigned long, arg2, unsigned long, arg3,
        case PR_FUTEX_HASH:
                error = futex_hash_prctl(arg2, arg3, arg4);
                break;
+       case PR_RSEQ_SLICE_EXTENSION:
+               if (arg4 || arg5)
+                       return -EINVAL;
+               error = rseq_slice_extension_prctl(arg2, arg3);
+               break;
        default:
                trace_task_prctl_unknown(option, arg2, arg3, arg4, arg5);
                error = -EINVAL;