mirror of https://gitee.com/openkylin/linux.git
Merge branch 'tip/perf/core' of git://git.kernel.org/pub/scm/linux/kernel/git/rostedt/linux-2.6-trace into perf/core
This commit is contained in:
commit
a5a2bad55d
5
Makefile
5
Makefile
|
@ -591,6 +591,11 @@ KBUILD_CFLAGS += $(call cc-option,-fno-strict-overflow)
|
|||
# conserve stack if available
|
||||
KBUILD_CFLAGS += $(call cc-option,-fconserve-stack)
|
||||
|
||||
# check for 'asm goto'
|
||||
ifeq ($(shell $(CONFIG_SHELL) $(srctree)/scripts/gcc-goto.sh $(CC)), y)
|
||||
KBUILD_CFLAGS += -DCC_HAVE_ASM_GOTO
|
||||
endif
|
||||
|
||||
# Add user supplied CPPFLAGS, AFLAGS and CFLAGS as the last assignments
|
||||
# But warn user when we do so
|
||||
warn-assign = \
|
||||
|
|
|
@ -158,4 +158,7 @@ config HAVE_PERF_EVENTS_NMI
|
|||
subsystem. Also has support for calculating CPU cycle events
|
||||
to determine how many clock cycles in a given period.
|
||||
|
||||
config HAVE_ARCH_JUMP_LABEL
|
||||
bool
|
||||
|
||||
source "kernel/gcov/Kconfig"
|
||||
|
|
|
@ -30,6 +30,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
|
||||
|
|
|
@ -0,0 +1,32 @@
|
|||
#ifndef _ASM_SPARC_JUMP_LABEL_H
|
||||
#define _ASM_SPARC_JUMP_LABEL_H
|
||||
|
||||
#ifdef __KERNEL__
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <asm/system.h>
|
||||
|
||||
#define JUMP_LABEL_NOP_SIZE 4
|
||||
|
||||
#define JUMP_LABEL(key, label) \
|
||||
do { \
|
||||
asm goto("1:\n\t" \
|
||||
"nop\n\t" \
|
||||
"nop\n\t" \
|
||||
".pushsection __jump_table, \"a\"\n\t"\
|
||||
".word 1b, %l[" #label "], %c0\n\t" \
|
||||
".popsection \n\t" \
|
||||
: : "i" (key) : : label);\
|
||||
} while (0)
|
||||
|
||||
#endif /* __KERNEL__ */
|
||||
|
||||
typedef u32 jump_label_t;
|
||||
|
||||
struct jump_entry {
|
||||
jump_label_t code;
|
||||
jump_label_t target;
|
||||
jump_label_t key;
|
||||
};
|
||||
|
||||
#endif
|
|
@ -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
|
||||
|
|
|
@ -0,0 +1,47 @@
|
|||
#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;
|
||||
u32 *insn = (u32 *) (unsigned long) entry->code;
|
||||
|
||||
if (type == JUMP_LABEL_ENABLE) {
|
||||
s32 off = (s32)entry->target - (s32)entry->code;
|
||||
|
||||
#ifdef CONFIG_SPARC64
|
||||
/* ba,pt %xcc, . + (off << 2) */
|
||||
val = 0x10680000 | ((u32) off >> 2);
|
||||
#else
|
||||
/* ba . + (off << 2) */
|
||||
val = 0x10800000 | ((u32) off >> 2);
|
||||
#endif
|
||||
} else {
|
||||
val = 0x01000000;
|
||||
}
|
||||
|
||||
get_online_cpus();
|
||||
mutex_lock(&text_mutex);
|
||||
*insn = val;
|
||||
flushi(insn);
|
||||
mutex_unlock(&text_mutex);
|
||||
put_online_cpus();
|
||||
}
|
||||
|
||||
void arch_jump_label_text_poke_early(jump_label_t addr)
|
||||
{
|
||||
u32 *insn_p = (u32 *) (unsigned long) addr;
|
||||
|
||||
*insn_p = 0x01000000;
|
||||
flushi(insn_p);
|
||||
}
|
||||
|
||||
#endif
|
|
@ -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 */
|
||||
jump_label_apply_nops(me);
|
||||
|
||||
/* Cheetah's I-cache is fully coherent. */
|
||||
if (tlb_type == spitfire) {
|
||||
unsigned long va;
|
||||
|
|
|
@ -59,6 +59,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)
|
||||
|
|
|
@ -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>
|
||||
|
||||
/*
|
||||
|
@ -160,6 +161,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.
|
||||
|
@ -180,4 +183,12 @@ 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 void arch_init_ideal_nop5(void);
|
||||
#else
|
||||
static inline void arch_init_ideal_nop5(void) {}
|
||||
#endif
|
||||
|
||||
#endif /* _ASM_X86_ALTERNATIVE_H */
|
||||
|
|
|
@ -0,0 +1,37 @@
|
|||
#ifndef _ASM_X86_JUMP_LABEL_H
|
||||
#define _ASM_X86_JUMP_LABEL_H
|
||||
|
||||
#ifdef __KERNEL__
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <asm/nops.h>
|
||||
|
||||
#define JUMP_LABEL_NOP_SIZE 5
|
||||
|
||||
# define JUMP_LABEL_INITIAL_NOP ".byte 0xe9 \n\t .long 0\n\t"
|
||||
|
||||
# define JUMP_LABEL(key, label) \
|
||||
do { \
|
||||
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" (key) : : label); \
|
||||
} while (0)
|
||||
|
||||
#endif /* __KERNEL__ */
|
||||
|
||||
#ifdef CONFIG_X86_64
|
||||
typedef u64 jump_label_t;
|
||||
#else
|
||||
typedef u32 jump_label_t;
|
||||
#endif
|
||||
|
||||
struct jump_entry {
|
||||
jump_label_t code;
|
||||
jump_label_t target;
|
||||
jump_label_t key;
|
||||
};
|
||||
|
||||
#endif
|
|
@ -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
|
||||
|
|
|
@ -195,7 +195,7 @@ static void __init_or_module add_nops(void *insns, unsigned int len)
|
|||
|
||||
extern struct alt_instr __alt_instructions[], __alt_instructions_end[];
|
||||
extern s32 __smp_locks[], __smp_locks_end[];
|
||||
static void *text_poke_early(void *addr, const void *opcode, size_t len);
|
||||
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
|
||||
|
@ -522,7 +522,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;
|
||||
|
@ -641,3 +641,67 @@ 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];
|
||||
|
||||
void __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;
|
||||
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -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
|
||||
|
@ -338,62 +333,6 @@ int ftrace_update_ftrace_func(ftrace_func_t func)
|
|||
|
||||
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;
|
||||
|
||||
|
|
|
@ -0,0 +1,50 @@
|
|||
/*
|
||||
* jump label x86 support
|
||||
*
|
||||
* Copyright (C) 2009 Jason Baron <jbaron@redhat.com>
|
||||
*
|
||||
*/
|
||||
#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>
|
||||
#include <asm/alternative.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
|
||||
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);
|
||||
mutex_unlock(&text_mutex);
|
||||
put_online_cpus();
|
||||
}
|
||||
|
||||
void arch_jump_label_text_poke_early(jump_label_t addr)
|
||||
{
|
||||
text_poke_early((void *)addr, ideal_nop5, JUMP_LABEL_NOP_SIZE);
|
||||
}
|
||||
|
||||
#endif
|
|
@ -1218,7 +1218,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;
|
||||
|
|
|
@ -239,6 +239,9 @@ int module_finalize(const Elf_Ehdr *hdr,
|
|||
apply_paravirt(pseg, pseg + para->sh_size);
|
||||
}
|
||||
|
||||
/* make jump label nops */
|
||||
jump_label_apply_nops(me);
|
||||
|
||||
return module_bug_finalize(hdr, sechdrs, me);
|
||||
}
|
||||
|
||||
|
|
|
@ -112,6 +112,7 @@
|
|||
#include <asm/numa_64.h>
|
||||
#endif
|
||||
#include <asm/mce.h>
|
||||
#include <asm/alternative.h>
|
||||
|
||||
/*
|
||||
* end_pfn only includes RAM, while max_pfn_mapped includes all e820 entries.
|
||||
|
@ -726,6 +727,7 @@ void __init setup_arch(char **cmdline_p)
|
|||
{
|
||||
int acpi = 0;
|
||||
int k8 = 0;
|
||||
unsigned long flags;
|
||||
|
||||
#ifdef CONFIG_X86_32
|
||||
memcpy(&boot_cpu_data, &new_cpu_data, sizeof(new_cpu_data));
|
||||
|
@ -1071,6 +1073,10 @@ void __init setup_arch(char **cmdline_p)
|
|||
x86_init.oem.banner();
|
||||
|
||||
mcheck_init();
|
||||
|
||||
local_irq_save(flags);
|
||||
arch_init_ideal_nop5();
|
||||
local_irq_restore(flags);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_X86_32
|
||||
|
|
|
@ -220,6 +220,8 @@
|
|||
\
|
||||
BUG_TABLE \
|
||||
\
|
||||
JUMP_TABLE \
|
||||
\
|
||||
/* PCI quirks */ \
|
||||
.pci_fixup : AT(ADDR(.pci_fixup) - LOAD_OFFSET) { \
|
||||
VMLINUX_SYMBOL(__start_pci_fixups_early) = .; \
|
||||
|
@ -563,6 +565,14 @@
|
|||
#define BUG_TABLE
|
||||
#endif
|
||||
|
||||
#define JUMP_TABLE \
|
||||
. = ALIGN(8); \
|
||||
__jump_table : AT(ADDR(__jump_table) - LOAD_OFFSET) { \
|
||||
VMLINUX_SYMBOL(__start___jump_table) = .; \
|
||||
*(__jump_table) \
|
||||
VMLINUX_SYMBOL(__stop___jump_table) = .; \
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_TRACE
|
||||
#define TRACEDATA \
|
||||
. = ALIGN(4); \
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
#ifndef _DYNAMIC_DEBUG_H
|
||||
#define _DYNAMIC_DEBUG_H
|
||||
|
||||
#include <linux/jump_label.h>
|
||||
|
||||
/* dynamic_printk_enabled, and dynamic_printk_enabled2 are bitmasks in which
|
||||
* bit n is set to 1 if any modname hashes into the bucket n, 0 otherwise. They
|
||||
* use independent hash functions, to reduce the chance of false positives.
|
||||
|
@ -22,8 +24,6 @@ struct _ddebug {
|
|||
const char *function;
|
||||
const char *filename;
|
||||
const char *format;
|
||||
char primary_hash;
|
||||
char secondary_hash;
|
||||
unsigned int lineno:24;
|
||||
/*
|
||||
* The flags field controls the behaviour at the callsite.
|
||||
|
@ -33,6 +33,7 @@ struct _ddebug {
|
|||
#define _DPRINTK_FLAGS_PRINT (1<<0) /* printk() a message using the format */
|
||||
#define _DPRINTK_FLAGS_DEFAULT 0
|
||||
unsigned int flags:8;
|
||||
char enabled;
|
||||
} __attribute__((aligned(8)));
|
||||
|
||||
|
||||
|
@ -42,33 +43,35 @@ int ddebug_add_module(struct _ddebug *tab, unsigned int n,
|
|||
#if defined(CONFIG_DYNAMIC_DEBUG)
|
||||
extern int ddebug_remove_module(const char *mod_name);
|
||||
|
||||
#define __dynamic_dbg_enabled(dd) ({ \
|
||||
int __ret = 0; \
|
||||
if (unlikely((dynamic_debug_enabled & (1LL << DEBUG_HASH)) && \
|
||||
(dynamic_debug_enabled2 & (1LL << DEBUG_HASH2)))) \
|
||||
if (unlikely(dd.flags)) \
|
||||
__ret = 1; \
|
||||
__ret; })
|
||||
|
||||
#define dynamic_pr_debug(fmt, ...) do { \
|
||||
__label__ do_printk; \
|
||||
__label__ out; \
|
||||
static struct _ddebug descriptor \
|
||||
__used \
|
||||
__attribute__((section("__verbose"), aligned(8))) = \
|
||||
{ KBUILD_MODNAME, __func__, __FILE__, fmt, DEBUG_HASH, \
|
||||
DEBUG_HASH2, __LINE__, _DPRINTK_FLAGS_DEFAULT }; \
|
||||
if (__dynamic_dbg_enabled(descriptor)) \
|
||||
printk(KERN_DEBUG pr_fmt(fmt), ##__VA_ARGS__); \
|
||||
{ KBUILD_MODNAME, __func__, __FILE__, fmt, __LINE__, \
|
||||
_DPRINTK_FLAGS_DEFAULT }; \
|
||||
JUMP_LABEL(&descriptor.enabled, do_printk); \
|
||||
goto out; \
|
||||
do_printk: \
|
||||
printk(KERN_DEBUG pr_fmt(fmt), ##__VA_ARGS__); \
|
||||
out: ; \
|
||||
} while (0)
|
||||
|
||||
|
||||
#define dynamic_dev_dbg(dev, fmt, ...) do { \
|
||||
__label__ do_printk; \
|
||||
__label__ out; \
|
||||
static struct _ddebug descriptor \
|
||||
__used \
|
||||
__attribute__((section("__verbose"), aligned(8))) = \
|
||||
{ KBUILD_MODNAME, __func__, __FILE__, fmt, DEBUG_HASH, \
|
||||
DEBUG_HASH2, __LINE__, _DPRINTK_FLAGS_DEFAULT }; \
|
||||
if (__dynamic_dbg_enabled(descriptor)) \
|
||||
dev_printk(KERN_DEBUG, dev, fmt, ##__VA_ARGS__); \
|
||||
{ KBUILD_MODNAME, __func__, __FILE__, fmt, __LINE__, \
|
||||
_DPRINTK_FLAGS_DEFAULT }; \
|
||||
JUMP_LABEL(&descriptor.enabled, do_printk); \
|
||||
goto out; \
|
||||
do_printk: \
|
||||
dev_printk(KERN_DEBUG, dev, fmt, ##__VA_ARGS__); \
|
||||
out: ; \
|
||||
} while (0)
|
||||
|
||||
#else
|
||||
|
|
|
@ -0,0 +1,64 @@
|
|||
#ifndef _LINUX_JUMP_LABEL_H
|
||||
#define _LINUX_JUMP_LABEL_H
|
||||
|
||||
#if defined(CC_HAVE_ASM_GOTO) && defined(CONFIG_HAVE_ARCH_JUMP_LABEL)
|
||||
# include <asm/jump_label.h>
|
||||
# define HAVE_JUMP_LABEL
|
||||
#endif
|
||||
|
||||
enum jump_label_type {
|
||||
JUMP_LABEL_ENABLE,
|
||||
JUMP_LABEL_DISABLE
|
||||
};
|
||||
|
||||
struct module;
|
||||
|
||||
#ifdef HAVE_JUMP_LABEL
|
||||
|
||||
extern struct jump_entry __start___jump_table[];
|
||||
extern struct jump_entry __stop___jump_table[];
|
||||
|
||||
extern void arch_jump_label_transform(struct jump_entry *entry,
|
||||
enum jump_label_type type);
|
||||
extern void arch_jump_label_text_poke_early(jump_label_t addr);
|
||||
extern void jump_label_update(unsigned long key, enum jump_label_type type);
|
||||
extern void jump_label_apply_nops(struct module *mod);
|
||||
extern int jump_label_text_reserved(void *start, void *end);
|
||||
|
||||
#define enable_jump_label(key) \
|
||||
jump_label_update((unsigned long)key, JUMP_LABEL_ENABLE);
|
||||
|
||||
#define disable_jump_label(key) \
|
||||
jump_label_update((unsigned long)key, JUMP_LABEL_DISABLE);
|
||||
|
||||
#else
|
||||
|
||||
#define JUMP_LABEL(key, label) \
|
||||
do { \
|
||||
if (unlikely(*key)) \
|
||||
goto label; \
|
||||
} while (0)
|
||||
|
||||
#define enable_jump_label(cond_var) \
|
||||
do { \
|
||||
*(cond_var) = 1; \
|
||||
} while (0)
|
||||
|
||||
#define disable_jump_label(cond_var) \
|
||||
do { \
|
||||
*(cond_var) = 0; \
|
||||
} while (0)
|
||||
|
||||
static inline int jump_label_apply_nops(struct module *mod)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int jump_label_text_reserved(void *start, void *end)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
|
@ -350,7 +350,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;
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
#include <linux/errno.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/rcupdate.h>
|
||||
#include <linux/jump_label.h>
|
||||
|
||||
struct module;
|
||||
struct tracepoint;
|
||||
|
@ -145,7 +146,9 @@ static inline void tracepoint_update_probe_range(struct tracepoint *begin,
|
|||
extern struct tracepoint __tracepoint_##name; \
|
||||
static inline void trace_##name(proto) \
|
||||
{ \
|
||||
if (unlikely(__tracepoint_##name.state)) \
|
||||
JUMP_LABEL(&__tracepoint_##name.state, do_trace); \
|
||||
return; \
|
||||
do_trace: \
|
||||
__DO_TRACE(&__tracepoint_##name, \
|
||||
TP_PROTO(data_proto), \
|
||||
TP_ARGS(data_args)); \
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -0,0 +1,429 @@
|
|||
/*
|
||||
* jump label support
|
||||
*
|
||||
* Copyright (C) 2009 Jason Baron <jbaron@redhat.com>
|
||||
*
|
||||
*/
|
||||
#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 <linux/sort.h>
|
||||
#include <linux/err.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;
|
||||
unsigned long key;
|
||||
};
|
||||
|
||||
struct jump_label_module_entry {
|
||||
struct hlist_node hlist;
|
||||
struct jump_entry *table;
|
||||
int nr_entries;
|
||||
struct module *mod;
|
||||
};
|
||||
|
||||
static int jump_label_cmp(const void *a, const void *b)
|
||||
{
|
||||
const struct jump_entry *jea = a;
|
||||
const struct jump_entry *jeb = b;
|
||||
|
||||
if (jea->key < jeb->key)
|
||||
return -1;
|
||||
|
||||
if (jea->key > jeb->key)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
sort_jump_label_entries(struct jump_entry *start, struct jump_entry *stop)
|
||||
{
|
||||
unsigned long size;
|
||||
|
||||
size = (((unsigned long)stop - (unsigned long)start)
|
||||
/ sizeof(struct jump_entry));
|
||||
sort(start, size, sizeof(struct jump_entry), jump_label_cmp, NULL);
|
||||
}
|
||||
|
||||
static struct jump_label_entry *get_jump_label_entry(jump_label_t key)
|
||||
{
|
||||
struct hlist_head *head;
|
||||
struct hlist_node *node;
|
||||
struct jump_label_entry *e;
|
||||
u32 hash = jhash((void *)&key, sizeof(jump_label_t), 0);
|
||||
|
||||
head = &jump_label_table[hash & (JUMP_LABEL_TABLE_SIZE - 1)];
|
||||
hlist_for_each_entry(e, node, head, hlist) {
|
||||
if (key == e->key)
|
||||
return e;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct jump_label_entry *
|
||||
add_jump_label_entry(jump_label_t key, int nr_entries, struct jump_entry *table)
|
||||
{
|
||||
struct hlist_head *head;
|
||||
struct jump_label_entry *e;
|
||||
u32 hash;
|
||||
|
||||
e = get_jump_label_entry(key);
|
||||
if (e)
|
||||
return ERR_PTR(-EEXIST);
|
||||
|
||||
e = kmalloc(sizeof(struct jump_label_entry), GFP_KERNEL);
|
||||
if (!e)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
hash = jhash((void *)&key, sizeof(jump_label_t), 0);
|
||||
head = &jump_label_table[hash & (JUMP_LABEL_TABLE_SIZE - 1)];
|
||||
e->key = key;
|
||||
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(iter->key);
|
||||
if (!entry) {
|
||||
iter_begin = iter;
|
||||
count = 0;
|
||||
while ((iter < stop) &&
|
||||
(iter->key == iter_begin->key)) {
|
||||
iter++;
|
||||
count++;
|
||||
}
|
||||
entry = add_jump_label_entry(iter_begin->key,
|
||||
count, iter_begin);
|
||||
if (IS_ERR(entry))
|
||||
return PTR_ERR(entry);
|
||||
} else {
|
||||
WARN_ONCE(1, KERN_ERR "build_jump_hashtable: unexpected entry!\n");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/***
|
||||
* jump_label_update - update jump label text
|
||||
* @key - key value associated with a a jump label
|
||||
* @type - enum set to JUMP_LABEL_ENABLE or JUMP_LABEL_DISABLE
|
||||
*
|
||||
* Will enable/disable the jump for jump label @key, depending on the
|
||||
* value of @type.
|
||||
*
|
||||
*/
|
||||
|
||||
void jump_label_update(unsigned long key, 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((jump_label_t)key);
|
||||
if (entry) {
|
||||
count = entry->nr_entries;
|
||||
iter = entry->table;
|
||||
while (count--) {
|
||||
if (kernel_text_address(iter->code))
|
||||
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);
|
||||
}
|
||||
|
||||
static int addr_conflict(struct jump_entry *entry, void *start, void *end)
|
||||
{
|
||||
if (entry->code <= (unsigned long)end &&
|
||||
entry->code + JUMP_LABEL_NOP_SIZE > (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 __init int init_jump_label(void)
|
||||
{
|
||||
int ret;
|
||||
struct jump_entry *iter_start = __start___jump_table;
|
||||
struct jump_entry *iter_stop = __stop___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) {
|
||||
arch_jump_label_text_poke_early(iter->code);
|
||||
iter++;
|
||||
}
|
||||
mutex_unlock(&jump_label_mutex);
|
||||
return ret;
|
||||
}
|
||||
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(iter->key);
|
||||
iter_begin = iter;
|
||||
count = 0;
|
||||
while ((iter < mod->jump_entries + mod->num_jump_entries) &&
|
||||
(iter->key == iter_begin->key)) {
|
||||
iter++;
|
||||
count++;
|
||||
}
|
||||
if (!entry) {
|
||||
entry = add_jump_label_entry(iter_begin->key, 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;
|
||||
}
|
||||
|
||||
/***
|
||||
* 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 jump_label_apply_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) {
|
||||
arch_jump_label_text_poke_early(iter->code);
|
||||
iter++;
|
||||
}
|
||||
}
|
||||
|
||||
struct notifier_block jump_label_module_nb = {
|
||||
.notifier_call = jump_label_module_notify,
|
||||
.priority = 0,
|
||||
};
|
||||
|
||||
static __init int init_jump_label_module(void)
|
||||
{
|
||||
return register_module_notifier(&jump_label_module_nb);
|
||||
}
|
||||
early_initcall(init_jump_label_module);
|
||||
|
||||
#endif /* CONFIG_MODULES */
|
||||
|
||||
#endif
|
|
@ -47,6 +47,7 @@
|
|||
#include <linux/memory.h>
|
||||
#include <linux/ftrace.h>
|
||||
#include <linux/cpu.h>
|
||||
#include <linux/jump_label.h>
|
||||
|
||||
#include <asm-generic/sections.h>
|
||||
#include <asm/cacheflush.h>
|
||||
|
@ -1146,7 +1147,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;
|
||||
}
|
||||
|
|
|
@ -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>
|
||||
|
@ -2308,6 +2309,11 @@ static void find_module_sections(struct module *mod, struct load_info *info)
|
|||
sizeof(*mod->tracepoints),
|
||||
&mod->num_tracepoints);
|
||||
#endif
|
||||
#ifdef HAVE_JUMP_LABEL
|
||||
mod->jump_entries = section_objs(info, "__jump_table",
|
||||
sizeof(*mod->jump_entries),
|
||||
&mod->num_jump_entries);
|
||||
#endif
|
||||
#ifdef CONFIG_EVENT_TRACING
|
||||
mod->trace_events = section_objs(info, "_ftrace_events",
|
||||
sizeof(*mod->trace_events),
|
||||
|
|
|
@ -263,6 +263,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, NULL);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
@ -279,11 +284,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:
|
||||
|
|
|
@ -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[];
|
||||
|
@ -263,7 +264,13 @@ static void set_tracepoint(struct tracepoint_entry **entry,
|
|||
* is used.
|
||||
*/
|
||||
rcu_assign_pointer(elem->funcs, (*entry)->funcs);
|
||||
elem->state = active;
|
||||
if (!elem->state && active) {
|
||||
enable_jump_label(&elem->state);
|
||||
elem->state = active;
|
||||
} else if (elem->state && !active) {
|
||||
disable_jump_label(&elem->state);
|
||||
elem->state = active;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -277,7 +284,10 @@ static void disable_tracepoint(struct tracepoint *elem)
|
|||
if (elem->unregfunc && elem->state)
|
||||
elem->unregfunc();
|
||||
|
||||
elem->state = 0;
|
||||
if (elem->state) {
|
||||
disable_jump_label(&elem->state);
|
||||
elem->state = 0;
|
||||
}
|
||||
rcu_assign_pointer(elem->funcs, NULL);
|
||||
}
|
||||
|
||||
|
|
|
@ -26,19 +26,11 @@
|
|||
#include <linux/dynamic_debug.h>
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/jump_label.h>
|
||||
|
||||
extern struct _ddebug __start___verbose[];
|
||||
extern struct _ddebug __stop___verbose[];
|
||||
|
||||
/* dynamic_debug_enabled, and dynamic_debug_enabled2 are bitmasks in which
|
||||
* bit n is set to 1 if any modname hashes into the bucket n, 0 otherwise. They
|
||||
* use independent hash functions, to reduce the chance of false positives.
|
||||
*/
|
||||
long long dynamic_debug_enabled;
|
||||
EXPORT_SYMBOL_GPL(dynamic_debug_enabled);
|
||||
long long dynamic_debug_enabled2;
|
||||
EXPORT_SYMBOL_GPL(dynamic_debug_enabled2);
|
||||
|
||||
struct ddebug_table {
|
||||
struct list_head link;
|
||||
char *mod_name;
|
||||
|
@ -87,26 +79,6 @@ static char *ddebug_describe_flags(struct _ddebug *dp, char *buf,
|
|||
return buf;
|
||||
}
|
||||
|
||||
/*
|
||||
* must be called with ddebug_lock held
|
||||
*/
|
||||
|
||||
static int disabled_hash(char hash, bool first_table)
|
||||
{
|
||||
struct ddebug_table *dt;
|
||||
char table_hash_value;
|
||||
|
||||
list_for_each_entry(dt, &ddebug_tables, link) {
|
||||
if (first_table)
|
||||
table_hash_value = dt->ddebugs->primary_hash;
|
||||
else
|
||||
table_hash_value = dt->ddebugs->secondary_hash;
|
||||
if (dt->num_enabled && (hash == table_hash_value))
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Search the tables for _ddebug's which match the given
|
||||
* `query' and apply the `flags' and `mask' to them. Tells
|
||||
|
@ -170,17 +142,9 @@ static void ddebug_change(const struct ddebug_query *query,
|
|||
dt->num_enabled++;
|
||||
dp->flags = newflags;
|
||||
if (newflags) {
|
||||
dynamic_debug_enabled |=
|
||||
(1LL << dp->primary_hash);
|
||||
dynamic_debug_enabled2 |=
|
||||
(1LL << dp->secondary_hash);
|
||||
enable_jump_label(&dp->enabled);
|
||||
} else {
|
||||
if (disabled_hash(dp->primary_hash, true))
|
||||
dynamic_debug_enabled &=
|
||||
~(1LL << dp->primary_hash);
|
||||
if (disabled_hash(dp->secondary_hash, false))
|
||||
dynamic_debug_enabled2 &=
|
||||
~(1LL << dp->secondary_hash);
|
||||
disable_jump_label(&dp->enabled);
|
||||
}
|
||||
if (verbose)
|
||||
printk(KERN_INFO
|
||||
|
|
|
@ -101,14 +101,6 @@ basename_flags = -D"KBUILD_BASENAME=KBUILD_STR($(call name-fix,$(basetarget)))"
|
|||
modname_flags = $(if $(filter 1,$(words $(modname))),\
|
||||
-D"KBUILD_MODNAME=KBUILD_STR($(call name-fix,$(modname)))")
|
||||
|
||||
#hash values
|
||||
ifdef CONFIG_DYNAMIC_DEBUG
|
||||
debug_flags = -D"DEBUG_HASH=$(shell ./scripts/basic/hash djb2 $(@D)$(modname))"\
|
||||
-D"DEBUG_HASH2=$(shell ./scripts/basic/hash r5 $(@D)$(modname))"
|
||||
else
|
||||
debug_flags =
|
||||
endif
|
||||
|
||||
orig_c_flags = $(KBUILD_CPPFLAGS) $(KBUILD_CFLAGS) $(KBUILD_SUBDIR_CCFLAGS) \
|
||||
$(ccflags-y) $(CFLAGS_$(basetarget).o)
|
||||
_c_flags = $(filter-out $(CFLAGS_REMOVE_$(basetarget).o), $(orig_c_flags))
|
||||
|
@ -152,8 +144,7 @@ endif
|
|||
|
||||
c_flags = -Wp,-MD,$(depfile) $(NOSTDINC_FLAGS) $(LINUXINCLUDE) \
|
||||
$(__c_flags) $(modkern_cflags) \
|
||||
-D"KBUILD_STR(s)=\#s" $(basename_flags) $(modname_flags) \
|
||||
$(debug_flags)
|
||||
-D"KBUILD_STR(s)=\#s" $(basename_flags) $(modname_flags)
|
||||
|
||||
a_flags = -Wp,-MD,$(depfile) $(NOSTDINC_FLAGS) $(LINUXINCLUDE) \
|
||||
$(__a_flags) $(modkern_aflags)
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
# fixdep: Used to generate dependency information during build process
|
||||
# docproc: Used in Documentation/DocBook
|
||||
|
||||
hostprogs-y := fixdep docproc hash
|
||||
hostprogs-y := fixdep docproc
|
||||
always := $(hostprogs-y)
|
||||
|
||||
# fixdep is needed to compile other host programs
|
||||
|
|
|
@ -1,64 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2008 Red Hat, Inc., Jason Baron <jbaron@redhat.com>
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#define DYNAMIC_DEBUG_HASH_BITS 6
|
||||
|
||||
static const char *program;
|
||||
|
||||
static void usage(void)
|
||||
{
|
||||
printf("Usage: %s <djb2|r5> <modname>\n", program);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/* djb2 hashing algorithm by Dan Bernstein. From:
|
||||
* http://www.cse.yorku.ca/~oz/hash.html
|
||||
*/
|
||||
|
||||
static unsigned int djb2_hash(char *str)
|
||||
{
|
||||
unsigned long hash = 5381;
|
||||
int c;
|
||||
|
||||
c = *str;
|
||||
while (c) {
|
||||
hash = ((hash << 5) + hash) + c;
|
||||
c = *++str;
|
||||
}
|
||||
return (unsigned int)(hash & ((1 << DYNAMIC_DEBUG_HASH_BITS) - 1));
|
||||
}
|
||||
|
||||
static unsigned int r5_hash(char *str)
|
||||
{
|
||||
unsigned long hash = 0;
|
||||
int c;
|
||||
|
||||
c = *str;
|
||||
while (c) {
|
||||
hash = (hash + (c << 4) + (c >> 4)) * 11;
|
||||
c = *++str;
|
||||
}
|
||||
return (unsigned int)(hash & ((1 << DYNAMIC_DEBUG_HASH_BITS) - 1));
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
program = argv[0];
|
||||
|
||||
if (argc != 3)
|
||||
usage();
|
||||
if (!strcmp(argv[1], "djb2"))
|
||||
printf("%d\n", djb2_hash(argv[2]));
|
||||
else if (!strcmp(argv[1], "r5"))
|
||||
printf("%d\n", r5_hash(argv[2]));
|
||||
else
|
||||
usage();
|
||||
exit(0);
|
||||
}
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
#!/bin/sh
|
||||
# Test for gcc 'asm goto' suport
|
||||
# Copyright (C) 2010, Jason Baron <jbaron@redhat.com>
|
||||
|
||||
echo "int main(void) { entry: asm goto (\"\"::::entry); return 0; }" | $1 -x c - -c -o /dev/null >/dev/null 2>&1 && echo "y"
|
Loading…
Reference in New Issue