[PATCH 1/1] alpha: Add support for HAVE_ARCH_JUMP_LABEL
Magnus Lindholm <[email protected]>
| Newsgroups | org.kernel.vger.linux-alpha,org.kernel.vger.linux-kernel |
|---|---|
| Message-ID | <[email protected]> |
Implement static key (jump label) support for Alpha. Provide arch_static_branch() helpers and implement arch_jump_label_transform() to patch a single instruction site between NOP and BR, with appropriate I-cache synchronization. Tested on Alpha UP2000+ SMP. Static key sites toggle correctly under tracepoint enable/disable and repeated stress testing without faults. Signed-off-by: Magnus Lindholm <[email protected]> --- arch/alpha/Kconfig | 1 + arch/alpha/include/asm/jump_label.h | 66 +++++++++++++++++++++++ arch/alpha/kernel/Makefile | 1 + arch/alpha/kernel/jump_label.c | 81 +++++++++++++++++++++++++++++ 4 files changed, 149 insertions(+) create mode 100644 arch/alpha/include/asm/jump_label.h create mode 100644 arch/alpha/kernel/jump_label.c diff --git a/arch/alpha/Kconfig b/arch/alpha/Kconfig index 6c7dbf0adad6..71db2cc7b3c4 100644 --- a/arch/alpha/Kconfig +++ b/arch/alpha/Kconfig @@ -41,6 +41,7 @@ config ALPHA select MMU_GATHER_RCU_TABLE_FREE select SPARSEMEM_EXTREME if SPARSEMEM select ZONE_DMA + select HAVE_ARCH_JUMP_LABEL help The Alpha is a 64-bit general-purpose processor designed and marketed by the Digital Equipment Corporation of blessed memory, diff --git a/arch/alpha/include/asm/jump_label.h b/arch/alpha/include/asm/jump_label.h new file mode 100644 index 000000000000..b570a7cef4c9 --- /dev/null +++ b/arch/alpha/include/asm/jump_label.h @@ -0,0 +1,66 @@ +/* SPDX-License-Identifier: GPL-2.0 + * + * Alpha architecture jump label (static key) definitions + * + * Defines patch site emission and jump table layout for + * Alpha static key support. + * + * Copyright (C) 2026 Magnus Lindholm <[email protected]> + */ + + +#ifndef _ASM_ALPHA_JUMP_LABEL_H +#define _ASM_ALPHA_JUMP_LABEL_H + +#ifndef __ASSEMBLER__ + +#include <linux/types.h> + +#define JUMP_LABEL_NOP_SIZE 4 + +typedef u64 jump_label_t; + +struct jump_entry { + jump_label_t code; + jump_label_t target; + jump_label_t key; +}; + +static __always_inline bool arch_static_branch(struct static_key *key, bool branch) +{ + asm goto("1:\n\t" + "nop\n\t" + ".pushsection __jump_table, \"aw\"\n\t" + ".align 3\n\t" + ".quad 1b, %l[l_yes], %0\n\t" + ".popsection\n\t" + : + : "i"(&((char *)key)[branch]) + : + : l_yes); + + return false; +l_yes: + return true; +} + +static __always_inline bool arch_static_branch_jump(struct static_key *key, bool branch) +{ + asm goto("1:\n\t" + "br $31, %l[l_yes]\n\t" + ".pushsection __jump_table, \"aw\"\n\t" + ".align 3\n\t" + ".quad 1b, %l[l_yes], %0\n\t" + ".popsection\n\t" + : + : "i"(&((char *)key)[branch]) + : + : l_yes); + + return false; +l_yes: + return true; +} + +#endif /* __ASSEMBLER__ */ +#endif /* _ASM_ALPHA_JUMP_LABEL_H */ diff --git a/arch/alpha/kernel/Makefile b/arch/alpha/kernel/Makefile index 187cd8df2faf..8a357b78eab1 100644 --- a/arch/alpha/kernel/Makefile +++ b/arch/alpha/kernel/Makefile @@ -19,6 +19,7 @@ obj-$(CONFIG_MODULES) += module.o obj-$(CONFIG_PERF_EVENTS) += perf_event.o obj-$(CONFIG_RTC_DRV_ALPHA) += rtc.o obj-$(CONFIG_AUDIT) += audit.o +obj-$(CONFIG_JUMP_LABEL) += jump_label.o ifdef CONFIG_ALPHA_GENERIC diff --git a/arch/alpha/kernel/jump_label.c b/arch/alpha/kernel/jump_label.c new file mode 100644 index 000000000000..ff061a09d813 --- /dev/null +++ b/arch/alpha/kernel/jump_label.c @@ -0,0 +1,81 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Alpha architecture jump label (static key) support + * + * Implements runtime patching of static key sites by replacing + * a NOP instruction with an unconditional branch and vice versa. + * + * Copyright (C) 2026 Magnus Lindholm <[email protected]> + */ + +#include <linux/jump_label.h> +#include <linux/kernel.h> +#include <linux/mutex.h> +#include <asm/cacheflush.h> + +/* + * Alpha instruction encoding helpers. + * + * Branch format: + * [31:26] opcode + * [25:21] Ra + * [20:0 ] disp (signed, in instructions; hardware multiplies by 4) + * + * Unconditional branch: + * BR opcode is 0x30. We use Ra=r31 so no link register is written. + * + * Updated PC semantics: + * Target = (pc + 4) + (disp << 2) + * so disp = (target - (pc + 4)) >> 2. + */ +#define ALPHA_OP_BR 0x30 +#define ALPHA_RA_R31 31 +#define ALPHA_BR_DISP_MASK ((1u << 21) - 1) + +#define ALPHA_INSN_NOP 0x47FF041Fu /* BIS r31,r31,r31 */ /* common Alpha NOP */ + +static inline u32 alpha_br_insn(unsigned long pc, unsigned long target) +{ + long off_bytes = (long)target - (long)(pc + 4); + long disp = off_bytes >> 2; + + /* + * 21-bit signed displacement: range is [-2^20, 2^20-1] instructions. + * If this trips, the site/target are too far apart for a BR. + */ + if (disp < -(1L << 20) || disp > ((1L << 20) - 1)) { + /* + * Most arches WARN and fall back to something else (or BUG), + * but jump-label sites are expected to be in range. + */ + WARN_ON_ONCE(1); + disp = 0; + } + + return (ALPHA_OP_BR << 26) | + (ALPHA_RA_R31 << 21) | + ((u32)disp & ALPHA_BR_DISP_MASK); +} + +static inline void alpha_patch_text(u32 *site, u32 insn) +{ + WRITE_ONCE(*site, insn); + /* + * Alpha needs an I-cache sync after patching executable text. + */ + flush_icache_range((unsigned long)site, (unsigned long)site + sizeof(*site)); +} + +void arch_jump_label_transform(struct jump_entry *entry, + enum jump_label_type type) +{ + u32 *site = (u32 *)jump_entry_code(entry); + u32 insn; + + if (type == JUMP_LABEL_JMP) + insn = alpha_br_insn((unsigned long)site, jump_entry_target(entry)); + else + insn = ALPHA_INSN_NOP; + + alpha_patch_text(site, insn); +} -- 2.52.0