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 <[email protected]> wrote: > > 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 <[email protected]> > --- > 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.h > > diff --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_STACKTRACE > diff --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
