summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHeiko Carstens <hca@linux.ibm.com>2026-01-17 17:53:26 +0300
committerHeiko Carstens <hca@linux.ibm.com>2026-01-17 17:53:26 +0300
commit86302ddf20e6b27ef463006c8bde6fd753056101 (patch)
tree0333bbd118d452d31e0e833efe9a9d9e9902ff3e
parent12ea976f955cefb06eeae4c9e5eb48d08038ccb2 (diff)
parent48b4790f054994d4df6d1025ec9267b19618f0ec (diff)
downloadlinux-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.h2
-rw-r--r--arch/s390/include/asm/preempt.h47
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
}
/*