Difference between revisions of "Rseq"
Amikhalitsyn (talk | contribs) (draft of the article about rseq support in CRIU) |
Amikhalitsyn (talk | contribs) |
||
Line 1: | Line 1: | ||
"Restartable sequences" (<code>rseq</code>) are small segments of user-space code designed to access per-CPU data structures without the need for heavyweight locking. | "Restartable sequences" (<code>rseq</code>) are small segments of user-space code designed to access per-CPU data structures without the need for heavyweight locking. | ||
rseq is supported since Linux kernel 4.18 [1] | rseq is supported since Linux kernel 4.18 [1] | ||
+ | |||
+ | I strongly suggest reading the article [https://www.efficios.com/blog/2019/02/08/linux-restartable-sequences/ Linux restartable sequences] before this one. | ||
== Linux kernel interface == | == Linux kernel interface == |
Revision as of 08:26, 18 April 2022
"Restartable sequences" (rseq
) are small segments of user-space code designed to access per-CPU data structures without the need for heavyweight locking.
rseq is supported since Linux kernel 4.18 [1]
I strongly suggest reading the article Linux restartable sequences before this one.
Linux kernel interface
The Linux kernel interface for rseq is fairly simple. It's just rseq
syscall:
sys_rseq(struct rseq *rseq, uint32_t rseq_len, int flags, uint32_t sig)
enum rseq_cs_flags { RSEQ_CS_FLAG_NO_RESTART_ON_PREEMPT = (1U << RSEQ_CS_FLAG_NO_RESTART_ON_PREEMPT_BIT), RSEQ_CS_FLAG_NO_RESTART_ON_SIGNAL = (1U << RSEQ_CS_FLAG_NO_RESTART_ON_SIGNAL_BIT), RSEQ_CS_FLAG_NO_RESTART_ON_MIGRATE = (1U << RSEQ_CS_FLAG_NO_RESTART_ON_MIGRATE_BIT), }; struct rseq_cs { __u32 version; /* always 0 at this moment */ enum rseq_cs_flags flags; void *start_ip; /* Offset from start_ip. */ intptr_t post_commit_offset; void *abort_ip; } struct rseq { __u32 cpu_id_start; __u32 cpu_id; struct rseq_cs *rseq_cs; enum rseq_cs_flags flags; }
From the userspace side, we need to keep struct rseq
somewhere and register it on the kernel side using the rseq
syscall.
Then, once we want to execute some code as a rseq critical section (rseq cs
or just CS) we need to allocate and fill with the data
struct rseq_cs
. We have to specify the start address of our CS, and the address of the abort handler (called when CS was interrupted by a preemption, migration
or signal). Then we need to put an pointer to struct rseq_cs
to the (struct rseq).rseq_cs
field.
What about flags
?
You may have noticed that both struct rseq
and struct rseq_cs
have flags
field. It may took values from enum rseq_cs_flags
.
First of all, a user may specify flags in any place they will be combined on the kernel side:
static int rseq_need_restart(struct task_struct *t, u32 cs_flags) { u32 flags, event_mask; int ret; /* Get thread flags. */ ret = get_user(flags, &t->rseq->flags); if (ret) return ret; /* Take critical section flags into account. */ flags |= cs_flags; // <<<<<<<< here we have flags combined from struct rseq + struct rseq_cs
The most common flags
value is zero. In this case, the rseq CS will be interrupted (and IP will be fixed up to the abort handler)
if preemption, migration, or signal occurs. But there are situations when users may want not to abort section once one of these events happen.
It's worth mentioning that RSEQ_CS_FLAG_NO_RESTART_ON_SIGNAL
can be used only in combination with RSEQ_CS_FLAG_NO_RESTART_ON_PREEMPT
and RSEQ_CS_FLAG_NO_RESTART_ON_MIGRATE
:
/* * Restart on signal can only be inhibited when restart on * preempt and restart on migrate are inhibited too. Otherwise, * a preempted signal handler could fail to restart the prior * execution context on sigreturn. */ if (unlikely((flags & RSEQ_CS_FLAG_NO_RESTART_ON_SIGNAL) && (flags & RSEQ_CS_PREEMPT_MIGRATE_FLAGS) != RSEQ_CS_PREEMPT_MIGRATE_FLAGS)) return -EINVAL;