2010-04-16 15:24:37

by Jason Baron

[permalink] [raw]
Subject: [PATCH 00/11] jump label v7

Hi,

Based on David Miller's suggestion this version re-works the architecture
interfaces for jump labels. I have also added a documentation file, and
re-freshed the patchset against the latest -tip tree.

The new architecture interface (quoting from the docs file), is as follows:

"
4) architecture interface

There are a few functions and macros which arches must implement in order to
take advantage of this optimization. As previously mentioned, if there is no
architecture support we simply fall back to a traditional, load, test, and
jump sequence.

* add "HAVE_ARCH_JUMP_LABEL" to arch/<arch>/Kconfig to indicate support

* #define JUMP_LABEL_NOP_SIZE, arch/x86/include/asm/jump_label.h

* #define "JUMP_LABEL(tag, label, cond)", arch/x86/include/asm/jump_label.h

* add: void arch_jump_label_transform(struct jump_entry *entry, enum jump_label_type type)
and
const u8 *arch_get_jump_label_nop(void)

see: arch/x86/kernel/jump_label.c

* finally add a definition for "struct jump_entry". This must be done in a
separate .h file, b/c the modpost.c code uses this definition to sort the
the jump label tabel in the vmlinux, so that it does not have to be sorted at
runtime. see: arch/x86/include/asm/jump_entry.h
"

David, I re-worked the sparc64 to match the updated interfaces. The code should
hopefully compile now, although I did not test the sparc bits.

thanks,

-Jason

David S. Miller (1):
sparc64: Add jump_label support

Jason Baron (9):
jump label: base patch
jump label: x86 support
jump label: tracepoint support
jump label: add module support
jump label: move ftrace_dyn_arch_init to common code
jump label: sort jump table at build-time
jump label: initialize workqueue tracepoints *before* they are
registered
jump label: jump_label_text_reserved() to reserve our jump points
jump label: add docs

Mathieu Desnoyers (1):
jump label: notifier atomic call chain notrace

Documentation/jump-label.txt | 140 +++++++++++
Makefile | 6 +-
arch/Kconfig | 3 +
arch/sparc/Kconfig | 1 +
arch/sparc/include/asm/jump_entry.h | 16 ++
arch/sparc/include/asm/jump_label.h | 21 ++
arch/sparc/kernel/Makefile | 2 +
arch/sparc/kernel/jump_label.c | 38 +++
arch/sparc/kernel/module.c | 6 +
arch/x86/Kconfig | 1 +
arch/x86/include/asm/alternative.h | 14 ++
arch/x86/include/asm/jump_entry.h | 28 +++
arch/x86/include/asm/jump_label.h | 23 ++
arch/x86/kernel/Makefile | 2 +-
arch/x86/kernel/alternative.c | 71 ++++++-
arch/x86/kernel/ftrace.c | 70 +------
arch/x86/kernel/jump_label.c | 47 ++++
arch/x86/kernel/kprobes.c | 3 +-
arch/x86/kernel/module.c | 3 +
arch/x86/kernel/ptrace.c | 1 +
arch/x86/kernel/setup.c | 3 +
include/asm-generic/vmlinux.lds.h | 22 ++-
include/linux/jump_label.h | 75 ++++++
include/linux/module.h | 5 +-
include/linux/tracepoint.h | 34 ++--
kernel/Makefile | 2 +-
kernel/jump_label.c | 431 +++++++++++++++++++++++++++++++++++
kernel/kprobes.c | 3 +-
kernel/module.c | 7 +
kernel/notifier.c | 6 +-
kernel/trace/ftrace.c | 13 +-
kernel/trace/trace_workqueue.c | 10 +-
kernel/tracepoint.c | 8 +
scripts/mod/Makefile | 1 +
scripts/mod/modpost.c | 71 ++++++-
scripts/mod/modpost.h | 2 +
36 files changed, 1073 insertions(+), 116 deletions(-)
create mode 100644 Documentation/jump-label.txt
create mode 100644 arch/sparc/include/asm/jump_entry.h
create mode 100644 arch/sparc/include/asm/jump_label.h
create mode 100644 arch/sparc/kernel/jump_label.c
create mode 100644 arch/x86/include/asm/jump_entry.h
create mode 100644 arch/x86/include/asm/jump_label.h
create mode 100644 arch/x86/kernel/jump_label.c
create mode 100644 include/linux/jump_label.h
create mode 100644 kernel/jump_label.c


2010-04-16 15:24:46

by Jason Baron

[permalink] [raw]
Subject: [PATCH 02/11] jump label: base patch

base patch to implement 'jump labeling'. Based on a new 'asm goto' inline
assembly gcc mechanism, we can now branch to labels from an 'asm goto'
statment. This allows us to create a 'no-op' fastpath, which can subsequently
be patched with a jump to the slowpath code. This is useful for code which
might be rarely used, but which we'd like to be able to call, if needed.
Tracepoints are the current usecase that these are being implemented for.

Signed-off-by: Jason Baron <[email protected]>
---
arch/Kconfig | 3 +
include/asm-generic/vmlinux.lds.h | 10 ++-
include/linux/jump_label.h | 59 ++++++++++++
kernel/Makefile | 2 +-
kernel/jump_label.c | 179 +++++++++++++++++++++++++++++++++++++
5 files changed, 251 insertions(+), 2 deletions(-)
create mode 100644 include/linux/jump_label.h
create mode 100644 kernel/jump_label.c

diff --git a/arch/Kconfig b/arch/Kconfig
index f06010f..4e7a1ec 100644
--- a/arch/Kconfig
+++ b/arch/Kconfig
@@ -140,4 +140,7 @@ config HAVE_HW_BREAKPOINT
config HAVE_USER_RETURN_NOTIFIER
bool

+config HAVE_ARCH_JUMP_LABEL
+ bool
+
source "kernel/gcov/Kconfig"
diff --git a/include/asm-generic/vmlinux.lds.h b/include/asm-generic/vmlinux.lds.h
index 67e6520..83a469d 100644
--- a/include/asm-generic/vmlinux.lds.h
+++ b/include/asm-generic/vmlinux.lds.h
@@ -167,7 +167,8 @@
BRANCH_PROFILE() \
TRACE_PRINTKS() \
FTRACE_EVENTS() \
- TRACE_SYSCALLS()
+ TRACE_SYSCALLS() \
+ JUMP_TABLE() \

