Skip to content

Commit 933d248

Browse files
author
Peter Zijlstra
committed
Merge branch 'tip/sched/urgent'
Pick up urgent fixes. Signed-off-by: Peter Zijlstra <peterz@infradead.org>
2 parents 3b7be8e + 411c1cf commit 933d248

21 files changed

Lines changed: 604 additions & 245 deletions

File tree

Documentation/userspace-api/rseq.rst

Lines changed: 93 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,97 @@ Quick access to CPU number, node ID
2424
Allows to implement per CPU data efficiently. Documentation is in code and
2525
selftests. :(
2626

27+
Optimized RSEQ V2
28+
-----------------
29+
30+
On architectures which utilize the generic entry code and generic TIF bits
31+
the kernel supports runtime optimizations for RSEQ, which also enable
32+
enhanced features like scheduler time slice extensions.
33+
34+
To enable them a task has to register the RSEQ region with at least the
35+
length advertised by getauxval(AT_RSEQ_FEATURE_SIZE).
36+
37+
If existing binaries register with RSEQ_ORIG_SIZE (32 bytes), the kernel
38+
keeps the legacy low performance mode enabled to fulfil the expectations
39+
of existing users regarding the original RSEQ implementation behaviour.
40+
41+
The following table documents the ABI and behavioral guarantees of the
42+
legacy and the optimized V2 mode.
43+
44+
.. list-table:: RSEQ modes
45+
:header-rows: 1
46+
47+
* - Nr
48+
- What
49+
50+
- Legacy
51+
- Optimized V2
52+
53+
* - 1
54+
- The cpu_id_start, cpu_id, node_id and mm_cid fields (User mode read
55+
only)
56+
.. Legacy
57+
- Updated by the kernel unconditionally after each context switch and
58+
before signal delivery
59+
.. Optimized V2
60+
- Updated by the kernel if and only if they change, i.e. if the task
61+
is migrated or mm_cid changes
62+
63+
* - 2
64+
- The rseq_cs critical section field
65+
.. Legacy
66+
- Evaluated and handled unconditionally after each context switch and
67+
before signal delivery
68+
.. Optimized V2
69+
- Evaluated and handled conditionally only when user space was
70+
interrupted and was scheduled out or before delivering a signal in
71+
the interrupted context.
72+
73+
* - 3
74+
- Read only fields
75+
.. Legacy
76+
- No strict enforcement except in debug mode
77+
.. Optimized V2
78+
- Strict enforcement
79+
80+
* - 4
81+
- membarrier(...RSEQ)
82+
.. Legacy
83+
- All running threads of the process are interrupted and the ID fields
84+
are rewritten and eventually active critical sections are aborted
85+
before they return to user space. All threads which are scheduled
86+
out whether voluntary or not are covered by #1/#2 above.
87+
.. Optimized V2
88+
- All running threads of the process are interrupted and eventually
89+
active critical sections are aborted before these threads return to
90+
user space. The ID fields are only updated if changed as a
91+
consequence of the interrupt. All threads which are scheduled out
92+
whether voluntary or not are covered by #1/#2 above.
93+
94+
* - 5
95+
- Time slice extensions
96+
.. Legacy
97+
- Not supported
98+
.. Optimized V2
99+
- Supported
100+
101+
The legacy mode is obviously less performant as it does unconditional
102+
updates and critical section checks even if not strictly required by the
103+
ABI contract. That can't be changed anymore as some users depend on that
104+
observed behavior, which in turn enables them to violate the ABI and
105+
overwrite the cpu_id_start field for their own purposes. This is obviously
106+
discouraged as it renders RSEQ incompatible with the intended usage and
107+
breaks the expectation of other libraries in the same application.
108+
109+
The ABI compliant optimized v2 mode, which respects the read only fields,
110+
does not require unconditional updates and therefore is way more
111+
performant. The kernel validates the read only fields for compliance. If
112+
user space modifies them, the process is killed. Compliant usage allows
113+
multiple libraries in the same application to benefit from the RSEQ
114+
functionality without disturbing each other. The ABI compliant optimized v2
115+
mode also enables extended RSEQ features like time slice extensions.
116+
117+
27118
Scheduler time slice extensions
28119
-------------------------------
29120

@@ -37,7 +128,8 @@ The prerequisites for this functionality are:
37128

38129
* Enabled at boot time (default is enabled)
39130

40-
* A rseq userspace pointer has been registered for the thread
131+
* A rseq userspace pointer has been registered for the thread in
132+
optimized V2 mode
41133

42134
The thread has to enable the functionality via prctl(2)::
43135

arch/arm64/kernel/entry-common.c

Lines changed: 24 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,13 @@ static void noinstr arm64_exit_to_kernel_mode(struct pt_regs *regs,
6262
irqentry_exit_to_kernel_mode_after_preempt(regs, state);
6363
}
6464

65+
static __always_inline void arm64_syscall_enter_from_user_mode(struct pt_regs *regs)
66+
{
67+
enter_from_user_mode(regs);
68+
mte_disable_tco_entry(current);
69+
sme_enter_from_user_mode();
70+
}
71+
6572
/*
6673
* Handle IRQ/context state management when entering from user mode.
6774
* Before this function is called it is not safe to call regular kernel code,
@@ -70,20 +77,30 @@ static void noinstr arm64_exit_to_kernel_mode(struct pt_regs *regs,
7077
static __always_inline void arm64_enter_from_user_mode(struct pt_regs *regs)
7178
{
7279
enter_from_user_mode(regs);
80+
rseq_note_user_irq_entry();
7381
mte_disable_tco_entry(current);
7482
sme_enter_from_user_mode();
7583
}
7684

85+
static __always_inline void arm64_syscall_exit_to_user_mode(struct pt_regs *regs)
86+
{
87+
local_irq_disable();
88+
syscall_exit_to_user_mode_prepare(regs);
89+
local_daif_mask();
90+
sme_exit_to_user_mode();
91+
mte_check_tfsr_exit();
92+
exit_to_user_mode();
93+
}
94+
7795
/*
7896
* Handle IRQ/context state management when exiting to user mode.
7997
* After this function returns it is not safe to call regular kernel code,
8098
* instrumentable code, or any code which may trigger an exception.
8199
*/
82-
83100
static __always_inline void arm64_exit_to_user_mode(struct pt_regs *regs)
84101
{
85102
local_irq_disable();
86-
exit_to_user_mode_prepare_legacy(regs);
103+
irqentry_exit_to_user_mode_prepare(regs);
87104
local_daif_mask();
88105
sme_exit_to_user_mode();
89106
mte_check_tfsr_exit();
@@ -92,7 +109,7 @@ static __always_inline void arm64_exit_to_user_mode(struct pt_regs *regs)
92109

93110
asmlinkage void noinstr asm_exit_to_user_mode(struct pt_regs *regs)
94111
{
95-
arm64_exit_to_user_mode(regs);
112+
arm64_syscall_exit_to_user_mode(regs);
96113
}
97114

98115
/*
@@ -716,12 +733,12 @@ static void noinstr el0_brk64(struct pt_regs *regs, unsigned long esr)
716733

717734
static void noinstr el0_svc(struct pt_regs *regs)
718735
{
719-
arm64_enter_from_user_mode(regs);
736+
arm64_syscall_enter_from_user_mode(regs);
720737
cortex_a76_erratum_1463225_svc_handler();
721738
fpsimd_syscall_enter();
722739
local_daif_restore(DAIF_PROCCTX);
723740
do_el0_svc(regs);
724-
arm64_exit_to_user_mode(regs);
741+
arm64_syscall_exit_to_user_mode(regs);
725742
fpsimd_syscall_exit();
726743
}
727744

@@ -868,11 +885,11 @@ static void noinstr el0_cp15(struct pt_regs *regs, unsigned long esr)
868885

869886
static void noinstr el0_svc_compat(struct pt_regs *regs)
870887
{
871-
arm64_enter_from_user_mode(regs);
888+
arm64_syscall_enter_from_user_mode(regs);
872889
cortex_a76_erratum_1463225_svc_handler();
873890
local_daif_restore(DAIF_PROCCTX);
874891
do_el0_svc_compat(regs);
875-
arm64_exit_to_user_mode(regs);
892+
arm64_syscall_exit_to_user_mode(regs);
876893
}
877894

878895
static void noinstr el0_bkpt32(struct pt_regs *regs, unsigned long esr)

include/linux/irq-entry-common.h

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -218,14 +218,6 @@ static __always_inline void __exit_to_user_mode_validate(void)
218218
lockdep_sys_exit();
219219
}
220220

221-
/* Temporary workaround to keep ARM64 alive */
222-
static __always_inline void exit_to_user_mode_prepare_legacy(struct pt_regs *regs)
223-
{
224-
__exit_to_user_mode_prepare(regs, EXIT_TO_USER_MODE_WORK);
225-
rseq_exit_to_user_mode_legacy();
226-
__exit_to_user_mode_validate();
227-
}
228-
229221
/**
230222
* syscall_exit_to_user_mode_prepare - call exit_to_user_mode_loop() if required
231223
* @regs: Pointer to pt_regs on entry stack

include/linux/rseq.h

Lines changed: 26 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -9,15 +9,19 @@
99

1010
void __rseq_handle_slowpath(struct pt_regs *regs);
1111

12+
static __always_inline bool rseq_v2(struct task_struct *t)
13+
{
14+
return IS_ENABLED(CONFIG_GENERIC_IRQ_ENTRY) && likely(t->rseq.event.has_rseq > 1);
15+
}
16+
1217
/* Invoked from resume_user_mode_work() */
1318
static inline void rseq_handle_slowpath(struct pt_regs *regs)
1419
{
1520
if (IS_ENABLED(CONFIG_GENERIC_ENTRY)) {
1621
if (current->rseq.event.slowpath)
1722
__rseq_handle_slowpath(regs);
1823
} else {
19-
/* '&' is intentional to spare one conditional branch */
20-
if (current->rseq.event.sched_switch & current->rseq.event.has_rseq)
24+
if (current->rseq.event.sched_switch && current->rseq.event.has_rseq)
2125
__rseq_handle_slowpath(regs);
2226
}
2327
}
@@ -30,9 +34,9 @@ void __rseq_signal_deliver(int sig, struct pt_regs *regs);
3034
*/
3135
static inline void rseq_signal_deliver(struct ksignal *ksig, struct pt_regs *regs)
3236
{
33-
if (IS_ENABLED(CONFIG_GENERIC_IRQ_ENTRY)) {
34-
/* '&' is intentional to spare one conditional branch */
35-
if (current->rseq.event.has_rseq & current->rseq.event.user_irq)
37+
if (rseq_v2(current)) {
38+
/* has_rseq is implied in rseq_v2() */
39+
if (current->rseq.event.user_irq)
3640
__rseq_signal_deliver(ksig->sig, regs);
3741
} else {
3842
if (current->rseq.event.has_rseq)
@@ -50,22 +54,30 @@ static __always_inline void rseq_sched_switch_event(struct task_struct *t)
5054
{
5155
struct rseq_event *ev = &t->rseq.event;
5256

53-
if (IS_ENABLED(CONFIG_GENERIC_IRQ_ENTRY)) {
57+
/*
58+
* Only apply the user_irq optimization for RSEQ ABI V2 registrations.
59+
* Legacy users like TCMalloc rely on the original ABI V1 behaviour
60+
* which updates IDs on every context swtich.
61+
*/
62+
if (rseq_v2(t)) {
5463
/*
55-
* Avoid a boat load of conditionals by using simple logic
56-
* to determine whether NOTIFY_RESUME needs to be raised.
64+
* Avoid a boat load of conditionals by using simple logic to
65+
* determine whether TIF_NOTIFY_RESUME or TIF_RSEQ needs to be
66+
* raised.
5767
*
58-
* It's required when the CPU or MM CID has changed or
59-
* the entry was from user space.
68+
* It's required when the CPU or MM CID has changed or the entry
69+
* was via interrupt from user space. ev->has_rseq does not have
70+
* to be evaluated here because rseq_v2() implies has_rseq.
6071
*/
61-
bool raise = (ev->user_irq | ev->ids_changed) & ev->has_rseq;
72+
bool raise = ev->user_irq | ev->ids_changed;
6273

6374
if (raise) {
6475
ev->sched_switch = true;
6576
rseq_raise_notify_resume(t);
6677
}
6778
} else {
6879
if (ev->has_rseq) {
80+
t->rseq.event.ids_changed = true;
6981
t->rseq.event.sched_switch = true;
7082
rseq_raise_notify_resume(t);
7183
}
@@ -119,6 +131,8 @@ static inline void rseq_virt_userspace_exit(void)
119131

120132
static inline void rseq_reset(struct task_struct *t)
121133
{
134+
/* Protect against preemption and membarrier IPI */
135+
guard(irqsave)();
122136
memset(&t->rseq, 0, sizeof(t->rseq));
123137
t->rseq.ids.cpu_id = RSEQ_CPU_ID_UNINITIALIZED;
124138
}
@@ -159,6 +173,7 @@ static inline unsigned int rseq_alloc_align(void)
159173
}
160174

161175
#else /* CONFIG_RSEQ */
176+
static inline bool rseq_v2(struct task_struct *t) { return false; }
162177
static inline void rseq_handle_slowpath(struct pt_regs *regs) { }
163178
static inline void rseq_signal_deliver(struct ksignal *ksig, struct pt_regs *regs) { }
164179
static inline void rseq_sched_switch_event(struct task_struct *t) { }

0 commit comments

Comments
 (0)