diff options
| author | Mark Rutland <mark.rutland@arm.com> | 2026-09-08 16:17:26 +0100 |
|---|---|---|
| committer | Catalin Marinas <catalin.marinas@arm.com> | 2026-09-24 14:51:40 +0100 |
| commit | 1bee18253c2ea9db07fca4496b0e25ab3f5db8e1 (patch) | |
| tree | d583b8d22eaaf82e9707ff1178fb976ece918ac5 /tools/perf/scripts/python/bin | |
| parent | de2824489899f34db976f6c8f1a548d4cf76fc44 (diff) | |
arm64: preempt: Simplify and optimize __preempt_count_dec_and_test()
In arm64's __preempt_count_dec_and_test(), the final conditional load of
'ti->preempt_count' is always executed in the common case. The
conditional load leads to unfortunate code generation for
preempt_enable[_notrace](), and it would be better to unconditionally
load 'ti->preempt_count', as described below.
On arm64, struct thread_info contains the following union:
| union {
| u64 preempt_count;
| struct {
| u32 count;
| u32 need_resched;
| } preempt;
| };
Note: 'need_resched' is encoded so that '0' means a reschedule is
needed, and '1' means a reschedule is NOT needed.
The core logic of __preempt_count_dec_and_test() is:
| static inline bool __preempt_count_dec_and_test(void)
| {
| struct thread_info *ti = current_thread_info();
| u64 pc = READ_ONCE(ti->preempt_count);
|
| WRITE_ONCE(ti->preempt.count, --pc);
|
| return !pc || !READ_ONCE(ti->preempt_count);
| }
The '!pc' condition can only be true when both:
* The initial value of 'need_resched' was 0, meaning that a reschedule
is needed. This should be rare.
* The initial value of 'count' was exactly 1. This cannot be true for a
nested preempt_disable() ... preempt_enable() sequence.
Hence in common cases, '!pc' will be false, and it's necessary to
execute the final READ_ONCE(ti->preempt_count).
This results in a conditional branch in the common case, as can be seen
when __preempt_count_dec_and_test() is outlined:
| <outline___preempt_count_dec_and_test>:
| mrs x2, sp_el0
| ldr x1, [x2, #8]
| mov w0, #0x1
| sub x1, x1, #0x1
| str w1, [x2, #8]
| cbz x1, 1f
| ldr x0, [x2, #8]
| cmp x0, #0x0
| cset w0, eq // eq = none
| 1: ret
It would be better to avoid the special case for 'pc == 0', and to
always load 'ti->preempt_count' after decrementing 'ti->preempt.count'.
For the common cases this will remove a conditional branch. For the rare
cases where preemption is needed initially, this only adds a single
load, whose cost should be dominated by other factors.
Remove the special case for 'pc == 0', and always load the combined
'ti->preempt_count' after decrementing 'ti->preempt.count'.
The removal of the conditional branch helps with code generation, as the
compiler can more easily move a dependent slow path out-of-line, as
demonstrated with the following compiled with GCC 15.2.0:
| void outline_preempt_enable_notrace(void)
| {
| preempt_enable_notrace();
| }
Before this patch:
| <outline_preempt_enable_notrace>:
| mrs x1, sp_el0
| ldr x0, [x1, #8]
| sub x0, x0, #0x1
| str w0, [x1, #8]
| cbz x0, 1f
| ldr x0, [x1, #8]
| cbnz x0, 2f
| 1: paciasp
| stp x29, x30, [sp, #-16]!
| mov x29, sp
| bl preempt_schedule_notrace
| ldp x29, x30, [sp], #16
| autiasp
| ret
| 2: ret
After this patch:
| <outline_preempt_enable_notrace>:
| mrs x0, sp_el0
| ldr w1, [x0, #8]
| sub w1, w1, #0x1
| str w1, [x0, #8]
| ldr x0, [x0, #8]
| cbz x0, 1f
| ret
| 1: paciasp
| stp x29, x30, [sp, #-16]!
| mov x29, sp
| bl preempt_schedule_notrace
| ldp x29, x30, [sp], #16
| autiasp
| ret
Signed-off-by: Mark Rutland <mark.rutland@arm.com>
Reviewed-by: Jinjie Ruan <ruanjinjie@huawei.com>
Tested-by: Muhammad Usama Anjum <usama.anjum@arm.com>
Cc: Ada Couprie Diaz <ada.coupriediaz@arm.com>
Cc: Ard Biesheuvel <ardb@kernel.org>
Cc: James Morse <james.morse@arm.com>
Cc: Marc Zyngier <maz@kernel.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Vladimir Murzin <vladimir.murzin@arm.com>
Cc: Will Deacon <will@kernel.org>
Cc: Yang Shi <yang@os.amperecomputing.com>
[catalin.marinas@arm.com: fixed typo in comment]
Signed-off-by: Catalin Marinas <catalin.marinas@arm.com>
Diffstat (limited to 'tools/perf/scripts/python/bin')
0 files changed, 0 insertions, 0 deletions