/*
* Data section helpers
@@ -206,6 +207,7 @@
*(__vermagic) /* Kernel version magic */ \
*(__markers_strings) /* Markers: strings */ \
*(__tracepoints_strings)/* Tracepoints: strings */ \
+ *(__jump_strings)/* Jump: strings */ \
} \
\
.rodata1 : AT(ADDR(.rodata1) - LOAD_OFFSET) { \
@@ -557,6 +559,12 @@
#define BUG_TABLE
#endif

+#define JUMP_TABLE() \
+ . = ALIGN(64); \
+ VMLINUX_SYMBOL(__start___jump_table) = .; \
+ *(__jump_table) \
+ VMLINUX_SYMBOL(__stop___jump_table) = .; \
+
#ifdef CONFIG_PM_TRACE
#define TRACEDATA \
. = ALIGN(4); \
diff --git a/include/linux/jump_label.h b/include/linux/jump_label.h
new file mode 100644
index 0000000..914ff7c
--- /dev/null
+++ b/include/linux/jump_label.h
@@ -0,0 +1,59 @@
+#ifndef _LINUX_JUMP_LABEL_H
+#define _LINUX_JUMP_LABEL_H
+
+#if (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5))
+# ifdef CONFIG_HAVE_ARCH_JUMP_LABEL
+# include <asm/jump_label.h>
+# define HAVE_JUMP_LABEL 1
+# endif
+#endif
+
+enum jump_label_type {
+ JUMP_LABEL_ENABLE,
+ JUMP_LABEL_DISABLE
+};
+
+#ifdef HAVE_JUMP_LABEL
+
+extern struct jump_entry __start___jump_table[];
+extern struct jump_entry __stop___jump_table[];
+
+#define DEFINE_JUMP_LABEL(name) \
+ const char __jlstrtab_##name[] \
+ __used __attribute__((section("__jump_strings"))) = #name;
+
+extern void arch_jump_label_transform(struct jump_entry *entry,
+ enum jump_label_type type);
+extern const u8 *arch_get_jump_label_nop(void);
+
+extern void jump_label_update(const char *name, enum jump_label_type type);
+
+#define enable_jump_label(name) \
+ jump_label_update(name, JUMP_LABEL_ENABLE);
+
+#define disable_jump_label(name) \
+ jump_label_update(name, JUMP_LABEL_DISABLE);
+
+#else
+
+#define DEFINE_JUMP_LABEL(name)
+
+#define JUMP_LABEL(tag, label, cond) \
+do { \
+ if (unlikely(cond)) \
+ goto label; \
+} while (0)
+
+static inline int enable_jump_label(const char *name)
+{
+ return 0;
+}
+
+static inline int disable_jump_label(const char *name)
+{
+ return 0;
+}
+
+#endif
+
+#endif
diff --git a/kernel/Makefile b/kernel/Makefile
index d5c3006..59ff12e 100644
--- a/kernel/Makefile
+++ b/kernel/Makefile
@@ -10,7 +10,7 @@ obj-y = sched.o fork.o exec_domain.o panic.o printk.o \
kthread.o wait.o kfifo.o sys_ni.o posix-cpu-timers.o mutex.o \
hrtimer.o rwsem.o nsproxy.o srcu.o semaphore.o \
notifier.o ksysfs.o pm_qos_params.o sched_clock.o cred.o \
- async.o range.o
+ async.o range.o jump_label.o
obj-$(CONFIG_HAVE_EARLY_RES) += early_res.o
obj-y += groups.o

diff --git a/kernel/jump_label.c b/kernel/jump_label.c
new file mode 100644
index 0000000..f518922
--- /dev/null
+++ b/kernel/jump_label.c
@@ -0,0 +1,179 @@
+/*
+ * jump label support
+ *
+ * Copyright (C) 2009 Jason Baron <[email protected]>
+ *
+ */
+#include <linux/jump_label.h>
+#include <linux/memory.h>
+#include <linux/uaccess.h>
+#include <linux/module.h>
+#include <linux/list.h>
+#include <linux/jhash.h>
+#include <linux/slab.h>
+#include <asm/alternative.h>
+
+#ifdef HAVE_JUMP_LABEL
+
+#define JUMP_LABEL_HASH_BITS 6
+#define JUMP_LABEL_TABLE_SIZE (1 << JUMP_LABEL_HASH_BITS)
+static struct hlist_head jump_label_table[JUMP_LABEL_TABLE_SIZE];
+
+/* mutex to protect coming/going of the the jump_label table */
+static DEFINE_MUTEX(jump_label_mutex);
+
+struct jump_label_entry {
+ struct hlist_node hlist;
+ struct jump_entry *table;
+ int nr_entries;
+ /* hang modules off here */
+ struct hlist_head modules;
+ const char *name;
+};
+
+static void swap_jump_label_entries(struct jump_entry *previous, struct jump_entry *next)
+{
+ struct jump_entry tmp;
+
+ tmp = *next;
+ *next = *previous;
+ *previous = tmp;
+}
+
+static void sort_jump_label_entries(struct jump_entry *start, struct jump_entry *stop)
+{
+ int swapped = 0;
+ struct jump_entry *iter;
+ struct jump_entry *iter_next;
+
+ do {
+ swapped = 0;
+ iter = start;
+ iter_next = start;
+ iter_next++;
+ for (; iter_next < stop; iter++, iter_next++) {
+ if (strcmp((char *)iter->name,
+ (char *)iter_next->name) > 0) {
+ swap_jump_label_entries(iter, iter_next);
+ swapped = 1;
+ }
+ }
+ } while (swapped == 1);
+}
+
+static struct jump_label_entry *get_jump_label_entry(const char *name)
+{
+ struct hlist_head *head;
+ struct hlist_node *node;
+ struct jump_label_entry *e;
+ u32 hash = jhash(name, strlen(name), 0);
+
+ head = &jump_label_table[hash & (JUMP_LABEL_TABLE_SIZE - 1)];
+ hlist_for_each_entry(e, node, head, hlist) {
+ if (!strcmp((char *)name, (char *)e->name))
+ return e;
+ }
+ return NULL;
+}
+
+static struct jump_label_entry *add_jump_label_entry(const char *name, int nr_entries, struct jump_entry *table)
+{
+ struct hlist_head *head;
+ struct jump_label_entry *e;
+ size_t name_len;
+ u32 hash;
+
+ e = get_jump_label_entry(name);
+ if (e)
+ return ERR_PTR(-EEXIST);
+
+ e = kmalloc(sizeof(struct jump_label_entry), GFP_KERNEL);
+ if (!e)
+ return ERR_PTR(-ENOMEM);
+
+ name_len = strlen(name) + 1;
+ hash = jhash(name, name_len-1, 0);
+ head = &jump_label_table[hash & (JUMP_LABEL_TABLE_SIZE - 1)];
+ e->name = name;
+ e->table = table;
+ e->nr_entries = nr_entries;
+ INIT_HLIST_HEAD(&(e->modules));
+ hlist_add_head(&e->hlist, head);
+ return e;
+}
+
+static int build_jump_label_hashtable(struct jump_entry *start, struct jump_entry *stop)
+{
+ struct jump_entry *iter, *iter_begin;
+ struct jump_label_entry *entry;
+ int count;
+
+ sort_jump_label_entries(start, stop);
+ iter = start;
+ while (iter < stop) {
+ entry = get_jump_label_entry((char *)iter->name);
+ if (!entry) {
+ iter_begin = iter;
+ count = 0;
+ while ((iter < stop) &&
+ (strcmp((char *)iter->name,
+ (char *)iter_begin->name) == 0)) {
+ iter++;
+ count++;
+ }
+ entry = add_jump_label_entry((char *)iter_begin->name,
+ count, iter_begin);
+ if (IS_ERR(entry))
+ return PTR_ERR(entry);
+ continue;
+ }
+ WARN(1, KERN_ERR "build_jump_hashtable: unexpected entry!\n");
+ }
+ return 0;
+}
+
+/***
+ * jump_label_update - update jump label text
+ * @name - name of the jump label
+ * @type - enum set to JUMP_LABEL_ENABLE or JUMP_LABEL_DISABLE
+ *
+ * Will enable/disable the jump for jump label @name, depending on the
+ * value of @type.
+ *
+ */
+
+void jump_label_update(const char *name, enum jump_label_type type)
+{
+ struct jump_entry *iter;
+ struct jump_label_entry *entry;
+ struct hlist_node *module_node;
+ struct jump_label_module_entry *e_module;
+ int count;
+
+ mutex_lock(&jump_label_mutex);
+ entry = get_jump_label_entry(name);
+ if (entry) {
+ count = entry->nr_entries;
+ iter = entry->table;
+ while (count--) {
+ if (kernel_text_address(iter->code))
+ arch_jump_label_transform(iter, type);
+ iter++;
+ }
+ }
+ mutex_unlock(&jump_label_mutex);
+}
+
+static int init_jump_label(void)
+{
+ int ret;
+
+ mutex_lock(&jump_label_mutex);
+ ret = build_jump_label_hashtable(__start___jump_table,
+ __stop___jump_table);
+ mutex_unlock(&jump_label_mutex);
+ return ret;
+}
+early_initcall(init_jump_label);
+
+#endif
--
1.7.0.1

2010-04-16 15:24:53

by Jason Baron

[permalink] [raw]
Subject: [PATCH 03/11] jump label: x86 support

add x86 support for jump label. I'm keeping this patch separate so its clear to
arch maintainers what was required for x86 support this new feature. hopefully,
it wouldn't be too painful for other arches.

Signed-off-by: Jason Baron <[email protected]>
---
arch/x86/Kconfig | 1 +
arch/x86/include/asm/jump_entry.h | 28 +++++++++++++++++
arch/x86/include/asm/jump_label.h | 27 ++++++++++++++++
arch/x86/kernel/Makefile | 2 +-
arch/x86/kernel/jump_label.c | 60 +++++++++++++++++++++++++++++++++++++
5 files changed, 117 insertions(+), 1 deletions(-)
create mode 100644 arch/x86/include/asm/jump_entry.h
create mode 100644 arch/x86/include/asm/jump_label.h
create mode 100644 arch/x86/kernel/jump_label.c

diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig
index d3b7bb3..802b91b 100644
--- a/arch/x86/Kconfig
+++ b/arch/x86/Kconfig
@@ -58,6 +58,7 @@ config X86
select ANON_INODES
select HAVE_ARCH_KMEMCHECK
select HAVE_USER_RETURN_NOTIFIER
+ select HAVE_ARCH_JUMP_LABEL

config INSTRUCTION_DECODER
def_bool (KPROBES || PERF_EVENTS)
diff --git a/arch/x86/include/asm/jump_entry.h b/arch/x86/include/asm/jump_entry.h
new file mode 100644
index 0000000..9b32696
--- /dev/null
+++ b/arch/x86/include/asm/jump_entry.h
@@ -0,0 +1,28 @@
+#ifndef _LINUX_JUMP_ENTRY_H
+#define _LINUX_JUMP_ENTRY_H
+
+/* Defined in a separate file, so that it can be used by user program modpost.c
+ * This file is included by include/asm/jump_label.h, in turn included by
+ * include/linux/jump_label.h. Kernel sources should only include
+ * include/linux/jump_label.h.
+ **/
+
+#ifdef CONFIG_X86_64
+
+struct jump_entry {
+ __u64 code;
+ __u64 target;
+ __u64 name;
+};
+
+#else
+
+struct jump_entry {
+ __u32 code;
+ __u32 target;
+ __u32 name;
+};
+
+#endif
+
+#endif
diff --git a/arch/x86/include/asm/jump_label.h b/arch/x86/include/asm/jump_label.h
new file mode 100644
index 0000000..70da737
--- /dev/null
+++ b/arch/x86/include/asm/jump_label.h
@@ -0,0 +1,27 @@
+#ifndef _ASM_X86_JUMP_LABEL_H
+#define _ASM_X86_JUMP_LABEL_H
+
+#include <linux/types.h>
+#include <asm/nops.h>
+#include <asm/jump_entry.h>
+
+#define JUMP_LABEL_NOP_SIZE 5
+
+#ifdef CONFIG_X86_64
+# define JUMP_LABEL_NOP P6_NOP5
+#else
+# define JUMP_LABEL_NOP ".byte 0xe9 \n\t .long 0\n\t"
+#endif
+
+#define JUMP_LABEL(tag, label, cond) \
+ do { \
+ extern const char __jlstrtab_##tag[]; \
+ asm goto("1:" \
+ JUMP_LABEL_NOP \
+ ".pushsection __jump_table, \"a\" \n\t"\
+ _ASM_PTR "1b, %l[" #label "], %c0 \n\t" \
+ ".popsection \n\t" \
+ : : "i" (__jlstrtab_##tag) : : label);\
+ } while (0)
+
+#endif
diff --git a/arch/x86/kernel/Makefile b/arch/x86/kernel/Makefile
index e77b220..44f6bea 100644
--- a/arch/x86/kernel/Makefile
+++ b/arch/x86/kernel/Makefile
@@ -32,7 +32,7 @@ GCOV_PROFILE_paravirt.o := n
obj-y := process_$(BITS).o signal.o entry_$(BITS).o
obj-y += traps.o irq.o irq_$(BITS).o dumpstack_$(BITS).o
obj-y += time.o ioport.o ldt.o dumpstack.o
-obj-y += setup.o x86_init.o i8259.o irqinit.o
+obj-y += setup.o x86_init.o i8259.o irqinit.o jump_label.o
obj-$(CONFIG_X86_VISWS) += visws_quirks.o
obj-$(CONFIG_X86_32) += probe_roms_32.o
obj-$(CONFIG_X86_32) += sys_i386_32.o i386_ksyms_32.o
diff --git a/arch/x86/kernel/jump_label.c b/arch/x86/kernel/jump_label.c
new file mode 100644
index 0000000..c347431
--- /dev/null
+++ b/arch/x86/kernel/jump_label.c
@@ -0,0 +1,60 @@
+/*
+ * jump label x86 support
+ *
+ * Copyright (C) 2009 Jason Baron <[email protected]>
+ *
+ */
+#include <linux/jump_label.h>
+#include <linux/memory.h>
+#include <linux/uaccess.h>
+#include <linux/module.h>
+#include <linux/list.h>
+#include <linux/jhash.h>
+#include <linux/cpu.h>
+#include <asm/kprobes.h>
+
+#ifdef HAVE_JUMP_LABEL
+
+union jump_code_union {
+ char code[JUMP_LABEL_NOP_SIZE];
+ struct {
+ char jump;
+ int offset;
+ } __attribute__((packed));
+};
+
+void arch_jump_label_transform(struct jump_entry *entry, enum jump_label_type type)
+{
+ union jump_code_union code;
+
+ if (type == JUMP_LABEL_ENABLE) {
+ code.jump = 0xe9;
+ code.offset = entry->target - (entry->code + JUMP_LABEL_NOP_SIZE);
+ } else {
+#ifdef CONFIG_X86_64
+ /* opcode for P6_NOP5 */
+ code.code[0] = 0x0f;
+ code.code[1] = 0x1f;
+ code.code[2] = 0x44;
+ code.code[3] = 0x00;
+ code.code[4] = 0x00;
+#else
+ code.jump = 0xe9;
+ code.offset = 0;
+#endif
+ }
+ get_online_cpus();
+ mutex_lock(&text_mutex);
+ text_poke_smp((void *)entry->code, &code, JUMP_LABEL_NOP_SIZE);
+ mutex_unlock(&text_mutex);
+ put_online_cpus();
+}
+
+static const u8 jump_label_nop[JUMP_LABEL_NOP_SIZE] = { 0x0f, 0x1f, 0x44, 0x00, 0x00 };
+
+const u8 *arch_get_jump_label_nop()
+{
+ return jump_label_nop;
+}
+
+#endif
--
1.7.0.1

2010-04-16 15:25:02

by Jason Baron

[permalink] [raw]
Subject: [PATCH 05/11] jump label: add module support

Add support for 'jump label' for modules.

Signed-off-by: Jason Baron <[email protected]>
---
arch/x86/kernel/ptrace.c | 1 +
include/linux/jump_label.h | 3 +-
include/linux/module.h | 5 +-
kernel/jump_label.c | 136 ++++++++++++++++++++++++++++++++++++++++++++
kernel/module.c | 7 ++
5 files changed, 150 insertions(+), 2 deletions(-)

diff --git a/arch/x86/kernel/ptrace.c b/arch/x86/kernel/ptrace.c
index 055be0a..f5d411b 100644
--- a/arch/x86/kernel/ptrace.c
+++ b/arch/x86/kernel/ptrace.c
@@ -21,6 +21,7 @@
#include <linux/signal.h>
#include <linux/perf_event.h>
#include <linux/hw_breakpoint.h>
+#include <linux/module.h>

#include <asm/uaccess.h>
#include <asm/pgtable.h>
diff --git a/include/linux/jump_label.h b/include/linux/jump_label.h
index 914ff7c..ab879ef 100644
--- a/include/linux/jump_label.h
+++ b/include/linux/jump_label.h
@@ -20,7 +20,8 @@ extern struct jump_entry __stop___jump_table[];

#define DEFINE_JUMP_LABEL(name) \
const char __jlstrtab_##name[] \
- __used __attribute__((section("__jump_strings"))) = #name;
+ __used __attribute__((section("__jump_strings"))) = #name; \
+ EXPORT_SYMBOL_GPL(__jlstrtab_##name);

extern void arch_jump_label_transform(struct jump_entry *entry,
enum jump_label_type type);
diff --git a/include/linux/module.h b/include/linux/module.h
index 6914fca..4575f89 100644
--- a/include/linux/module.h
+++ b/include/linux/module.h
@@ -343,7 +343,10 @@ struct module
struct tracepoint *tracepoints;
unsigned int num_tracepoints;
#endif
-
+#ifdef HAVE_JUMP_LABEL
+ struct jump_entry *jump_entries;
+ unsigned int num_jump_entries;
+#endif
#ifdef CONFIG_TRACING
const char **trace_bprintk_fmt_start;
unsigned int num_trace_bprintk_fmt;
diff --git a/kernel/jump_label.c b/kernel/jump_label.c
index f518922..0484b7e 100644
--- a/kernel/jump_label.c
+++ b/kernel/jump_label.c
@@ -31,6 +31,13 @@ struct jump_label_entry {
const char *name;
};

+struct jump_label_module_entry {
+ struct hlist_node hlist;
+ struct jump_entry *table;
+ int nr_entries;
+ struct module *mod;
+};
+
static void swap_jump_label_entries(struct jump_entry *previous, struct jump_entry *next)
{
struct jump_entry tmp;
@@ -160,6 +167,17 @@ void jump_label_update(const char *name, enum jump_label_type type)
arch_jump_label_transform(iter, type);
iter++;
}
+ /* eanble/disable jump labels in modules */
+ hlist_for_each_entry(e_module, module_node, &(entry->modules),
+ hlist) {
+ count = e_module->nr_entries;
+ iter = e_module->table;
+ while (count--) {
+ if (kernel_text_address(iter->code))
+ arch_jump_label_transform(iter, type);
+ iter++;
+ }
+ }
}
mutex_unlock(&jump_label_mutex);
}
@@ -176,4 +194,122 @@ static int init_jump_label(void)
}
early_initcall(init_jump_label);

+#ifdef CONFIG_MODULES
+
+static struct jump_label_module_entry *add_jump_label_module_entry(struct jump_label_entry *entry, struct jump_entry *iter_begin, int count, struct module *mod)
+{
+ struct jump_label_module_entry *e;
+
+ e = kmalloc(sizeof(struct jump_label_module_entry), GFP_KERNEL);
+ if (!e)
+ return ERR_PTR(-ENOMEM);
+ e->mod = mod;
+ e->nr_entries = count;
+ e->table = iter_begin;
+ hlist_add_head(&e->hlist, &entry->modules);
+ return e;
+}
+
+static int add_jump_label_module(struct module *mod)
+{
+ struct jump_entry *iter, *iter_begin;
+ struct jump_label_entry *entry;
+ struct jump_label_module_entry *module_entry;
+ int count;
+
+ /* if the module doesn't have jump label entries, just return */
+ if (!mod->num_jump_entries)
+ return 0;
+
+ sort_jump_label_entries(mod->jump_entries,
+ mod->jump_entries + mod->num_jump_entries);
+ iter = mod->jump_entries;
+ while (iter < mod->jump_entries + mod->num_jump_entries) {
+ entry = get_jump_label_entry((char *)iter->name);
+ iter_begin = iter;
+ count = 0;
+ while ((iter < mod->jump_entries + mod->num_jump_entries) &&
+ (strcmp((char *)iter->name, (char *)iter_begin->name) == 0)) {
+ iter++;
+ count++;
+ }
+ if (!entry) {
+ entry = add_jump_label_entry((char *)iter_begin->name, 0, NULL);
+ if (IS_ERR(entry))
+ return PTR_ERR(entry);
+ }
+ module_entry = add_jump_label_module_entry(entry, iter_begin,
+ count, mod);
+ if (IS_ERR(module_entry))
+ return PTR_ERR(module_entry);
+ }
+ return 0;
+}
+
+static void remove_jump_label_module(struct module *mod)
+{
+ struct hlist_head *head;
+ struct hlist_node *node, *node_next, *module_node, *module_node_next;
+ struct jump_label_entry *e;
+ struct jump_label_module_entry *e_module;
+ int i;
+
+ /* if the module doesn't have jump label entries, just return */
+ if (!mod->num_jump_entries)
+ return;
+
+ for (i = 0; i < JUMP_LABEL_TABLE_SIZE; i++) {
+ head = &jump_label_table[i];
+ hlist_for_each_entry_safe(e, node, node_next, head, hlist) {
+ hlist_for_each_entry_safe(e_module, module_node,
+ module_node_next,
+ &(e->modules), hlist) {
+ if (e_module->mod == mod) {
+ hlist_del(&e_module->hlist);
+ kfree(e_module);
+ }
+ }
+ if (hlist_empty(&e->modules) && (e->nr_entries == 0)) {
+ hlist_del(&e->hlist);
+ kfree(e);
+ }
+ }
+ }
+}
+
+static int jump_label_module_notify(struct notifier_block *self, unsigned long val, void *data)
+{
+ struct module *mod = data;
+ int ret = 0;
+
+ switch (val) {
+ case MODULE_STATE_COMING:
+ mutex_lock(&jump_label_mutex);
+ ret = add_jump_label_module(mod);
+ if (ret)
+ remove_jump_label_module(mod);
+ mutex_unlock(&jump_label_mutex);
+ break;
+ case MODULE_STATE_GOING:
+ mutex_lock(&jump_label_mutex);
+ remove_jump_label_module(mod);
+ mutex_unlock(&jump_label_mutex);
+ break;
+ }
+ return ret;
+}
+
+struct notifier_block jump_label_module_nb = {
+ .notifier_call = jump_label_module_notify,
+ .priority = 0,
+};
+
+static int init_jump_label_module(void)
+{
+ return register_module_notifier(&jump_label_module_nb);
+}
+early_initcall(init_jump_label_module);
+
+#endif /* CONFIG_MODULES */
+
#endif
diff --git a/kernel/module.c b/kernel/module.c
index b8a1e31..add858f 100644
--- a/kernel/module.c
+++ b/kernel/module.c
@@ -55,6 +55,7 @@
#include <linux/async.h>
#include <linux/percpu.h>
#include <linux/kmemleak.h>
+#include <linux/jump_label.h>

#define CREATE_TRACE_POINTS
#include <trace/events/module.h>
@@ -2311,6 +2312,12 @@ static noinline struct module *load_module(void __user *umod,
sizeof(*mod->tracepoints),
&mod->num_tracepoints);
#endif
+#ifdef HAVE_JUMP_LABEL
+ mod->jump_entries = section_objs(hdr, sechdrs, secstrings,
+ "__jump_table",
+ sizeof(*mod->jump_entries),
+ &mod->num_jump_entries);
+#endif
#ifdef CONFIG_EVENT_TRACING
mod->trace_events = section_objs(hdr, sechdrs, secstrings,
"_ftrace_events",
--
1.7.0.1

2010-04-16 15:25:12

by Jason Baron

[permalink] [raw]
Subject: [PATCH 06/11] jump label: move ftrace_dyn_arch_init to common code

Move Steve's code for finding the best 5-byte no-op from ftrace.c to alternative.c.
The idea is that other consumers (in this case jump label) want to make use of
that code. I've created a global: 'char ideal_nop[5]', that is setup during
setup_arch that can be used.

Signed-off-by: Jason Baron <[email protected]>
---
arch/x86/include/asm/alternative.h | 14 +++++++
arch/x86/include/asm/jump_label.h | 10 ++----
arch/x86/kernel/alternative.c | 71 +++++++++++++++++++++++++++++++++++-
arch/x86/kernel/ftrace.c | 70 +-----------------------------------
arch/x86/kernel/jump_label.c | 19 ++--------
arch/x86/kernel/module.c | 3 ++
arch/x86/kernel/setup.c | 3 ++
include/linux/jump_label.h | 9 +++++
kernel/jump_label.c | 33 +++++++++++++++++
kernel/trace/ftrace.c | 13 +------
10 files changed, 139 insertions(+), 106 deletions(-)

diff --git a/arch/x86/include/asm/alternative.h b/arch/x86/include/asm/alternative.h
index b09ec55..0feca01 100644
--- a/arch/x86/include/asm/alternative.h
+++ b/arch/x86/include/asm/alternative.h
@@ -4,6 +4,7 @@
#include <linux/types.h>
#include <linux/stddef.h>
#include <linux/stringify.h>
+#include <linux/jump_label.h>
#include <asm/asm.h>

/*
@@ -153,6 +154,8 @@ static inline void apply_paravirt(struct paravirt_patch_site *start,
#define __parainstructions_end NULL
#endif

+extern void *text_poke_early(void *addr, const void *opcode, size_t len);
+
/*
* Clear and restore the kernel write-protection flag on the local CPU.
* Allows the kernel to edit read-only pages.
@@ -173,4 +176,15 @@ static inline void apply_paravirt(struct paravirt_patch_site *start,
extern void *text_poke(void *addr, const void *opcode, size_t len);
extern void *text_poke_smp(void *addr, const void *opcode, size_t len);

+#if defined(CONFIG_DYNAMIC_FTRACE) || defined(HAVE_JUMP_LABEL)
+#define IDEAL_NOP_SIZE_5 5
+extern unsigned char ideal_nop5[IDEAL_NOP_SIZE_5];
+extern int arch_init_ideal_nop5(void);
+#else
+static inline int arch_init_ideal_nop5(void)
+{
+ return 0;
+}
+#endif
+
#endif /* _ASM_X86_ALTERNATIVE_H */
diff --git a/arch/x86/include/asm/jump_label.h b/arch/x86/include/asm/jump_label.h
index 70da737..a65a71d 100644
--- a/arch/x86/include/asm/jump_label.h
+++ b/arch/x86/include/asm/jump_label.h
@@ -7,17 +7,13 @@

#define JUMP_LABEL_NOP_SIZE 5

-#ifdef CONFIG_X86_64
-# define JUMP_LABEL_NOP P6_NOP5
-#else
-# define JUMP_LABEL_NOP ".byte 0xe9 \n\t .long 0\n\t"
-#endif
+# define JUMP_LABEL_INITIAL_NOP ".byte 0xe9 \n\t .long 0\n\t"

-#define JUMP_LABEL(tag, label, cond) \
+# define JUMP_LABEL(tag, label, cond) \
do { \
extern const char __jlstrtab_##tag[]; \
asm goto("1:" \
- JUMP_LABEL_NOP \
+ JUMP_LABEL_INITIAL_NOP \
".pushsection __jump_table, \"a\" \n\t"\
_ASM_PTR "1b, %l[" #label "], %c0 \n\t" \
".popsection \n\t" \
diff --git a/arch/x86/kernel/alternative.c b/arch/x86/kernel/alternative.c
index 1a160d5..e472504 100644
--- a/arch/x86/kernel/alternative.c
+++ b/arch/x86/kernel/alternative.c
@@ -195,7 +195,6 @@ static void __init_or_module add_nops(void *insns, unsigned int len)

extern struct alt_instr __alt_instructions[], __alt_instructions_end[];
extern u8 *__smp_locks[], *__smp_locks_end[];
-static void *text_poke_early(void *addr, const void *opcode, size_t len);

/* Replace instructions with better alternatives for this CPU type.
This runs before SMP is initialized to avoid SMP problems with
@@ -514,7 +513,7 @@ void __init alternative_instructions(void)
* instructions. And on the local CPU you need to be protected again NMI or MCE
* handlers seeing an inconsistent instruction while you patch.
*/
-static void *__init_or_module text_poke_early(void *addr, const void *opcode,
+void *__init_or_module text_poke_early(void *addr, const void *opcode,
size_t len)
{
unsigned long flags;
@@ -633,3 +632,71 @@ void *__kprobes text_poke_smp(void *addr, const void *opcode, size_t len)
return addr;
}

+#if defined(CONFIG_DYNAMIC_FTRACE) || defined(HAVE_JUMP_LABEL)
+
+unsigned char ideal_nop5[IDEAL_NOP_SIZE_5];
+
+int __init arch_init_ideal_nop5(void)
+{
+ extern const unsigned char ftrace_test_p6nop[];
+ extern const unsigned char ftrace_test_nop5[];
+ extern const unsigned char ftrace_test_jmp[];
+ int faulted = 0;
+ unsigned long flags;
+
+ local_irq_save(flags);
+ /*
+ * There is no good nop for all x86 archs.
+ * We will default to using the P6_NOP5, but first we
+ * will test to make sure that the nop will actually
+ * work on this CPU. If it faults, we will then
+ * go to a lesser efficient 5 byte nop. If that fails
+ * we then just use a jmp as our nop. This isn't the most
+ * efficient nop, but we can not use a multi part nop
+ * since we would then risk being preempted in the middle
+ * of that nop, and if we enabled tracing then, it might
+ * cause a system crash.
+ *
+ * TODO: check the cpuid to determine the best nop.
+ */
+ asm volatile (
+ "ftrace_test_jmp:"
+ "jmp ftrace_test_p6nop\n"
+ "nop\n"
+ "nop\n"
+ "nop\n" /* 2 byte jmp + 3 bytes */
+ "ftrace_test_p6nop:"
+ P6_NOP5
+ "jmp 1f\n"
+ "ftrace_test_nop5:"
+ ".byte 0x66,0x66,0x66,0x66,0x90\n"
+ "1:"
+ ".section .fixup, \"ax\"\n"
+ "2: movl $1, %0\n"
+ " jmp ftrace_test_nop5\n"
+ "3: movl $2, %0\n"
+ " jmp 1b\n"
+ ".previous\n"
+ _ASM_EXTABLE(ftrace_test_p6nop, 2b)
+ _ASM_EXTABLE(ftrace_test_nop5, 3b)
+ : "=r"(faulted) : "0" (faulted));
+
+ switch (faulted) {
+ case 0:
+ pr_info("converting mcount calls to 0f 1f 44 00 00\n");
+ memcpy(ideal_nop5, ftrace_test_p6nop, IDEAL_NOP_SIZE_5);
+ break;
+ case 1:
+ pr_info("converting mcount calls to 66 66 66 66 90\n");
+ memcpy(ideal_nop5, ftrace_test_nop5, IDEAL_NOP_SIZE_5);
+ break;
+ case 2:
+ pr_info("converting mcount calls to jmp . + 5\n");
+ memcpy(ideal_nop5, ftrace_test_jmp, IDEAL_NOP_SIZE_5);
+ break;
+ }
+
+ local_irq_restore(flags);
+ return 0;
+}
+#endif
diff --git a/arch/x86/kernel/ftrace.c b/arch/x86/kernel/ftrace.c
index cd37469..ba2e0d9 100644
--- a/arch/x86/kernel/ftrace.c
+++ b/arch/x86/kernel/ftrace.c
@@ -257,14 +257,9 @@ do_ftrace_mod_code(unsigned long ip, void *new_code)
return mod_code_status;
}

-
-
-
-static unsigned char ftrace_nop[MCOUNT_INSN_SIZE];
-
static unsigned char *ftrace_nop_replace(void)
{
- return ftrace_nop;
+ return ideal_nop5;
}

static int
@@ -336,69 +331,6 @@ int ftrace_update_ftrace_func(ftrace_func_t func)
return ret;
}

-int __init ftrace_dyn_arch_init(void *data)
-{
- extern const unsigned char ftrace_test_p6nop[];
- extern const unsigned char ftrace_test_nop5[];
- extern const unsigned char ftrace_test_jmp[];
- int faulted = 0;
-
- /*
- * There is no good nop for all x86 archs.
- * We will default to using the P6_NOP5, but first we
- * will test to make sure that the nop will actually
- * work on this CPU. If it faults, we will then
- * go to a lesser efficient 5 byte nop. If that fails
- * we then just use a jmp as our nop. This isn't the most
- * efficient nop, but we can not use a multi part nop
- * since we would then risk being preempted in the middle
- * of that nop, and if we enabled tracing then, it might
- * cause a system crash.
- *
- * TODO: check the cpuid to determine the best nop.
- */
- asm volatile (
- "ftrace_test_jmp:"
- "jmp ftrace_test_p6nop\n"
- "nop\n"
- "nop\n"
- "nop\n" /* 2 byte jmp + 3 bytes */
- "ftrace_test_p6nop:"
- P6_NOP5
- "jmp 1f\n"
- "ftrace_test_nop5:"
- ".byte 0x66,0x66,0x66,0x66,0x90\n"
- "1:"
- ".section .fixup, \"ax\"\n"
- "2: movl $1, %0\n"
- " jmp ftrace_test_nop5\n"
- "3: movl $2, %0\n"
- " jmp 1b\n"
- ".previous\n"
- _ASM_EXTABLE(ftrace_test_p6nop, 2b)
- _ASM_EXTABLE(ftrace_test_nop5, 3b)
- : "=r"(faulted) : "0" (faulted));
-
- switch (faulted) {
- case 0:
- pr_info("converting mcount calls to 0f 1f 44 00 00\n");
- memcpy(ftrace_nop, ftrace_test_p6nop, MCOUNT_INSN_SIZE);
- break;
- case 1:
- pr_info("converting mcount calls to 66 66 66 66 90\n");
- memcpy(ftrace_nop, ftrace_test_nop5, MCOUNT_INSN_SIZE);
- break;
- case 2:
- pr_info("converting mcount calls to jmp . + 5\n");
- memcpy(ftrace_nop, ftrace_test_jmp, MCOUNT_INSN_SIZE);
- break;
- }
-
- /* The return code is retured via data */
- *(unsigned long *)data = 0;
-
- return 0;
-}
#endif

#ifdef CONFIG_FUNCTION_GRAPH_TRACER
diff --git a/arch/x86/kernel/jump_label.c b/arch/x86/kernel/jump_label.c
index c347431..2db569c 100644
--- a/arch/x86/kernel/jump_label.c
+++ b/arch/x86/kernel/jump_label.c
@@ -30,19 +30,8 @@ void arch_jump_label_transform(struct jump_entry *entry, enum jump_label_type ty
if (type == JUMP_LABEL_ENABLE) {
code.jump = 0xe9;
code.offset = entry->target - (entry->code + JUMP_LABEL_NOP_SIZE);
- } else {
-#ifdef CONFIG_X86_64
- /* opcode for P6_NOP5 */
- code.code[0] = 0x0f;
- code.code[1] = 0x1f;
- code.code[2] = 0x44;
- code.code[3] = 0x00;
- code.code[4] = 0x00;
-#else
- code.jump = 0xe9;
- code.offset = 0;
-#endif
- }
+ } else
+ memcpy(&code, ideal_nop5, JUMP_LABEL_NOP_SIZE);
get_online_cpus();
mutex_lock(&text_mutex);
text_poke_smp((void *)entry->code, &code, JUMP_LABEL_NOP_SIZE);
@@ -50,11 +39,9 @@ void arch_jump_label_transform(struct jump_entry *entry, enum jump_label_type ty
put_online_cpus();
}

-static const u8 jump_label_nop[JUMP_LABEL_NOP_SIZE] = { 0x0f, 0x1f, 0x44, 0x00, 0x00 };
-
const u8 *arch_get_jump_label_nop()
{
- return jump_label_nop;
+ return ideal_nop5;
}

#endif
diff --git a/arch/x86/kernel/module.c b/arch/x86/kernel/module.c
index e0bc186..3d964dc 100644
--- a/arch/x86/kernel/module.c
+++ b/arch/x86/kernel/module.c
@@ -239,6 +239,9 @@ int module_finalize(const Elf_Ehdr *hdr,
apply_paravirt(pseg, pseg + para->sh_size);
}

+ /* make jump label nops */
+ apply_jump_label_nops(me);
+
return module_bug_finalize(hdr, sechdrs, me);
}

diff --git a/arch/x86/kernel/setup.c b/arch/x86/kernel/setup.c
index c4851ef..de0a3e8 100644
--- a/arch/x86/kernel/setup.c
+++ b/arch/x86/kernel/setup.c
@@ -49,6 +49,7 @@
#include <asm/pci-direct.h>
#include <linux/init_ohci1394_dma.h>
#include <linux/kvm_para.h>
+#include <linux/jump_label.h>

#include <linux/errno.h>
#include <linux/kernel.h>
@@ -1051,6 +1052,8 @@ void __init setup_arch(char **cmdline_p)
x86_init.oem.banner();

mcheck_init();
+
+ arch_init_ideal_nop5();
}

#ifdef CONFIG_X86_32
diff --git a/include/linux/jump_label.h b/include/linux/jump_label.h
index ab879ef..7de278d 100644
--- a/include/linux/jump_label.h
+++ b/include/linux/jump_label.h
@@ -13,6 +13,8 @@ enum jump_label_type {
JUMP_LABEL_DISABLE
};

+struct module;
+
#ifdef HAVE_JUMP_LABEL

extern struct jump_entry __start___jump_table[];
@@ -29,6 +31,8 @@ extern const u8 *arch_get_jump_label_nop(void);

extern void jump_label_update(const char *name, enum jump_label_type type);

+extern void apply_jump_label_nops(struct module *mod);
+
#define enable_jump_label(name) \
jump_label_update(name, JUMP_LABEL_ENABLE);

@@ -55,6 +59,11 @@ static inline int disable_jump_label(const char *name)
return 0;
}

