Re: [PATCH v4 1/1] powerpc: enable dynamic preemption
From: Jirka Hladky <hidden>
Date: 2026-07-26 18:33:37
Also in:
lkml
Hi Shrikanth, Christophe, I'm seeing a significant performance regression on ppc64le after this patch landed in 6.16, caused by CONFIG_PREEMPT_RCU becoming active once HAVE_PREEMPT_DYNAMIC_KEY is selected. Benchmark: stress-ng kill stressor (tight kill() syscall loop), single thread, POWER10 LPAR (8 vCPUs, 1 core SMT-8). Bisected across Fedora ELN kernel builds on ppc64le: kernel CONFIG_PREEMPT_RCU kill bogo-ops/sec --- 6.15-rc6 (eln148) no 103,207 6.16 (eln150) yes 70,281 (-32%) 6.18 (eln154) yes 72,552 (-30%) For comparison, x86_64 (AMD EPYC 9355P) with the same config change shows only a 2.8% regression: 6.12 x86_64 37,436 7.2 x86_64 36,392 (-2.8%) perf report shows the overhead comes from rcu_read_lock/unlock in the SELinux AVC path (check_kill_permission -> security_task_kill -> selinux_task_kill -> avc_has_perm -> avc_lookup): Function 6.15 (no PREEMPT_RCU) 6.16 (PREEMPT_RCU) --- avc_lookup 15.23% 24.79% __rcu_read_lock ~0% 4.52% __rcu_read_unlock ~0% 4.17% selinux_task_kill 6.23% 7.35% audit_signal_info* 0.94% 3.59% On x86_64, rcu_read_lock/unlock are cheap thanks to static calls (HAVE_PREEMPT_DYNAMIC_CALL). On ppc64le with the KEY-based implementation, the weak memory model requires real barriers (lwsync/isync) making each RCU read-side critical section significantly more expensive. This aligns with Christophe's earlier review comment that HAVE_PREEMPT_DYNAMIC_CALL should be more performant. Would implementing static calls for ppc64 be feasible to close this gap? Test details: - Machine: IBM POWER10 (pvr 0080 0200), pHyp virtualization - stress-ng 0.21.03, gcc 14.3.1, glibc 2.39 - Tuned profile: virtual-guest - SELinux: enforcing (permissive recovers only ~7%) Happy to run additional tests if needed. On Mon, Feb 10, 2025 at 7:46 PM Shrikanth Hegde [off-list ref] wrote:
quoted hunk ↗ jump to hunk
Once the lazy preemption is supported, it would be desirable to change the preemption models at runtime. So add support for dynamic preemption using DYNAMIC_KEY. ::Tested lightly on Power10 LPAR Performance numbers indicate that, preempt=none(no dynamic) and preempt=none(dynamic) are close. cat /sys/kernel/debug/sched/preempt (none) voluntary full lazy perf stat -e probe:__cond_resched -a sleep 1 Performance counter stats for 'system wide': 1,253 probe:__cond_resched echo full > /sys/kernel/debug/sched/preempt cat /sys/kernel/debug/sched/preempt none voluntary (full) lazy perf stat -e probe:__cond_resched -a sleep 1 Performance counter stats for 'system wide': 0 probe:__cond_resched Signed-off-by: Shrikanth Hegde <redacted> --- arch/powerpc/Kconfig | 1 + arch/powerpc/include/asm/preempt.h | 16 ++++++++++++++++ arch/powerpc/kernel/interrupt.c | 6 +++++- arch/powerpc/lib/vmx-helper.c | 2 +- 4 files changed, 23 insertions(+), 2 deletions(-) create mode 100644 arch/powerpc/include/asm/preempt.hdiff --git a/arch/powerpc/Kconfig b/arch/powerpc/Kconfig index 424f188e62d9..364edaea1f88 100644 --- a/arch/powerpc/Kconfig +++ b/arch/powerpc/Kconfig@@ -275,6 +275,7 @@ config PPC select HAVE_PERF_EVENTS_NMI if PPC64 select HAVE_PERF_REGS select HAVE_PERF_USER_STACK_DUMP + select HAVE_PREEMPT_DYNAMIC_KEY select HAVE_RETHOOK if KPROBES select HAVE_REGS_AND_STACK_ACCESS_API select HAVE_RELIABLE_STACKTRACEdiff --git a/arch/powerpc/include/asm/preempt.h b/arch/powerpc/include/asm/preempt.h new file mode 100644 index 000000000000..000e2b9681f3 --- /dev/null +++ b/arch/powerpc/include/asm/preempt.h@@ -0,0 +1,16 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __ASM_POWERPC_PREEMPT_H +#define __ASM_POWERPC_PREEMPT_H + +#include <asm-generic/preempt.h> + +#if defined(CONFIG_PREEMPT_DYNAMIC) +#include <linux/jump_label.h> +DECLARE_STATIC_KEY_TRUE(sk_dynamic_irqentry_exit_cond_resched); +#define need_irq_preemption() \ + (static_branch_unlikely(&sk_dynamic_irqentry_exit_cond_resched)) +#else +#define need_irq_preemption() (IS_ENABLED(CONFIG_PREEMPTION)) +#endif + +#endif /* __ASM_POWERPC_PREEMPT_H */diff --git a/arch/powerpc/kernel/interrupt.c b/arch/powerpc/kernel/interrupt.c index 8f4acc55407b..e0c681d0b076 100644 --- a/arch/powerpc/kernel/interrupt.c +++ b/arch/powerpc/kernel/interrupt.c@@ -25,6 +25,10 @@ unsigned long global_dbcr0[NR_CPUS]; #endif +#if defined(CONFIG_PREEMPT_DYNAMIC) +DEFINE_STATIC_KEY_TRUE(sk_dynamic_irqentry_exit_cond_resched); +#endif + #ifdef CONFIG_PPC_BOOK3S_64 DEFINE_STATIC_KEY_FALSE(interrupt_exit_not_reentrant); static inline bool exit_must_hard_disable(void)@@ -396,7 +400,7 @@ notrace unsigned long interrupt_exit_kernel_prepare(struct pt_regs *regs) /* Returning to a kernel context with local irqs enabled. */ WARN_ON_ONCE(!(regs->msr & MSR_EE)); again: - if (IS_ENABLED(CONFIG_PREEMPTION)) { + if (need_irq_preemption()) { /* Return to preemptible kernel context */ if (unlikely(read_thread_flags() & _TIF_NEED_RESCHED)) { if (preempt_count() == 0)diff --git a/arch/powerpc/lib/vmx-helper.c b/arch/powerpc/lib/vmx-helper.c index 58ed6bd613a6..54340912398f 100644 --- a/arch/powerpc/lib/vmx-helper.c +++ b/arch/powerpc/lib/vmx-helper.c@@ -45,7 +45,7 @@ int exit_vmx_usercopy(void) * set and we are preemptible. The hack here is to schedule a * decrementer to fire here and reschedule for us if necessary. */ - if (IS_ENABLED(CONFIG_PREEMPTION) && need_resched()) + if (need_irq_preemption() && need_resched()) set_dec(1); return 0; } --2.39.3
-- -Jirka