jump label: Add s390 support
authorJan Glauber <jang@linux.vnet.ibm.com>
Wed, 16 Mar 2011 19:58:30 +0000 (15:58 -0400)
committerSteven Rostedt <rostedt@goodmis.org>
Mon, 4 Apr 2011 17:43:16 +0000 (13:43 -0400)
Implement the architecture backend for jump label support on s390.

For a shared kernel booted from a NSS silently disable jump labels
because the NSS is read-only. Therefore jump labels will be disabled
in a shared kernel and can't be activated.

Signed-off-by: Jan Glauber <jang@linux.vnet.ibm.com>
LKML-Reference: <6935d2c41ce111e1719176ed4bbd3dbe4de80855.1300299760.git.jbaron@redhat.com>
Acked-by: Peter Zijlstra <a.p.zijlstra@chello.nl>
Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
Signed-off-by: Steven Rostedt <rostedt@goodmis.org>
arch/s390/Kconfig
arch/s390/include/asm/jump_label.h [new file with mode: 0644]
arch/s390/kernel/Makefile
arch/s390/kernel/jump_label.c [new file with mode: 0644]

index 2508a6f3158862af5cee200d8254150d139f2c7c..4a7f14079e03b989bd4cd4b1a012b082e07447b3 100644 (file)
@@ -88,6 +88,7 @@ config S390
        select HAVE_KERNEL_XZ
        select HAVE_GET_USER_PAGES_FAST
        select HAVE_ARCH_MUTEX_CPU_RELAX
+       select HAVE_ARCH_JUMP_LABEL if !MARCH_G5
        select ARCH_INLINE_SPIN_TRYLOCK
        select ARCH_INLINE_SPIN_TRYLOCK_BH
        select ARCH_INLINE_SPIN_LOCK
diff --git a/arch/s390/include/asm/jump_label.h b/arch/s390/include/asm/jump_label.h
new file mode 100644 (file)
index 0000000..95a6cf2
--- /dev/null
@@ -0,0 +1,37 @@
+#ifndef _ASM_S390_JUMP_LABEL_H
+#define _ASM_S390_JUMP_LABEL_H
+
+#include <linux/types.h>
+
+#define JUMP_LABEL_NOP_SIZE 6
+
+#ifdef CONFIG_64BIT
+#define ASM_PTR ".quad"
+#define ASM_ALIGN ".balign 8"
+#else
+#define ASM_PTR ".long"
+#define ASM_ALIGN ".balign 4"
+#endif
+
+static __always_inline bool arch_static_branch(struct jump_label_key *key)
+{
+       asm goto("0:    brcl 0,0\n"
+               ".pushsection __jump_table, \"aw\"\n"
+               ASM_ALIGN "\n"
+               ASM_PTR " 0b, %l[label], %0\n"
+               ".popsection\n"
+               : : "X" (key) : : label);
+       return false;
+label:
+       return true;
+}
+
+typedef unsigned long jump_label_t;
+
+struct jump_entry {
+       jump_label_t code;
+       jump_label_t target;
+       jump_label_t key;
+};
+
+#endif
index 64230bc392fa39a26a61d5839ddb93f6bd4d0618..5ff15dacb5718c04973d4660c9a18478afd1c8f8 100644 (file)
@@ -23,7 +23,7 @@ CFLAGS_sysinfo.o += -Iinclude/math-emu -Iarch/s390/math-emu -w
 obj-y  :=  bitmap.o traps.o time.o process.o base.o early.o setup.o \
            processor.o sys_s390.o ptrace.o signal.o cpcmd.o ebcdic.o \
            s390_ext.o debug.o irq.o ipl.o dis.o diag.o mem_detect.o \
-           vdso.o vtime.o sysinfo.o nmi.o sclp.o
+           vdso.o vtime.o sysinfo.o nmi.o sclp.o jump_label.o
 
 obj-y  += $(if $(CONFIG_64BIT),entry64.o,entry.o)
 obj-y  += $(if $(CONFIG_64BIT),reipl64.o,reipl.o)
diff --git a/arch/s390/kernel/jump_label.c b/arch/s390/kernel/jump_label.c
new file mode 100644 (file)
index 0000000..44cc06b
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+ * Jump label s390 support
+ *
+ * Copyright IBM Corp. 2011
+ * Author(s): Jan Glauber <jang@linux.vnet.ibm.com>
+ */
+#include <linux/module.h>
+#include <linux/uaccess.h>
+#include <linux/stop_machine.h>
+#include <linux/jump_label.h>
+#include <asm/ipl.h>
+
+#ifdef HAVE_JUMP_LABEL
+
+struct insn {
+       u16 opcode;
+       s32 offset;
+} __packed;
+
+struct insn_args {
+       unsigned long *target;
+       struct insn *insn;
+       ssize_t size;
+};
+
+static int __arch_jump_label_transform(void *data)
+{
+       struct insn_args *args = data;
+       int rc;
+
+       rc = probe_kernel_write(args->target, args->insn, args->size);
+       WARN_ON_ONCE(rc < 0);
+       return 0;
+}
+
+void arch_jump_label_transform(struct jump_entry *entry,
+                              enum jump_label_type type)
+{
+       struct insn_args args;
+       struct insn insn;
+
+       if (type == JUMP_LABEL_ENABLE) {
+               /* brcl 15,offset */
+               insn.opcode = 0xc0f4;
+               insn.offset = (entry->target - entry->code) >> 1;
+       } else {
+               /* brcl 0,0 */
+               insn.opcode = 0xc004;
+               insn.offset = 0;
+       }
+
+       args.target = (void *) entry->code;
+       args.insn = &insn;
+       args.size = JUMP_LABEL_NOP_SIZE;
+
+       stop_machine(__arch_jump_label_transform, &args, NULL);
+}
+
+#endif