tile/jump_label: add jump label support for TILE-Gx

Add the arch-specific code to support jump label for TILE-Gx. This
code shares NOP instruction with ftrace, so we move it to a common
header file.

Reviewed-by: Chris Metcalf <cmetcalf@ezchip.com>
Signed-off-by: Zhigang Lu <zlu@ezchip.com>
Signed-off-by: Chris Metcalf <cmetcalf@ezchip.com>
diff --git a/arch/tile/Kconfig b/arch/tile/Kconfig
index 106c21b..49be476 100644
--- a/arch/tile/Kconfig
+++ b/arch/tile/Kconfig
@@ -143,6 +143,7 @@
 	select HAVE_KRETPROBES
 	select HAVE_ARCH_KGDB
 	select ARCH_SUPPORTS_ATOMIC_RMW
+	select HAVE_ARCH_JUMP_LABEL
 
 config TILEPRO
 	def_bool !TILEGX
diff --git a/arch/tile/include/asm/insn.h b/arch/tile/include/asm/insn.h
new file mode 100644
index 0000000..f78ba5c
--- /dev/null
+++ b/arch/tile/include/asm/insn.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2015 Tilera Corporation. All Rights Reserved.
+ *
+ *   This program is free software; you can redistribute it and/or
+ *   modify it under the terms of the GNU General Public License
+ *   as published by the Free Software Foundation, version 2.
+ *
+ *   This program is distributed in the hope that it will be useful, but
+ *   WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
+ *   NON INFRINGEMENT.  See the GNU General Public License for
+ *   more details.
+ */
+#ifndef __ASM_TILE_INSN_H
+#define __ASM_TILE_INSN_H
+
+#include <arch/opcode.h>
+
+static inline tilegx_bundle_bits NOP(void)
+{
+	return create_UnaryOpcodeExtension_X0(FNOP_UNARY_OPCODE_X0) |
+		create_RRROpcodeExtension_X0(UNARY_RRR_0_OPCODE_X0) |
+		create_Opcode_X0(RRR_0_OPCODE_X0) |
+		create_UnaryOpcodeExtension_X1(NOP_UNARY_OPCODE_X1) |
+		create_RRROpcodeExtension_X1(UNARY_RRR_0_OPCODE_X1) |
+		create_Opcode_X1(RRR_0_OPCODE_X1);
+}
+
+static inline tilegx_bundle_bits tilegx_gen_branch(unsigned long pc,
+					    unsigned long addr,
+					    bool link)
+{
+	tilegx_bundle_bits opcode_x0, opcode_x1;
+	long pcrel_by_instr = (addr - pc) >> TILEGX_LOG2_BUNDLE_SIZE_IN_BYTES;
+
+	if (link) {
+		/* opcode: jal addr */
+		opcode_x1 =
+			create_Opcode_X1(JUMP_OPCODE_X1) |
+			create_JumpOpcodeExtension_X1(JAL_JUMP_OPCODE_X1) |
+			create_JumpOff_X1(pcrel_by_instr);
+	} else {
+		/* opcode: j addr */
+		opcode_x1 =
+			create_Opcode_X1(JUMP_OPCODE_X1) |
+			create_JumpOpcodeExtension_X1(J_JUMP_OPCODE_X1) |
+			create_JumpOff_X1(pcrel_by_instr);
+	}
+
+	/* opcode: fnop */
+	opcode_x0 =
+		create_UnaryOpcodeExtension_X0(FNOP_UNARY_OPCODE_X0) |
+		create_RRROpcodeExtension_X0(UNARY_RRR_0_OPCODE_X0) |
+		create_Opcode_X0(RRR_0_OPCODE_X0);
+
+	return opcode_x1 | opcode_x0;
+}
+
+#endif /* __ASM_TILE_INSN_H */
diff --git a/arch/tile/include/asm/jump_label.h b/arch/tile/include/asm/jump_label.h
new file mode 100644
index 0000000..cde7573
--- /dev/null
+++ b/arch/tile/include/asm/jump_label.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2015 Tilera Corporation. All Rights Reserved.
+ *
+ *   This program is free software; you can redistribute it and/or
+ *   modify it under the terms of the GNU General Public License
+ *   as published by the Free Software Foundation, version 2.
+ *
+ *   This program is distributed in the hope that it will be useful, but
+ *   WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
+ *   NON INFRINGEMENT.  See the GNU General Public License for
+ *   more details.
+ */
+
+#ifndef _ASM_TILE_JUMP_LABEL_H
+#define _ASM_TILE_JUMP_LABEL_H
+
+#include <arch/opcode.h>
+
+#define JUMP_LABEL_NOP_SIZE	TILE_BUNDLE_SIZE_IN_BYTES
+
+static __always_inline bool arch_static_branch(struct static_key *key,
+					       bool branch)
+{
+	asm_volatile_goto("1:\n\t"
+		"nop" "\n\t"
+		".pushsection __jump_table,  \"aw\"\n\t"
+		".quad 1b, %l[l_yes], %0 + %1 \n\t"
+		".popsection\n\t"
+		: :  "i" (key), "i" (branch) : : l_yes);
+	return false;
+l_yes:
+	return true;
+}
+
+static __always_inline bool arch_static_branch_jump(struct static_key *key,
+						    bool branch)
+{
+	asm_volatile_goto("1:\n\t"
+		"j %l[l_yes]" "\n\t"
+		".pushsection __jump_table,  \"aw\"\n\t"
+		".quad 1b, %l[l_yes], %0 + %1 \n\t"
+		".popsection\n\t"
+		: :  "i" (key), "i" (branch) : : l_yes);
+	return false;
+l_yes:
+	return true;
+}
+
+typedef u64 jump_label_t;
+
+struct jump_entry {
+	jump_label_t code;
+	jump_label_t target;
+	jump_label_t key;
+};
+
+#endif /* _ASM_TILE_JUMP_LABEL_H */
diff --git a/arch/tile/kernel/Makefile b/arch/tile/kernel/Makefile
index 21f77bf..09936d0 100644
--- a/arch/tile/kernel/Makefile
+++ b/arch/tile/kernel/Makefile
@@ -32,5 +32,6 @@
 obj-$(CONFIG_FUNCTION_TRACER)	+= ftrace.o mcount_64.o
 obj-$(CONFIG_KPROBES)		+= kprobes.o
 obj-$(CONFIG_KGDB)		+= kgdb.o