+static inline int apply_jump_label_nops(struct module *mod)
+{
+ return 0;
+}
+
#endif

#endif
diff --git a/kernel/jump_label.c b/kernel/jump_label.c
index 0484b7e..1db7a63 100644
--- a/kernel/jump_label.c
+++ b/kernel/jump_label.c
@@ -185,10 +185,19 @@ void jump_label_update(const char *name, enum jump_label_type type)
static int init_jump_label(void)
{
int ret;
+ struct jump_entry *iter_start = __start___jump_table;
+ struct jump_entry *iter_stop = __start___jump_table;
+ struct jump_entry *iter;

mutex_lock(&jump_label_mutex);
ret = build_jump_label_hashtable(__start___jump_table,
__stop___jump_table);
+ iter = iter_start;
+ while (iter < iter_stop) {
+ text_poke_early((void *)iter->code, arch_get_jump_label_nop(),
+ JUMP_LABEL_NOP_SIZE);
+ iter++;
+ }
mutex_unlock(&jump_label_mutex);
return ret;
}
@@ -299,6 +308,30 @@ static int jump_label_module_notify(struct notifier_block *self, unsigned long v
return ret;
}

+/***
+ * apply_jump_label_nops - patch module jump labels with arch_get_jump_label_nop()
+ * @mod: module to patch
+ *
+ * Allow for run-time selection of the optimal nops. Before the module
+ * loads patch these with arch_get_jump_label_nop(), which is specified by
+ * the arch specific jump label code.
+ */
+void apply_jump_label_nops(struct module *mod)
+{
+ struct jump_entry *iter;
+
+ /* if the module doesn't have jump label entries, just return */
+ if (!mod->num_jump_entries)
+ return;
+
+ iter = mod->jump_entries;
+ while (iter < mod->jump_entries + mod->num_jump_entries) {
+ text_poke_early((void *)iter->code, arch_get_jump_label_nop(),
+ JUMP_LABEL_NOP_SIZE);
+ iter++;
+ }
+}
+
struct notifier_block jump_label_module_nb = {
.notifier_call = jump_label_module_notify,
.priority = 0,
diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c
index 2404b59..a7ff98d 100644
--- a/kernel/trace/ftrace.c
+++ b/kernel/trace/ftrace.c
@@ -2728,20 +2728,9 @@ extern unsigned long __stop_mcount_loc[];

void __init ftrace_init(void)
{
- unsigned long count, addr, flags;
+ unsigned long count;
int ret;

- /* Keep the ftrace pointer to the stub */
- addr = (unsigned long)ftrace_stub;
-
- local_irq_save(flags);
- ftrace_dyn_arch_init(&addr);
- local_irq_restore(flags);
-
- /* ftrace_dyn_arch_init places the return code in addr */
- if (addr)
- goto failed;
-
count = __stop_mcount_loc - __start_mcount_loc;

ret = ftrace_dyn_table_alloc(count);
--
1.7.0.1

2010-04-16 15:25:19

by Jason Baron

[permalink] [raw]
Subject: [PATCH 09/11] jump label: jump_label_text_reserved() to reserve our jump points

Add a jump_label_text_reserved(void *start, void *end), so that other
pieces of code that want to modify kernel text, can first verify that
jump label has not reserved the instruction.

Signed-off-by: Jason Baron <[email protected]>
Acked-by: Masami Hiramatsu <[email protected]>
---
arch/x86/kernel/kprobes.c | 3 +-
include/linux/jump_label.h | 6 +++
kernel/jump_label.c | 83 ++++++++++++++++++++++++++++++++++++++++++++
kernel/kprobes.c | 3 +-
4 files changed, 93 insertions(+), 2 deletions(-)

diff --git a/arch/x86/kernel/kprobes.c b/arch/x86/kernel/kprobes.c
index f2f56c0..7a7343b 100644
--- a/arch/x86/kernel/kprobes.c
+++ b/arch/x86/kernel/kprobes.c
@@ -1202,7 +1202,8 @@ static int __kprobes copy_optimized_instructions(u8 *dest, u8 *src)
}
/* Check whether the address range is reserved */
if (ftrace_text_reserved(src, src + len - 1) ||
- alternatives_text_reserved(src, src + len - 1))
+ alternatives_text_reserved(src, src + len - 1) ||
+ jump_label_text_reserved(src, src + len - 1))
return -EBUSY;

