Re: [RFC PATCH 2/5] rseq: add per-task rseq operation state

From: Dmitry Vyukov

Date: Sat Aug 29 2026 - 18:37:55 EST


On Fri, 28 Aug 2026 at 17:34, <odion@xxxxxxxxxxxx> wrote:
>
> From: Olivier Dion <odion@xxxxxxxxxxxx>
>
> Add the task-side state for rseq operations: the rseq_event::rseq_op
> state indicating operation processing is enabled for the task, and the
> nr_ops counter in struct rseq_data. nr_ops is edge triggered: its 0<->1
> transition enables/disables rseq_event::rseq_op and the user visible
> RSEQ_CS_FLAG_RSEQ_OP_ENABLED flag. This makes so that only thread that
> enable RSEQ operations through prctl takes a performance hit.
>
> Include rseq_op in the sched-switch raise condition so pending operations
> force a return through the rseq exit path, and declare the rseq_op_prctl()
> entry point (with a CONFIG_RSEQ=n stub).
>
> Signed-off-by: Olivier Dion <odion@xxxxxxxxxxxx>
> ---
> include/linux/rseq.h | 10 +++++++++-
> include/linux/rseq_types.h | 9 +++++++++
> 2 files changed, 18 insertions(+), 1 deletion(-)
>
> diff --git a/include/linux/rseq.h b/include/linux/rseq.h
> index 7ef79b25e714..d1e33906d863 100644
> --- a/include/linux/rseq.h
> +++ b/include/linux/rseq.h
> @@ -69,7 +69,9 @@ static __always_inline void rseq_sched_switch_event(struct task_struct *t)
> * was via interrupt from user space. ev->has_rseq does not have
> * to be evaluated here because rseq_v2() implies has_rseq.
> */
> - bool raise = ev->user_irq | ev->ids_changed;
> + bool raise = (ev->user_irq |
> + ev->ids_changed |
> + ev->rseq_op);
>
> if (raise) {
> ev->sched_switch = true;
> @@ -172,6 +174,8 @@ static inline unsigned int rseq_alloc_align(void)
> return 1U << get_count_order(offsetof(struct rseq, end));
> }
>
> +int rseq_op_prctl(unsigned long arg2, unsigned long arg3);
> +
> #else /* CONFIG_RSEQ */
> static inline bool rseq_v2(struct task_struct *t) { return false; }
> static inline void rseq_handle_slowpath(struct pt_regs *regs) { }
> @@ -182,6 +186,10 @@ static inline void rseq_force_update(void) { }
> static inline void rseq_virt_userspace_exit(void) { }
> static inline void rseq_fork(struct task_struct *t, u64 clone_flags) { }
> static inline void rseq_execve(struct task_struct *t) { }
> +static inline int rseq_op_prctl(unsigned long arg2, unsigned long arg3)
> +{
> + return -ENOTSUPP;
> +}
> #endif /* !CONFIG_RSEQ */
>
> #ifdef CONFIG_DEBUG_RSEQ
> diff --git a/include/linux/rseq_types.h b/include/linux/rseq_types.h
> index 85739a63e85e..059292695c34 100644
> --- a/include/linux/rseq_types.h
> +++ b/include/linux/rseq_types.h
> @@ -23,6 +23,7 @@ struct rseq;
> * exit to user
> * @ids_changed: Indicator that IDs need to be updated
> * @user_irq: True on interrupt entry from user mode
> + * @rseq_op: Rseq operation processing is enabled for the task
> * @has_rseq: Greater than 0 if the task has a rseq pointer installed.
> * Contains the RSEQ version number
> * @error: Compound error code for the slow path to analyze
> @@ -44,6 +45,7 @@ struct rseq_event {
> u8 sched_switch;
> u8 ids_changed;
> u8 user_irq;
> + u8 rseq_op;
> };
> };
>
> @@ -115,6 +117,7 @@ struct rseq_slice {
> * @event: Storage for event management
> * @ids: Storage for cached CPU ID and MM CID
> * @slice: Storage for time slice extension data
> + * @nr_ops: Number of registered rseq operations
> */
> struct rseq_data {
> struct rseq __user *usrptr;
> @@ -125,6 +128,12 @@ struct rseq_data {
> #ifdef CONFIG_RSEQ_SLICE_EXTENSION
> struct rseq_slice slice;
> #endif
> + /*
> + * Number of rseq operations registered for the task. Edge triggered:
> + * the 0<->1 transition enables/disables rseq_event::rseq_op and the
> + * RSEQ_CS_FLAG_RSEQ_OP_ENABLED user flag.
> + */
> + u32 nr_ops;

If we copy ops to kernel memory, it would be useful to have 1 op
embeded here (with fallback array for more ops). For now the only
known use-case for this is memory allocators, so I would assume in
most cases it will be either 0 or 1 ops registered.

> };
>
> #else /* CONFIG_RSEQ */
> --
> 2.54.0
>