+obj-$(CONFIG_JUMP_LABEL)	+= jump_label.o
 
 obj-y				+= vdso/
diff --git a/arch/tile/kernel/ftrace.c b/arch/tile/kernel/ftrace.c
index 4180ccdf..4a57208 100644
--- a/arch/tile/kernel/ftrace.c
+++ b/arch/tile/kernel/ftrace.c
@@ -20,21 +20,12 @@
 #include <asm/cacheflush.h>
 #include <asm/ftrace.h>
 #include <asm/sections.h>
+#include <asm/insn.h>
 
 #include <arch/opcode.h>
 
 #ifdef CONFIG_DYNAMIC_FTRACE
 
-static inline tilegx_bundle_bits NOP(void)
-{
-	return create_UnaryOpcodeExtension_X0(FNOP_UNARY_OPCODE_X0) |
-		create_RRROpcodeExtension_X0(UNARY_RRR_0_OPCODE_X0) |
-		create_Opcode_X0(RRR_0_OPCODE_X0) |
-		create_UnaryOpcodeExtension_X1(NOP_UNARY_OPCODE_X1) |
-		create_RRROpcodeExtension_X1(UNARY_RRR_0_OPCODE_X1) |
-		create_Opcode_X1(RRR_0_OPCODE_X1);
-}
-
 static int machine_stopped __read_mostly;
 
 int ftrace_arch_code_modify_prepare(void)
diff --git a/arch/tile/kernel/jump_label.c b/arch/tile/kernel/jump_label.c
new file mode 100644
index 0000000..07802d5
--- /dev/null
+++ b/arch/tile/kernel/jump_label.c
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2015 Tilera Corporation. All Rights Reserved.
+ *
+ *   This program is free software; you can redistribute it and/or
+ *   modify it under the terms of the GNU General Public License
+ *   as published by the Free Software Foundation, version 2.
+ *
+ *   This program is distributed in the hope that it will be useful, but
+ *   WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
+ *   NON INFRINGEMENT.  See the GNU General Public License for
+ *   more details.
+ *
+ * jump label TILE-Gx support
+ */
+
+#include <linux/jump_label.h>
+#include <linux/memory.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/cpu.h>
+
+#include <asm/cacheflush.h>
+#include <asm/insn.h>
+
+#ifdef HAVE_JUMP_LABEL
+
+static void __jump_label_transform(struct jump_entry *e,
+				   enum jump_label_type type)
+{
+	tilegx_bundle_bits opcode;
+	/* Operate on writable kernel text mapping. */
+	unsigned long pc_wr = ktext_writable_addr(e->code);
+
+	if (type == JUMP_LABEL_JMP)
+		opcode = tilegx_gen_branch(e->code, e->target, false);
+	else
+		opcode = NOP();
+
+	*(tilegx_bundle_bits *)pc_wr = opcode;
+	/* Make sure that above mem writes were issued towards the memory. */
+	smp_wmb();
+}
+
+void arch_jump_label_transform(struct jump_entry *e,
+				enum jump_label_type type)
+{
+	get_online_cpus();
+	mutex_lock(&text_mutex);
+
+	__jump_label_transform(e, type);
+	flush_icache_range(e->code, e->code + sizeof(tilegx_bundle_bits));
+
+	mutex_unlock(&text_mutex);
+	put_online_cpus();
+}
+
+__init_or_module void arch_jump_label_transform_static(struct jump_entry *e,
+						enum jump_label_type type)
+{
+	__jump_label_transform(e, type);
+}
+
+#endif /* HAVE_JUMP_LABEL */