diff options
| author | Heiko Carstens <hca@linux.ibm.com> | 2026-01-17 17:53:26 +0300 |
|---|---|---|
| committer | Heiko Carstens <hca@linux.ibm.com> | 2026-01-17 17:53:26 +0300 |
| commit | 86302ddf20e6b27ef463006c8bde6fd753056101 (patch) | |
| tree | 0333bbd118d452d31e0e833efe9a9d9e9902ff3e | |
| parent | 12ea976f955cefb06eeae4c9e5eb48d08038ccb2 (diff) | |
| parent | 48b4790f054994d4df6d1025ec9267b19618f0ec (diff) | |
| download | linux-86302ddf20e6b27ef463006c8bde6fd753056101.tar.xz | |
Merge branch 'preempt'
Heiko Carstens says:
====================
The option to select PREEMPT_NONE will go away for all architectures which
support PREEMPT_LAZY [1]. Until now all distributions provide kernels built
with PREEMPT_NONE enabled for s390. In particular this means that all
preempt_disable() / preempt_enable() pairs are optimized away during
compile time.
With PREEMPT_LAZY this is not the case. Switching to PREEMPT_LAZY leads
to a kernel image size increase of ~218kb (defconfig, gcc15).
s390 provides optimized preempt primitives, however there is still room for
improvement. Since support for relocatable lowcore was added access to
preempt_count in lowcore requires an extra call of get_lowcore(), which
generates an extra instruction. Also all instructions have to use a base
register which is not zero to access preempt_count.
Address this by adding a couple of inline assemblies with alternatives.
This generates better code and reduces the size of a PREEMPT_LAZY built
kernel image by ~58kb.
[1] https://lore.kernel.org/all/20251219101502.GB1132199@noisy.programming.kicks-ass.net/
====================
Signed-off-by: Heiko Carstens <hca@linux.ibm.com>
| -rw-r--r-- | arch/s390/include/asm/asm.h | 2 | ||||
| -rw-r--r-- | arch/s390/include/asm/preempt.h | 47 |
2 files changed, 45 insertions, 4 deletions
diff --git a/arch/s390/include/asm/asm.h b/arch/s390/include/asm/asm.h index e9062b01e2a2..510901c2a5f9 100644 --- a/arch/s390/include/asm/asm.h +++ b/arch/s390/include/asm/asm.h @@ -30,7 +30,7 @@ */ #if defined(__GCC_ASM_FLAG_OUTPUTS__) && !(IS_ENABLED(CONFIG_CC_ASM_FLAG_OUTPUT_BROKEN)) -#define __HAVE_ASM_FLAG_OUTPUTS__ +#define __HAVE_ASM_FLAG_OUTPUTS__ 1 #define CC_IPM(sym) #define CC_OUT(sym, var) "=@cc" (var) diff --git a/arch/s390/include/asm/preempt.h b/arch/s390/include/asm/preempt.h index 6ccd033acfe5..6e5821bb047e 100644 --- a/arch/s390/include/asm/preempt.h +++ b/arch/s390/include/asm/preempt.h @@ -8,7 +8,10 @@ #include <asm/cmpxchg.h> #include <asm/march.h> -/* We use the MSB mostly because its available */ +/* + * Use MSB so it is possible to read preempt_count with LLGT which + * reads the least significant 31 bits with a single instruction. + */ #define PREEMPT_NEED_RESCHED 0x80000000 /* @@ -23,7 +26,20 @@ */ static __always_inline int preempt_count(void) { - return READ_ONCE(get_lowcore()->preempt_count) & ~PREEMPT_NEED_RESCHED; + unsigned long lc_preempt, count; + + BUILD_BUG_ON(sizeof_field(struct lowcore, preempt_count) != sizeof(int)); + lc_preempt = offsetof(struct lowcore, preempt_count); + /* READ_ONCE(get_lowcore()->preempt_count) & ~PREEMPT_NEED_RESCHED */ + asm_inline( + ALTERNATIVE("llgt %[count],%[offzero](%%r0)\n", + "llgt %[count],%[offalt](%%r0)\n", + ALT_FEATURE(MFEATURE_LOWCORE)) + : [count] "=d" (count) + : [offzero] "i" (lc_preempt), + [offalt] "i" (lc_preempt + LOWCORE_ALT_ADDRESS), + "m" (((struct lowcore *)0)->preempt_count)); + return count; } static __always_inline void preempt_count_set(int pc) @@ -68,7 +84,17 @@ static __always_inline void __preempt_count_add(int val) */ if (!IS_ENABLED(CONFIG_PROFILE_ALL_BRANCHES)) { if (__builtin_constant_p(val) && (val >= -128) && (val <= 127)) { - __atomic_add_const(val, &get_lowcore()->preempt_count); + unsigned long lc_preempt; + + lc_preempt = offsetof(struct lowcore, preempt_count); + asm_inline( + ALTERNATIVE("asi %[offzero](%%r0),%[val]\n", + "asi %[offalt](%%r0),%[val]\n", + ALT_FEATURE(MFEATURE_LOWCORE)) + : "+m" (((struct lowcore *)0)->preempt_count) + : [offzero] "i" (lc_preempt), [val] "i" (val), + [offalt] "i" (lc_preempt + LOWCORE_ALT_ADDRESS) + : "cc"); return; } } @@ -87,7 +113,22 @@ static __always_inline void __preempt_count_sub(int val) */ static __always_inline bool __preempt_count_dec_and_test(void) { +#ifdef __HAVE_ASM_FLAG_OUTPUTS__ + unsigned long lc_preempt; + int cc; + + lc_preempt = offsetof(struct lowcore, preempt_count); + asm_inline( + ALTERNATIVE("alsi %[offzero](%%r0),%[val]\n", + "alsi %[offalt](%%r0),%[val]\n", + ALT_FEATURE(MFEATURE_LOWCORE)) + : "=@cc" (cc), "+m" (((struct lowcore *)0)->preempt_count) + : [offzero] "i" (lc_preempt), [val] "i" (-1), + [offalt] "i" (lc_preempt + LOWCORE_ALT_ADDRESS)); + return (cc == 0) || (cc == 2); +#else return __atomic_add_const_and_test(-1, &get_lowcore()->preempt_count); +#endif } /* |