return len;
diff --git a/include/linux/jump_label.h b/include/linux/jump_label.h
index 7de278d..2464348 100644
--- a/include/linux/jump_label.h
+++ b/include/linux/jump_label.h
@@ -30,6 +30,7 @@ extern void arch_jump_label_transform(struct jump_entry *entry,
extern const u8 *arch_get_jump_label_nop(void);

extern void jump_label_update(const char *name, enum jump_label_type type);
+extern int jump_label_text_reserved(void *start, void *end);

extern void apply_jump_label_nops(struct module *mod);

@@ -64,6 +65,11 @@ static inline int apply_jump_label_nops(struct module *mod)
return 0;
}

+static inline int jump_label_text_reserved(void *start, void *end)
+{
+ return 0;
+}
+
#endif

#endif
diff --git a/kernel/jump_label.c b/kernel/jump_label.c
index 1db7a63..f44c792 100644
--- a/kernel/jump_label.c
+++ b/kernel/jump_label.c
@@ -182,6 +182,89 @@ void jump_label_update(const char *name, enum jump_label_type type)
mutex_unlock(&jump_label_mutex);
}

+static int addr_conflict(struct jump_entry *entry, void *start, void *end)
+{
+ if (entry->code <= (unsigned long)end &&
+ entry->code + IDEAL_NOP_SIZE_5 > (unsigned long)start)
+ return 1;
+
+ return 0;
+}
+
+#ifdef CONFIG_MODULES
+
+static int module_conflict(void *start, void *end)
+{
+ struct hlist_head *head;
+ struct hlist_node *node, *node_next, *module_node, *module_node_next;
+ struct jump_label_entry *e;
+ struct jump_label_module_entry *e_module;
+ struct jump_entry *iter;
+ int i, count;
+ int conflict = 0;
+
+ for (i = 0; i < JUMP_LABEL_TABLE_SIZE; i++) {
+ head = &jump_label_table[i];
+ hlist_for_each_entry_safe(e, node, node_next, head, hlist) {
+ hlist_for_each_entry_safe(e_module, module_node,
+ module_node_next,
+ &(e->modules), hlist) {
+ count = e_module->nr_entries;
+ iter = e_module->table;
+ while (count--) {
+ if (addr_conflict(iter, start, end)) {
+ conflict = 1;
+ goto out;
+ }
+ iter++;
+ }
+ }
+ }
+ }
+out:
+ return conflict;
+}
+
+#endif
+
+/***
+ * jump_label_text_reserved - check if addr range is reserved
+ * @start: start text addr
+ * @end: end text addr
+ *
+ * checks if the text addr located between @start and @end
+ * overlaps with any of the jump label patch addresses. Code
+ * that wants to modify kernel text should first verify that
+ * it does not overlap with any of the jump label addresses.
+ *
+ * returns 1 if there is an overlap, 0 otherwise
+ */
+int jump_label_text_reserved(void *start, void *end)
+{
+ struct jump_entry *iter;
+ struct jump_entry *iter_start = __start___jump_table;
+ struct jump_entry *iter_stop = __start___jump_table;
+ int conflict = 0;
+
+ mutex_lock(&jump_label_mutex);
+ iter = iter_start;
+ while (iter < iter_stop) {
+ if (addr_conflict(iter, start, end)) {
+ conflict = 1;
+ goto out;
+ }
+ iter++;
+ }
+
+ /* now check modules */
+#ifdef CONFIG_MODULES
+ conflict = module_conflict(start, end);
+#endif
+out:
+ mutex_unlock(&jump_label_mutex);
+ return conflict;
+}
+
static int init_jump_label(void)
{
int ret;
diff --git a/kernel/kprobes.c b/kernel/kprobes.c
index 0ed46f3..e866e04 100644
--- a/kernel/kprobes.c
+++ b/kernel/kprobes.c
@@ -1141,7 +1141,8 @@ int __kprobes register_kprobe(struct kprobe *p)
preempt_disable();
if (!kernel_text_address((unsigned long) p->addr) ||
in_kprobes_functions((unsigned long) p->addr) ||
- ftrace_text_reserved(p->addr, p->addr)) {
+ ftrace_text_reserved(p->addr, p->addr) ||
+ jump_label_text_reserved(p->addr, p->addr)) {
preempt_enable();
return -EINVAL;
}
--
1.7.0.1

2010-04-16 15:25:32

by Jason Baron

[permalink] [raw]
Subject: [PATCH 10/11] sparc64: Add jump_label support

From: David S. Miller <[email protected]>

Add jump label support for sparc64.

Signed-off-by: David S. Miller <[email protected]>
Signed-off-by: Jason Baron <[email protected]>
---
arch/sparc/Kconfig | 1 +
arch/sparc/include/asm/jump_entry.h | 16 ++++++++++++++
arch/sparc/include/asm/jump_label.h | 21 +++++++++++++++++++
arch/sparc/kernel/Makefile | 2 +
arch/sparc/kernel/jump_label.c | 38 +++++++++++++++++++++++++++++++++++
arch/sparc/kernel/module.c | 6 +++++
6 files changed, 84 insertions(+), 0 deletions(-)
create mode 100644 arch/sparc/include/asm/jump_entry.h
create mode 100644 arch/sparc/include/asm/jump_label.h
create mode 100644 arch/sparc/kernel/jump_label.c

diff --git a/arch/sparc/Kconfig b/arch/sparc/Kconfig
index d6781ce..0c581d4 100644
--- a/arch/sparc/Kconfig
+++ b/arch/sparc/Kconfig
@@ -29,6 +29,7 @@ config SPARC
select PERF_USE_VMALLOC
select HAVE_DMA_ATTRS
select HAVE_DMA_API_DEBUG
+ select HAVE_ARCH_JUMP_LABEL

config SPARC32
def_bool !64BIT
diff --git a/arch/sparc/include/asm/jump_entry.h b/arch/sparc/include/asm/jump_entry.h
new file mode 100644
index 0000000..6bb85e4
--- /dev/null
+++ b/arch/sparc/include/asm/jump_entry.h
@@ -0,0 +1,16 @@
+#ifndef _LINUX_JUMP_ENTRY_H
+#define _LINUX_JUMP_ENTRY_H
+
+/* Defined in a separate file, so that it can be used by user program modpost.c
+ * This file is included by include/asm/jump_label.h, in turn included by
+ * include/linux/jump_label.h. Kernel sources should only include
+ * include/linux/jump_label.h.
+ **/
+
+struct jump_entry {
+ __u32 code;
+ __u32 target;
+ __u32 name;
+};
+
+#endif
diff --git a/arch/sparc/include/asm/jump_label.h b/arch/sparc/include/asm/jump_label.h
new file mode 100644
index 0000000..32d2c2f
--- /dev/null
+++ b/arch/sparc/include/asm/jump_label.h
@@ -0,0 +1,21 @@
+#ifndef _ASM_SPARC_JUMP_LABEL_H
+#define _ASM_SPARC_JUMP_LABEL_H
+
+#include <linux/types.h>
+#include <asm/jump_entry.h>
+
+#define JUMP_LABEL_NOP_SIZE 4
+
+#define JUMP_LABEL(tag, label, cond) \
+ do { \
+ extern const char __jlstrtab_##tag[]; \
+ asm goto("1:\n\t" \
+ "nop\n\t" \
+ "nop\n\t" \
+ ".pushsection __jump_table, \"a\"\n\t"\
+ ".xword 1b, %l[" #label "], %c0\n\t" \
+ ".popsection \n\t" \
+ : : "i" (__jlstrtab_##tag) : : label);\
+ } while (0)
+
+#endif
diff --git a/arch/sparc/kernel/Makefile b/arch/sparc/kernel/Makefile
index 0c2dc1f..599398f 100644
--- a/arch/sparc/kernel/Makefile
+++ b/arch/sparc/kernel/Makefile
@@ -119,3 +119,5 @@ obj-$(CONFIG_COMPAT) += $(audit--y)

pc--$(CONFIG_PERF_EVENTS) := perf_event.o
obj-$(CONFIG_SPARC64) += $(pc--y)
+
+obj-$(CONFIG_SPARC64) += jump_label.o
diff --git a/arch/sparc/kernel/jump_label.c b/arch/sparc/kernel/jump_label.c
new file mode 100644
index 0000000..c1aa2d8
--- /dev/null
+++ b/arch/sparc/kernel/jump_label.c
@@ -0,0 +1,38 @@
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/mutex.h>
+#include <linux/cpu.h>
+
+#include <linux/jump_label.h>
+#include <linux/memory.h>
+
+#ifdef HAVE_JUMP_LABEL
+
+void arch_jump_label_transform(struct jump_entry *entry, enum jump_label_type type)
+{
+ u32 val, *insn = (u32 *) entry->code;
+
+ val = *insn;
+ if (type == JUMP_LABEL_ENABLE) {
+ s32 off = (s32)entry->target - (s32)entry->code;
+ val = 0x40000000 | ((u32) off >> 2);
+ } else {
+ val = 0x01000000;
+ }
+
+ get_online_cpus();
+ mutex_lock(&text_mutex);
+ *insn = val;
+ flushi(insn);
+ mutex_unlock(&text_mutex);
+ put_online_cpus();
+}
+
+static const u8 jump_label_nop[JUMP_LABEL_NOP_SIZE] = { 0x01, 0x00, 0x00, 0x00 };
+
+const u8 *arch_get_jump_label_nop()
+{
+ return jump_label_nop;
+}
+
+#endif
diff --git a/arch/sparc/kernel/module.c b/arch/sparc/kernel/module.c
index f848aad..37cf439 100644
--- a/arch/sparc/kernel/module.c
+++ b/arch/sparc/kernel/module.c
@@ -18,6 +18,9 @@
#include <asm/spitfire.h>

#ifdef CONFIG_SPARC64
+
+#include <linux/jump_label.h>
+
static void *module_map(unsigned long size)
{
struct vm_struct *area;
@@ -227,6 +230,9 @@ int module_finalize(const Elf_Ehdr *hdr,
const Elf_Shdr *sechdrs,
struct module *me)
{
+ /* make jump label nops */
+ apply_jump_label_nops(me);
+
/* Cheetah's I-cache is fully coherent. */
if (tlb_type == spitfire) {
unsigned long va;
--
1.7.0.1

2010-04-16 15:25:25

by Jason Baron

[permalink] [raw]
Subject: [PATCH 08/11] jump label: initialize workqueue tracepoints *before* they are registered

Initialize the workqueue data structures *before* they are registered
so that they are ready for callbacks.

Signed-off-by: Jason Baron <[email protected]>
---
kernel/trace/trace_workqueue.c | 10 +++++-----
1 files changed, 5 insertions(+), 5 deletions(-)

diff --git a/kernel/trace/trace_workqueue.c b/kernel/trace/trace_workqueue.c
index cc2d2fa..7705ad4 100644
--- a/kernel/trace/trace_workqueue.c
+++ b/kernel/trace/trace_workqueue.c
@@ -259,6 +259,11 @@ int __init trace_workqueue_early_init(void)
{
int ret, cpu;

+ for_each_possible_cpu(cpu) {
+ spin_lock_init(&workqueue_cpu_stat(cpu)->lock);
+ INIT_LIST_HEAD(&workqueue_cpu_stat(cpu)->list);
+ }
+
ret = register_trace_workqueue_insertion(probe_workqueue_insertion);
if (ret)
goto out;
@@ -275,11 +280,6 @@ int __init trace_workqueue_early_init(void)
if (ret)
goto no_creation;

- for_each_possible_cpu(cpu) {
- spin_lock_init(&workqueue_cpu_stat(cpu)->lock);
- INIT_LIST_HEAD(&workqueue_cpu_stat(cpu)->list);
- }
-
return 0;

no_creation:
--
1.7.0.1

2010-04-16 15:25:46

by Jason Baron

[permalink] [raw]
Subject: [PATCH 11/11] jump label: add docs

Add jump label docs as: Documentation/jump-label.txt

Signed-off-by: Jason Baron <[email protected]>
---
Documentation/jump-label.txt | 140 ++++++++++++++++++++++++++++++++++++++++++
1 files changed, 140 insertions(+), 0 deletions(-)
create mode 100644 Documentation/jump-label.txt

diff --git a/Documentation/jump-label.txt b/Documentation/jump-label.txt
new file mode 100644
index 0000000..4614821
--- /dev/null
+++ b/Documentation/jump-label.txt
@@ -0,0 +1,140 @@
+ Jump Label
+ ----------
+
+By: Jason Baron <[email protected]>
+
+
+1) motivation
+
+
+Currently, tracepoints are implemented using a conditional. The conditional
+check requires checking a global variable for each tracepoint. Although,
+the overhead of this check is small, it increases under memory pressure. As we
+increase the number of tracepoints in the kernel this may become more of an
+issue. In addition, tracepoints are often dormant (disabled), and provide no
+direct kernel functionality. Thus, it is highly desirable to reduce their
+impact as much as possible. Although tracepoints are the original motivation
+for this work, other kernel code paths should be able to make use of the jump
+label optimization.
+
+
+2) jump label description/usage
+
+
+gcc (v4.5) adds a new 'asm goto' statement that allows branching to a label.
+http://gcc.gnu.org/ml/gcc-patches/2009-07/msg01556.html
+
+Thus, this patch set introduces an architecture specific 'JUMP_LABEL()' macro as
+follows (x86):
+
+# define JUMP_LABEL_INITIAL_NOP ".byte 0xe9 \n\t .long 0\n\t"
+
+# define JUMP_LABEL(tag, label, cond) \
+ do { \
+ extern const char __jlstrtab_##tag[]; \
+ asm goto("1:" \
+ JUMP_LABEL_INITIAL_NOP
+ ".pushsection __jump_table, \"a\" \n\t"\
+ _ASM_PTR "1b, %l[" #label "], %c0 \n\t" \
+ ".popsection \n\t" \
+ : : "i" (__jlstrtab_##tag) : : label);\
+ } while (0)
+
+
+For architectures that have not yet introduced jump label support its simply:
+
+#define JUMP_LABEL(tag, label, cond) \
+ if (unlikely(cond)) \
+ goto label;
+
+which then can be used as:
+
+ ....
+ JUMP_LABEL(trace_name, trace_label, jump_enabled);
+ printk("not doing tracing\n");
+ return;
+trace_label:
+ printk("doing tracing: %d\n", file);
+ ....
+
+Thus, when tracing is disabled, we simply have a no-op followed by a jump around
+the dormant (disabled) tracing code. The 'JUMP_LABEL()' macro, produces a
+'jump_table' which has the following format:
+
+[instruction address] [jump target] [tracepoint name]
+
+Thus, to enable a tracepoint, we simply patch the 'instruction address' with
+a jump to the 'jump target'.
+
+The call to enable a jump label is: enable_jump_label(trace_name); to disable:
+disable_jump_label(trace_name);
+
+
+3) Jump label analysis (x86)
+
+
+I've tested the performance of using 'get_cycles()' calls around the
+tracepoint call sites. For an Intel Core 2 Quad cpu (in cycles, averages):
+
+ idle after tbench run
+ ---- ----------------
+old code 32 88
+new code 2 4
+
+
+The performance improvement can be reproduced reliably on both Intel and AMD
+hardware.
+
+In terms of code analysis the current code for the disabled case is a 'cmpl'
+followed by a 'je' around the tracepoint code. so:
+
+cmpl - 83 3d 0e 77 87 00 00 - 7 bytes
+je - 74 3e - 2 bytes
+
+total of 9 instruction bytes.
+
+The new code is a 'nopl' followed by a 'jmp'. Thus:
+
+nopl - 0f 1f 44 00 00 - 5 bytes
+jmp - eb 3e - 2 bytes
+
+total of 7 instruction bytes.
+
+So, the new code also accounts for 2 less bytes in the instruction cache per tracepoint.
+
+
+4) architecture interface
+
+
+There are a few functions and macros which arches must implement in order to
+take advantage of this optimization. As previously mentioned, if there is no
+architecture support we simply fall back to a traditional, load, test, and
+jump sequence.
+
+* add "HAVE_ARCH_JUMP_LABEL" to arch/<arch>/Kconfig to indicate support
+
+* #define JUMP_LABEL_NOP_SIZE, arch/x86/include/asm/jump_label.h
+
+* #define "JUMP_LABEL(tag, label, cond)", arch/x86/include/asm/jump_label.h
+
+* add: void arch_jump_label_transform(struct jump_entry *entry, enum jump_label_type type)
+ and
+ const u8 *arch_get_jump_label_nop(void)
+
+ see: arch/x86/kernel/jump_label.c
+
+* finally add a definition for "struct jump_entry". This must be done in a
+ separate .h file, b/c the modpost.c code uses this definition to sort the
+ the jump label tabel in the vmlinux, so that it does not have to be sorted at
+ runtime. see: arch/x86/include/asm/jump_entry.h
+
+
+5) Acknowledgments
+
+
+Thanks to Roland McGrath and Richard Henderson for coming up with the initial
+'asm goto' and jump label design.
+
+Thanks to Mathieu Desnoyers for calling attention to this issue, outlining the
+requirements of a solution, and implementing a solution in the form of the
+"Immediate Values" work.
--
1.7.0.1

2010-04-16 15:25:07

by Jason Baron

[permalink] [raw]
Subject: [PATCH 04/11] jump label: tracepoint support

Make use of the jump label infrastructure for tracepoints.

Signed-off-by: Jason Baron <[email protected]>
---
include/linux/tracepoint.h | 34 +++++++++++++++++++---------------
kernel/tracepoint.c | 8 ++++++++
2 files changed, 27 insertions(+), 15 deletions(-)

diff --git a/include/linux/tracepoint.h b/include/linux/tracepoint.h
index 78b4bd3..003ac28 100644
--- a/include/linux/tracepoint.h
+++ b/include/linux/tracepoint.h
@@ -16,6 +16,7 @@

#include <linux/types.h>
#include <linux/rcupdate.h>
+#include <linux/jump_label.h>

struct module;
struct tracepoint;
@@ -63,20 +64,22 @@ struct tracepoint {
* not add unwanted padding between the beginning of the section and the
* structure. Force alignment to the same alignment as the section start.
*/
-#define DECLARE_TRACE(name, proto, args) \
- extern struct tracepoint __tracepoint_##name; \
- static inline void trace_##name(proto) \
- { \
- if (unlikely(__tracepoint_##name.state)) \
- __DO_TRACE(&__tracepoint_##name, \
- TP_PROTO(proto), TP_ARGS(args)); \
- } \
- static inline int register_trace_##name(void (*probe)(proto)) \
- { \
- return tracepoint_probe_register(#name, (void *)probe); \
- } \
- static inline int unregister_trace_##name(void (*probe)(proto)) \
- { \
+#define DECLARE_TRACE(name, proto, args) \
+ extern struct tracepoint __tracepoint_##name; \
+ static inline void trace_##name(proto) \
+ { \
+ JUMP_LABEL(name, do_trace, __tracepoint_##name.state); \
+ return; \
+do_trace: \
+ __DO_TRACE(&__tracepoint_##name, \
+ TP_PROTO(proto), TP_ARGS(args)); \
+ } \
+ static inline int register_trace_##name(void (*probe)(proto)) \
+ { \
+ return tracepoint_probe_register(#name, (void *)probe); \
+ } \
+ static inline int unregister_trace_##name(void (*probe)(proto)) \
+ { \
return tracepoint_probe_unregister(#name, (void *)probe);\
}

@@ -86,7 +89,8 @@ struct tracepoint {
__attribute__((section("__tracepoints_strings"))) = #name; \
struct tracepoint __tracepoint_##name \
__attribute__((section("__tracepoints"), aligned(32))) = \
- { __tpstrtab_##name, 0, reg, unreg, NULL }
+ { __tpstrtab_##name, 0, reg, unreg, NULL }; \
+ DEFINE_JUMP_LABEL(name);

#define DEFINE_TRACE(name) \
DEFINE_TRACE_FN(name, NULL, NULL);
diff --git a/kernel/tracepoint.c b/kernel/tracepoint.c
index cc89be5..8acced8 100644
--- a/kernel/tracepoint.c
+++ b/kernel/tracepoint.c
@@ -25,6 +25,7 @@
#include <linux/err.h>
#include <linux/slab.h>
#include <linux/sched.h>
+#include <linux/jump_label.h>

extern struct tracepoint __start___tracepoints[];
extern struct tracepoint __stop___tracepoints[];
@@ -256,6 +257,10 @@ static void set_tracepoint(struct tracepoint_entry **entry,
* is used.
*/
rcu_assign_pointer(elem->funcs, (*entry)->funcs);
+ if (!elem->state && active)
+ enable_jump_label(elem->name);
+ if (elem->state && !active)
+ disable_jump_label(elem->name);
elem->state = active;
}

@@ -270,6 +275,9 @@ static void disable_tracepoint(struct tracepoint *elem)
if (elem->unregfunc && elem->state)
elem->unregfunc();

+ if (elem->state)
+ disable_jump_label(elem->name);
+
elem->state = 0;
rcu_assign_pointer(elem->funcs, NULL);
}
--
1.7.0.1

2010-04-16 15:26:38

by Jason Baron

[permalink] [raw]
Subject: [PATCH 07/11] jump label: sort jump table at build-time

The jump label table is more optimal accessed if the entries are continguous.
Sorting the table accomplishes this. Do the sort at build-time. Adds a '-j'
option to 'modpost' which replaces the vmlinux, with a sorted jump label
section vmlinux. I've tested this on x86 with relocatable and it works fine
there as well. Note that I have not sorted the jump label table in modules.
This is b/c the jump label names can be exported by the core kernel, and thus
I don't have them available at buildtime. This could be solved by either
finding the correct ones in the vmlinux, or by embedding the name of the jump
label in the module tables (and not just a pointer), but the module tables
tend to be smaller, and thus their is less value to this kind of change
anyway. The kernel continues to do the sort, just in case, but at least for
the vmlinux, this is just a verfication that the jump label table has
already been sorted.

Signed-off-by: Jason Baron <[email protected]>
---
Makefile | 6 +++-
include/asm-generic/vmlinux.lds.h | 18 ++++++++--
scripts/mod/Makefile | 1 +
scripts/mod/modpost.c | 71 +++++++++++++++++++++++++++++++++++--
scripts/mod/modpost.h | 2 +
5 files changed, 91 insertions(+), 7 deletions(-)

diff --git a/Makefile b/Makefile
index 35bffde..0cbad55 100644
--- a/Makefile
+++ b/Makefile
@@ -152,7 +152,7 @@ obj := $(objtree)

VPATH := $(srctree)$(if $(KBUILD_EXTMOD),:$(KBUILD_EXTMOD))

-export srctree objtree VPATH
+export srctree objtree VPATH hdr-arch


# SUBARCH tells the usermode build what the underlying arch is. That is set
@@ -845,6 +845,9 @@ define rule_vmlinux-modpost
$(Q)echo 'cmd_$@ := $(cmd_vmlinux-modpost)' > $(dot-target).cmd
endef

+quiet_cmd_sort-jump-label = SORT $@
+ cmd_sort-jump-label = $(srctree)/scripts/mod/modpost -j -s $@
+
# vmlinux image - including updated kernel symbols
vmlinux: $(vmlinux-lds) $(vmlinux-init) $(vmlinux-main) vmlinux.o $(kallsyms.o) FORCE
ifdef CONFIG_HEADERS_CHECK
@@ -858,6 +861,7 @@ ifdef CONFIG_BUILD_DOCSRC
endif
$(call vmlinux-modpost)
$(call if_changed_rule,vmlinux__)
+ $(call cmd,sort-jump-label)
$(Q)rm -f .old_version

# build vmlinux.o first to catch section mismatch errors early
diff --git a/include/asm-generic/vmlinux.lds.h b/include/asm-generic/vmlinux.lds.h
index 83a469d..f9d8188 100644
--- a/include/asm-generic/vmlinux.lds.h
+++ b/include/asm-generic/vmlinux.lds.h
@@ -168,7 +168,6 @@
TRACE_PRINTKS() \
FTRACE_EVENTS() \
TRACE_SYSCALLS() \
- JUMP_TABLE() \

/*
* Data section helpers
@@ -207,7 +206,6 @@
*(__vermagic) /* Kernel version magic */ \
*(__markers_strings) /* Markers: strings */ \
*(__tracepoints_strings)/* Tracepoints: strings */ \
- *(__jump_strings)/* Jump: strings */ \
} \
\
.rodata1 : AT(ADDR(.rodata1) - LOAD_OFFSET) { \
@@ -216,6 +214,10 @@
\
BUG_TABLE \
\
+ JUMP_TABLE \
+ \
+ JUMP_STRINGS \
+ \
/* PCI quirks */ \
.pci_fixup : AT(ADDR(.pci_fixup) - LOAD_OFFSET) { \
VMLINUX_SYMBOL(__start_pci_fixups_early) = .; \
@@ -559,11 +561,21 @@
#define BUG_TABLE
#endif

-#define JUMP_TABLE() \
+#define JUMP_TABLE \
. = ALIGN(64); \
+ __jump_table : AT(ADDR(__jump_table) - LOAD_OFFSET) { \
VMLINUX_SYMBOL(__start___jump_table) = .; \
*(__jump_table) \
VMLINUX_SYMBOL(__stop___jump_table) = .; \
+ }
+
+#define JUMP_STRINGS \
+ . = ALIGN(64); \
+ __jump_strings : AT(ADDR(__jump_strings) - LOAD_OFFSET) { \
+ VMLINUX_SYMBOL(__start___jump_strings) = .; \
+ *(__jump_strings) \
+ VMLINUX_SYMBOL(__stop___jump_strings) = .; \
+ }

#ifdef CONFIG_PM_TRACE
#define TRACEDATA \
diff --git a/scripts/mod/Makefile b/scripts/mod/Makefile
index ff954f8..80d7e18 100644
--- a/scripts/mod/Makefile
+++ b/scripts/mod/Makefile
@@ -1,4 +1,5 @@
hostprogs-y := modpost mk_elfconfig
+HOST_EXTRACFLAGS += -I$(srctree)/arch/$(hdr-arch)/include
always := $(hostprogs-y) empty.o

modpost-objs := modpost.o file2alias.o sumversion.o
diff --git a/scripts/mod/modpost.c b/scripts/mod/modpost.c
index 2092361..3966fa8 100644
--- a/scripts/mod/modpost.c
+++ b/scripts/mod/modpost.c
@@ -17,6 +17,8 @@
#include "modpost.h"
#include "../../include/generated/autoconf.h"
#include "../../include/linux/license.h"
+#include <linux/types.h>
+#include <asm/jump_entry.h>

/* Some toolchains use a `_' prefix for all user symbols. */
#ifdef CONFIG_SYMBOL_PREFIX
@@ -41,6 +43,8 @@ static int warn_unresolved = 0;
/* How a symbol is exported */
static int sec_mismatch_count = 0;
static int sec_mismatch_verbose = 1;
+/* jump label */
+static int enable_jump_label = 0;

enum export {
export_plain, export_unused, export_gpl,
@@ -315,12 +319,18 @@ void *grab_file(const char *filename, unsigned long *size)
void *map;
int fd;

- fd = open(filename, O_RDONLY);
+ if (!enable_jump_label)
+ fd = open(filename, O_RDONLY);
+ else
+ fd = open(filename, O_RDWR);
if (fd < 0 || fstat(fd, &st) != 0)
return NULL;

*size = st.st_size;
- map = mmap(NULL, *size, PROT_READ|PROT_WRITE, MAP_PRIVATE, fd, 0);
+ if (!enable_jump_label)
+ map = mmap(NULL, *size, PROT_READ|PROT_WRITE, MAP_PRIVATE, fd, 0);
+ else
+ map = mmap(NULL, *size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
close(fd);

if (map == MAP_FAILED)
@@ -367,6 +377,50 @@ void release_file(void *file, unsigned long size)
munmap(file, size);
}

+static void swap_jump_label_entries(struct jump_entry *previous, struct jump_entry *next)
+{
+ struct jump_entry tmp;
+
+ tmp = *next;
+ *next = *previous;
+ *previous = tmp;
+}
+
+static void sort_jump_label_table(struct elf_info *info, Elf_Ehdr *hdr)
+{
+ int swapped = 0;
+ struct jump_entry *iter, *iter_next;
+ char *name, *next_name;
+ Elf_Shdr *sechdrs = info->sechdrs;
+ unsigned long jump_table, jump_table_end;
+ unsigned long jump_strings, jump_strings_addr;
+
+ if ((info->jump_sec == 0) && (info->jump_strings_sec == 0))
+ return;
+
+ jump_table = (unsigned long)hdr + sechdrs[info->jump_sec].sh_offset;
+ jump_table_end = jump_table + sechdrs[info->jump_sec].sh_size;
+ jump_strings = (unsigned long)hdr +
+ sechdrs[info->jump_strings_sec].sh_offset;
+ jump_strings_addr = sechdrs[info->jump_strings_sec].sh_addr;
+
+ do {
+ swapped = 0;
+ iter = iter_next = (struct jump_entry *)jump_table;
+ iter_next++;
+ for (; iter_next < (struct jump_entry *)jump_table_end;
+ iter++, iter_next++) {
+ name = (char *)(jump_strings + (iter->name - jump_strings_addr));
+ next_name = (char *)(jump_strings +
+ (iter_next->name - jump_strings_addr));
+ if (strcmp(name, next_name) > 0) {
+ swap_jump_label_entries(iter, iter_next);
+ swapped = 1;
+ }
+ }
+ } while (swapped == 1);
+}
+
static int parse_elf(struct elf_info *info, const char *filename)
{
unsigned int i;
@@ -460,6 +514,10 @@ static int parse_elf(struct elf_info *info, const char *filename)
info->export_unused_gpl_sec = i;
else if (strcmp(secname, "__ksymtab_gpl_future") == 0)
info->export_gpl_future_sec = i;
+ else if (strcmp(secname, "__jump_table") == 0)
+ info->jump_sec = i;
+ else if (strcmp(secname, "__jump_strings") == 0)
+ info->jump_strings_sec = i;

if (sechdrs[i].sh_type != SHT_SYMTAB)
continue;
@@ -480,6 +538,10 @@ static int parse_elf(struct elf_info *info, const char *filename)
sym->st_value = TO_NATIVE(sym->st_value);
sym->st_size = TO_NATIVE(sym->st_size);
}
+
+ if (enable_jump_label)
+ sort_jump_label_table(info, hdr);
+
return 1;
}

@@ -1941,7 +2003,7 @@ int main(int argc, char **argv)
struct ext_sym_list *extsym_iter;
struct ext_sym_list *extsym_start = NULL;

- while ((opt = getopt(argc, argv, "i:I:e:cmsSo:awM:K:")) != -1) {
+ while ((opt = getopt(argc, argv, "i:I:e:cmsSo:awM:K:j")) != -1) {
switch (opt) {
case 'i':
kernel_read = optarg;
@@ -1979,6 +2041,9 @@ int main(int argc, char **argv)
case 'w':
warn_unresolved = 1;
break;
+ case 'j':
+ enable_jump_label = 1;
+ break;
default:
exit(1);
}
diff --git a/scripts/mod/modpost.h b/scripts/mod/modpost.h
index be987a4..0875d4b 100644
--- a/scripts/mod/modpost.h
+++ b/scripts/mod/modpost.h
@@ -126,6 +126,8 @@ struct elf_info {
Elf_Section export_gpl_sec;
Elf_Section export_unused_gpl_sec;
Elf_Section export_gpl_future_sec;
+ Elf_Section jump_sec;
+ Elf_Section jump_strings_sec;
const char *strtab;
char *modinfo;
unsigned int modinfo_len;
--
1.7.0.1

2010-04-16 15:24:45

by Jason Baron

[permalink] [raw]
Subject: [PATCH 01/11] jump label: notifier atomic call chain notrace

From: Mathieu Desnoyers <[email protected]>

In LTTng, being able to use the atomic notifier from cpu idle entry to
ensure the tracer flush the last events in the current subbuffer
requires the rcu read-side to be marked "notrace", otherwise it can end
up calling back into lockdep and the tracer.

Also apply to the the die notifier.

Signed-off-by: Mathieu Desnoyers <[email protected]>
Signed-off-by: Jason Baron <[email protected]>
Reviewed-by: Masami Hiramatsu <[email protected]>
---
kernel/notifier.c | 6 +++---
1 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/kernel/notifier.c b/kernel/notifier.c
index 2488ba7..88453a7 100644
--- a/kernel/notifier.c
+++ b/kernel/notifier.c
@@ -148,7 +148,7 @@ int atomic_notifier_chain_unregister(struct atomic_notifier_head *nh,
spin_lock_irqsave(&nh->lock, flags);
ret = notifier_chain_unregister(&nh->head, n);
spin_unlock_irqrestore(&nh->lock, flags);
- synchronize_rcu();
+ synchronize_sched();
return ret;
}
EXPORT_SYMBOL_GPL(atomic_notifier_chain_unregister);
@@ -178,9 +178,9 @@ int __kprobes __atomic_notifier_call_chain(struct atomic_notifier_head *nh,
{
int ret;

- rcu_read_lock();
+ rcu_read_lock_sched_notrace();
ret = notifier_call_chain(&nh->head, val, v, nr_to_call, nr_calls);
- rcu_read_unlock();
+ rcu_read_unlock_sched_notrace();
return ret;
}
EXPORT_SYMBOL_GPL(__atomic_notifier_call_chain);
--
1.7.0.1

2010-04-22 23:16:59

by David Miller

[permalink] [raw]
Subject: Re: [PATCH 00/11] jump label v7

From: Jason Baron <[email protected]>
Date: Fri, 16 Apr 2010 11:24:10 -0400

> David, I re-worked the sparc64 to match the updated interfaces. The
> code should hopefully compile now, although I did not test the sparc
> bits.

Thanks for working on this Jason. I'll take a close look at this
some time today.

2010-04-28 00:58:55

by David Miller

[permalink] [raw]
Subject: Re: [PATCH 00/11] jump label v7

From: David Miller <[email protected]>
Date: Thu, 22 Apr 2010 16:17:00 -0700 (PDT)

> From: Jason Baron <[email protected]>
> Date: Fri, 16 Apr 2010 11:24:10 -0400
>
>> David, I re-worked the sparc64 to match the updated interfaces. The
>> code should hopefully compile now, although I did not test the sparc
>> bits.
>
> Thanks for working on this Jason. I'll take a close look at this
> some time today.

The kernel stops compiling after the second patch because
kernel/jump_label.c is compiled unconditionally, and this generates an
attempt to include asm/alternatives.h which is an x86-only phenomenon.

Do you have access to a cross-compile environment or at least some
non-x86 system you can test build on before submitting these patch
sets?

Thanks.

2010-04-28 01:39:55

by Tony Breeds

[permalink] [raw]
Subject: Re: [PATCH 00/11] jump label v7

On Tue, Apr 27, 2010 at 05:58:58PM -0700, David Miller wrote:

> The kernel stops compiling after the second patch because
> kernel/jump_label.c is compiled unconditionally, and this generates an
> attempt to include asm/alternatives.h which is an x86-only phenomenon.
>
> Do you have access to a cross-compile environment or at least some
> non-x86 system you can test build on before submitting these patch
> sets?

I heard cross-compilers?

http://kernel.org/pub/tools/crosstool/files/bin/ i686 and x86_64 and 4.4.0
compilers suitable for kernel work.

I plan to build 4.4.$latest and 4.5.0 ASAP.

Yours Tony

2010-04-28 01:50:40

by Mathieu Desnoyers

[permalink] [raw]
Subject: Re: [PATCH 00/11] jump label v7

* Tony Breeds ([email protected]) wrote:
> On Tue, Apr 27, 2010 at 05:58:58PM -0700, David Miller wrote:
>
> > The kernel stops compiling after the second patch because
> > kernel/jump_label.c is compiled unconditionally, and this generates an
> > attempt to include asm/alternatives.h which is an x86-only phenomenon.
> >
> > Do you have access to a cross-compile environment or at least some
> > non-x86 system you can test build on before submitting these patch
> > sets?
>
> I heard cross-compilers?
>
> http://kernel.org/pub/tools/crosstool/files/bin/ i686 and x86_64 and 4.4.0
> compilers suitable for kernel work.
>
> I plan to build 4.4.$latest and 4.5.0 ASAP.
>
> Yours Tony

The crosstool package from Dan Kegel did a good job for this. Not sure
it's currently maintained though. It was a very useful project, it's a
shame if it does not live on. It would be good to have up-to-date and
tested compilers for various architectures available on kernel.org,
ideally with access to a package that helps building compilers for
various architectures.

Thanks,

Mathieu


--
Mathieu Desnoyers
Operating System Efficiency R&D Consultant
EfficiOS Inc.
http://www.efficios.com

2010-04-28 02:08:23

by Tony Breeds

[permalink] [raw]
Subject: Re: [PATCH 00/11] jump label v7

On Tue, Apr 27, 2010 at 09:49:41PM -0400, Mathieu Desnoyers wrote:

> The crosstool package from Dan Kegel did a good job for this. Not sure
> it's currently maintained though. It was a very useful project, it's a
> shame if it does not live on. It would be good to have up-to-date and
> tested compilers for various architectures available on kernel.org,
> ideally with access to a package that helps building compilers for
> various architectures.

Trimmed the CC' as this is a bit of a tangent.

Crosstool did a good job but seems to be less current than we'd like for the
kernel[1]. When using crosstool in the past I found a lot of the complexity
was in *libc to the toolcahins I've built don';t have a libc so they're only
helpful for kernel (or similar) builds.

In terms of "testing", I build a kernel for each toolchain but I don't boot
them and in the past I think blackfin said that what I had wouldn't actually be
runable. Last time I looked the wasn't a non-intel cross-toolchain for blackfin.

I shoudl also mention that a subset of these compilers are used to build test
linux-next daily.

Yours Tony

[1] http://www.kegel.com/crosstool/crosstool-0.43/buildlogs/

2010-04-28 02:13:39

by Mike Frysinger

[permalink] [raw]
Subject: Re: [PATCH 00/11] jump label v7

On Tue, Apr 27, 2010 at 22:08, Tony Breeds wrote:
> In terms of "testing", I build a kernel for each toolchain but I don't boot
> them and in the past I think blackfin said that what I had wouldn't actually be
> runable.

every Blackfin defconfig should be buildable & bootable

> Last time I looked the wasn't a non-intel cross-toolchain for blackfin.

we provide 32bit and 64bit binaries for people, and a simple script to
build up from source. if you're looking for some other arch, you need
only ask. it's easy for me to ppc/ppc64/ia64 (and ive done them in
the past), but no one has asked for those in a while.
-mike

2010-04-28 14:14:12

by Mathieu Desnoyers

[permalink] [raw]
Subject: Re: [PATCH 00/11] jump label v7

* Tony Breeds ([email protected]) wrote:
> On Tue, Apr 27, 2010 at 09:49:41PM -0400, Mathieu Desnoyers wrote:
>
> > The crosstool package from Dan Kegel did a good job for this. Not sure
> > it's currently maintained though. It was a very useful project, it's a
> > shame if it does not live on. It would be good to have up-to-date and
> > tested compilers for various architectures available on kernel.org,
> > ideally with access to a package that helps building compilers for
> > various architectures.
>
> Trimmed the CC' as this is a bit of a tangent.
>
> Crosstool did a good job but seems to be less current than we'd like for the
> kernel[1].

Ouch. The build failure ratio is surprisingly high.

> When using crosstool in the past I found a lot of the complexity
> was in *libc to the toolcahins I've built don';t have a libc so they're only
> helpful for kernel (or similar) builds.

So maybe we could do something more specialized for kernel needs, e.g. a
kcrosstool ? It could be a "simplified" crosstool.

Personally, I built a set of cross-compilers with crosstool 0.43 a while
ago, but must now do a lot of trial and error to find new correct compiler
and toolchain versions for each architecture. It would be good to have a
kcrosstool git repository that would:

- Have scripts and documentation specialized for Linux kernel build.
- Contain an updated list of "working" and "non-working" toolchain
versions for each arch.
- Could be easily updated; people would just have to report
success/failure of their config so these could be added to the git
repository. The build could probably prepare a report automatically to
lessen the reporter job (I'm even thinking about a scheme that would
give the option to report automatically).
- Optionally contain documentation on how to set up a cron job to
automatically fetch a git tree and do a distributed multi-architecture
build each day.

I think have this kind of updated tool would be of great value for
testing.

Thanks,

Mathieu

>
> In terms of "testing", I build a kernel for each toolchain but I don't boot
> them and in the past I think blackfin said that what I had wouldn't actually be
> runable. Last time I looked the wasn't a non-intel cross-toolchain for blackfin.
>
> I shoudl also mention that a subset of these compilers are used to build test
> linux-next daily.
>
> Yours Tony
>
> [1] http://www.kegel.com/crosstool/crosstool-0.43/buildlogs/

--
Mathieu Desnoyers
Operating System Efficiency R&D Consultant
EfficiOS Inc.
http://www.efficios.com

2010-04-28 21:54:47

by H. Peter Anvin

[permalink] [raw]
Subject: Re: [PATCH 00/11] jump label v7

On 04/27/2010 06:49 PM, Mathieu Desnoyers wrote:
> * Tony Breeds ([email protected]) wrote:
>> On Tue, Apr 27, 2010 at 05:58:58PM -0700, David Miller wrote:
>>
>>> The kernel stops compiling after the second patch because
>>> kernel/jump_label.c is compiled unconditionally, and this generates an
>>> attempt to include asm/alternatives.h which is an x86-only phenomenon.
>>>
>>> Do you have access to a cross-compile environment or at least some
>>> non-x86 system you can test build on before submitting these patch
>>> sets?
>>
>> I heard cross-compilers?
>>
>> http://kernel.org/pub/tools/crosstool/files/bin/ i686 and x86_64 and 4.4.0
>> compilers suitable for kernel work.
>>
>> I plan to build 4.4.$latest and 4.5.0 ASAP.
>>
>> Yours Tony
>
> The crosstool package from Dan Kegel did a good job for this. Not sure
> it's currently maintained though. It was a very useful project, it's a
> shame if it does not live on. It would be good to have up-to-date and
> tested compilers for various architectures available on kernel.org,
> ideally with access to a package that helps building compilers for
> various architectures.
>

Tony is taking over that work.

-hpa

2010-04-30 17:18:21

by Jason Baron

[permalink] [raw]
Subject: Re: [PATCH 00/11] jump label v7

On Tue, Apr 27, 2010 at 05:58:58PM -0700, David Miller wrote:
> >
> >> David, I re-worked the sparc64 to match the updated interfaces. The
> >> code should hopefully compile now, although I did not test the sparc
> >> bits.
> >
> > Thanks for working on this Jason. I'll take a close look at this
> > some time today.
>
> The kernel stops compiling after the second patch because
> kernel/jump_label.c is compiled unconditionally, and this generates an
> attempt to include asm/alternatives.h which is an x86-only phenomenon.
>

sorry. jump_label.c no longer requires asm/alternatives.h. It should
have been removed in this patch series.

> Do you have access to a cross-compile environment or at least some
> non-x86 system you can test build on before submitting these patch
> sets?
>

I'll make sure to test the next iteration on non-x86, with at least
dummy funtions.

thanks,

-Jason