x86/jump label: Paranoid checks and 2 or 5 byte nops
Subsystems (3)
Steven Rostedt
reviewerIngo,
I've done some final clean ups of the 2 or 5 byte nop code and I also added a few generic clean ups of jump label itself. The first 2 patches use the best nops as defined by Peter when we do not know what the machine is running.
The third patch adds paranoid checks, to make sure what we are modifying is truely what it expects. Otherwise we bug and crash the kernel (as recommended by Peter).
The last two patches are the clean up versions of the 2 or 5 byte nops.
Note, there is code in the infrastructure that can be shared among other users in scripts and elsewhere. I rather get this code in first and then fiddle with seeing who does what and then integrate all the code together. As that may not be such an easy task, and I don't want to post pone this code due to it.
Also, I feel that we can let this code sit for a bit on LKML and let people comment. If everyone gives there acks, then we can pull this code in. I already tested it, but if there are acks, I can rebase just to update the acked-bys.
Please pull (if everyone's OK with it) the latest tip/perf/jump-label tree,
which can be found at:
git://git.kernel.org/pub/scm/linux/kernel/git/rostedt/linux-trace.git
tip/perf/jump-label
Head SHA1: 7837dddecaa7e7f31b732a0c302a4bdd255a8321
Steven Rostedt (5):
x86/jump-label: Use best default nops for inital jump label calls x86/jump-label: Do not bother updating nops if they are correct x86/jump-label: Add safety checks to jump label conversions jump labels: Add infrastructure to update jump labels at compile time jump labels/x86: Use etiher 5 byte or 2 byte jumps
----
Makefile | 7 + arch/Kconfig | 6 + arch/x86/Kconfig | 1 + arch/x86/include/asm/jump_label.h | 9 +- arch/x86/kernel/jump_label.c | 110 +++++++++++- scripts/Makefile | 1 + scripts/Makefile.build | 15 ++- scripts/update_jump_label.c | 335 +++++++++++++++++++++++++++++++++++++ scripts/update_jump_label.h | 208 +++++++++++++++++++++++ 9 files changed, 678 insertions(+), 14 deletions(-)
Steven Rostedt
reviewerPATCH 1/5 x86/jump-label: Use best default nops for inital jump label calls
From: Steven Rostedt <[email protected]>
As specified by H. Peter Anvin, the best nops for x86 without knowing the running computer is:
32bit:
0x3e, 0x8d, 0x74, 0x26, 0x00
64bit:
0x0f, 0x1f, 0x44, 0x00, 0x00 also known as P6_NOP5
Currently the default nop that is used by jump label is:
0xe9 0x00 0x00 0x00 0x00
Which is really a 5byte jump to the next position.
It's better to use a real nop than a jmp.
--- arch/x86/include/asm/jump_label.h | 9 +++++++-- 1 files changed, 7 insertions(+), 2 deletions(-)
arch/x86/include/asm/jump_label.h +7 -2
@@ -3,18 +3,23 @@#ifdef __KERNEL__ +#include <linux/stringify.h> #include <linux/types.h> #include <asm/nops.h> #include <asm/asm.h> #define JUMP_LABEL_NOP_SIZE 5 -#define JUMP_LABEL_INITIAL_NOP ".byte 0xe9 \n\t .long 0\n\t" +#ifdef CONFIG_X86_64 +# define JUMP_LABEL_INIT_NOP P6_NOP5 +#else +# define JUMP_LABEL_INIT_NOP 0x3e, 0x8d, 0x74, 0x26, 0x00 +#endif static __always_inline bool arch_static_branch(struct jump_label_key *key) { asm goto("1:" -JUMP_LABEL_INITIAL_NOP + ".byte " __stringify(JUMP_LABEL_INIT_NOP) "\n\t"".pushsection __jump_table, \"aw\" \n\t" _ASM_ALIGN"\n\t" _ASM_PTR"1b, %l[l_yes], %c0 \n\t"
Signature · 1 line
1.7.8.3
H. Peter Anvin
reviewerRe: [PATCH 1/5] x86/jump-label: Use best default nops for inital jump label calls
Review
Quoted 7 lines · Steven Rostedt
#define
JUMP_LABEL_NOP_SIZE5(quoted diff) +2
-1-#define JUMP_LABEL_INITIAL_NOP ".byte 0xe9 \n\t .long 0\n\t" +#ifdef CONFIG_X86_64 +# define JUMP_LABEL_INIT_NOP P6_NOP5 +#else +# define JUMP_LABEL_INIT_NOP 0x3e, 0x8d, 0x74, 0x26, 0x00 +#endif
Why not just use ASM_NOP5_ATOMIC since you have configuration available here?
-hpa
Steven Rostedt
reviewerRe: [PATCH 1/5] x86/jump-label: Use best default nops for inital jump label calls
[ Added Michal ]
Quoted 11 lines · H. Peter Anvin
Review
Quoted 7 lines · Steven Rostedt
#define
JUMP_LABEL_NOP_SIZE5(quoted diff) +2
-1-#define JUMP_LABEL_INITIAL_NOP ".byte 0xe9 \n\t .long 0\n\t" +#ifdef CONFIG_X86_64 +# define JUMP_LABEL_INIT_NOP P6_NOP5 +#else +# define JUMP_LABEL_INIT_NOP 0x3e, 0x8d, 0x74, 0x26, 0x00 +#endifWhy not just use
ASM_NOP5_ATOMICsince you have configuration available here?
This eventually needs to match the code in the update_jump_label.c, otherwise the checks will be incorrect on boot up, and we'll BUG the kernel.
I need to find a way to get the config options of the kernel into the compiling of the update_jump_label.c. Then I could use the nops.h file with the set configs.
Michal, can we get host tools to compile with the configs set by the .config file?
update_jump_label.c is built just like recordmcount.c is. It would be great if it could be compiled with the same config.h defines as the kernel is. That way I can make it (and even recordmcount.c) customize to the type of kernel that is being built.
Then again, it would also need to be part of the dependencies that are done with configs.
If not, at the very least, I'll add a comment (in the last patch) that explains why we use our own "JUMP_LABEL_INIT_NOP" instead of ASM_NOP5_ATOMIC.
-- Steve
H. Peter Anvin
reviewerRe: [PATCH 1/5] x86/jump-label: Use best default nops for inital jump label calls
Quoted 5 lines · Steven Rostedt
If not, at the very least, I'll add a comment (in the last patch) that explains why we use our own "
JUMP_LABEL_INIT_NOP" instead ofASM_NOP5_ATOMIC.
Then at least use GENERIC_NOP5_ATOMIC instead of open-coding it.
-hpa
Steven Rostedt
reviewerRe: [PATCH 1/5] x86/jump-label: Use best default nops for inital jump label calls
Quoted 8 lines · H. Peter Anvin
Quoted 5 lines · Steven Rostedt
If not, at the very least, I'll add a comment (in the last patch) that explains why we use our own "
JUMP_LABEL_INIT_NOP" instead ofASM_NOP5_ATOMIC.Then at least use
GENERIC_NOP5_ATOMICinstead of open-coding it.
OK, I can do that. Hehe, I looked for the x86_32 version, but it was so obfuscated that I didn't find it. Thanks for pointing it out.
-- Steve
Re: [PATCH 1/5] x86/jump-label: Use best default nops for inital jump label calls
In reply to Steven RostedtQuoted 3 lines · Steven Rostedt
I need to find a way to get the config options of the kernel into the compiling of the
update_jump_label.c. Then I could use thenops.hfile with the set configs.
You can do a #include "../include/generated/autoconf.h" and you will get the CONFIG_ definitions.
Quoted 2 lines
Michal, can we get host tools to compile with the configs set by the .config file?
Yeah, we should simply compile all the host tools with autoconf.h and kconfig.h included. The idea behind not doing this probably was that a single scripts/ directory should work with multiple kernel configurations, but this is obviously not the case.
Michal
Steven Rostedt
reviewerPATCH 2/5 x86/jump-label: Do not bother updating nops if they are correct
In reply to Steven RostedtFrom: Steven Rostedt <[email protected]>
On boot up, the jump label init function scans all the jump label locations and converts them to the best nop for the machine. If the nop is already the ideal nop, do not bother with changing it.
--- arch/x86/kernel/jump_label.c | 20 +++++++++++++++++++- 1 files changed, 19 insertions(+), 1 deletions(-)
arch/x86/kernel/jump_label.c +19 -1
@@ -53,7 +53,25 @@ void arch_jump_label_transform(struct jump_entry *entry,void arch_jump_label_transform_static (struct jump_entry *entry,enum jump_label_type type) { -__jump_label_transform (entry, type, text_poke_early); +static int once; +static int update; + +/* +* This function is called at boot up and when modules are +* first loaded. Check if the default nop, the one that is +* inserted at compile time, is the ideal nop. If it is, then +* we do not need to update the nop, and we can leave it as is. +* If it is not, then we need to update the nop to the ideal nop. +*/ +if (!once) { +unsigned char default_nop[] = {JUMP_LABEL_INIT_NOP }; +const unsigned char *ideal_nop = ideal_nops[NOP_ATOMIC5 ]; + once++; +if (memcmp (ideal_nop, default_nop,5 ) !=0 ) + update =1 ; + } +if (update) +__jump_label_transform (entry, type, text_poke_early); }#endif
Signature · 1 line
1.7.8.3
Re: [PATCH 2/5] x86/jump-label: Do not bother updating nops if they are correct
Quoted 9 lines · On Fri, 27 Jan 2012 15:14:44 -0500 Steven Rostedt <[email protected]> wrote:
arch/x86/kernel/jump_label.c +2
-1@@ -53,7 +53,25 @@ void arch_jump_label_transform(struct jump_entry *entry,void arch_jump_label_transform_static (struct jump_entry *entry,enum jump_label_type type) { -__jump_label_transform (entry, type, text_poke_early); +static int once; +static int update;
arch/x86/kernel/jump_label.c void arch_jump_label_transform(struct jump_entry *entry, · line 67
Quoted 10 lines
arch/x86/kernel/jump_label.c +10
-0+ +/* +* This function is called at boot up and when modules are +* first loaded. Check if the default nop, the one that is +* inserted at compile time, is the ideal nop. If it is, then +* we do not need to update the nop, and we can leave it as is. +* If it is not, then we need to update the nop to the ideal nop. +*/ +if (!once) { +unsigned char default_nop[] = {JUMP_LABEL_INIT_NOP };
const. It generates less code. With my compiler version, at least. That was a bit dumb of it, given that memcmp() is declared to take const args.
Quoted 8 lines
arch/x86/kernel/jump_label.c +7
-0+const unsigned char *ideal_nop = ideal_nops[NOP_ATOMIC5 ]; + once++; +if (memcmp (ideal_nop, default_nop,5 ) !=0 ) + update =1 ; + } +if (update) +__jump_label_transform (entry, type, text_poke_early); }
Steven Rostedt
reviewerRe: [PATCH 2/5] x86/jump-label: Do not bother updating nops if they are correct
Quoted 6 lines · Andrew Morton
arch/x86/kernel/jump_label.c void arch_jump_label_transform(struct jump_entry *entry, · line 67
Quoted 2 lines
arch/x86/kernel/jump_label.c +2
-0+if (!once) { +unsigned char default_nop[] = {JUMP_LABEL_INIT_NOP };const. It generates less code. With my compiler version, at least. That was a bit dumb of it, given that
memcmp()is declared to take const args.
OK will update,
Thanks!
-- Steve
Steven Rostedt
reviewerPATCH 3/5 x86/jump-label: Add safety checks to jump label conversions
In reply to Steven RostedtFrom: Steven Rostedt <[email protected]>
As with all modifying of kernel text, we need to be very paranoid.
When converting the jump label locations to and from nops to jumps a check has been added to make sure what we are replacing is what we expect, otherwise we bug.
--- arch/x86/kernel/jump_label.c | 32 ++++++++++++++++++++++++++++---- 1 files changed, 28 insertions(+), 4 deletions(-)
arch/x86/kernel/jump_label.c +28 -4
@@ -26,16 +26,40 @@ union jump_code_union {static void __jump_label_transform (struct jump_entry *entry,enum jump_label_type type, -void *(*poker)(void *,const void *,size_t )) +void *(*poker)(void *,const void *,size_t ), +int init) {union jump_code_union code; +const unsigned char *ideal_nop = ideal_nops[NOP_ATOMIC5 ];if (type ==JUMP_LABEL_ENABLE ) { +/* +* We are enabling this jump label. If it is not a nop +* then something must have gone wrong. +*/ +BUG_ON (memcmp ((void *)entry->code, ideal_nop,5 ) !=0 ); + code.jump =0xe9 ; code.offset = entry->target - (entry->code +JUMP_LABEL_NOP_SIZE ); - }else + }else { +/* +* We are disabling this jump label. If it is not what +* we think it is, then something must have gone wrong. +* If this is the first initialization call, then we +* are converting the default nop to the ideal nop. +*/ +if (init) { +unsigned char default_nop[] = {JUMP_LABEL_INIT_NOP }; +BUG_ON (memcmp ((void *)entry->code, default_nop,5 ) !=0 ); + }else { + code.jump =0xe9 ; + code.offset = entry->target - + (entry->code +JUMP_LABEL_NOP_SIZE ); +BUG_ON (memcmp ((void *)entry->code, &code,5 ) !=0 ); + }memcpy (&code, ideal_nops[NOP_ATOMIC5 ],JUMP_LABEL_NOP_SIZE ); + } (*poker)((void *)entry->code, &code,JUMP_LABEL_NOP_SIZE ); }
@@ -45,7 +69,7 @@ void arch_jump_label_transform(struct jump_entry *entry, {get_online_cpus ();mutex_lock (&text_mutex); -__jump_label_transform (entry, type, text_poke_smp); +__jump_label_transform (entry, type, text_poke_smp,0 );mutex_unlock (&text_mutex);put_online_cpus (); }
@@ -71,7 +95,7 @@ void arch_jump_label_transform_static(struct jump_entry *entry, update = 1; }if (update) -__jump_label_transform (entry, type, text_poke_early); +__jump_label_transform (entry, type, text_poke_early,1 ); }#endif
Signature · 1 line
1.7.8.3
Steven Rostedt
reviewerPATCH 4/5 jump labels: Add infrastructure to update jump labels at compile time
In reply to Steven RostedtFrom: Steven Rostedt <[email protected]>
Add the infrastructure to allow architectures to modify the jump label locations at compile time. This is mainly for x86, where the jmps may be either 2 bytes or 5 bytes. Instead of wasting 5 bytes for all jump labels, this code will let x86 put in a jmp instead of a 5 byte nop. Then the assembler will make either a 2 byte or 5 byte jmp depending on where the target is.
At compile time, this code will replace the jmps with either a 2 byte or 5 byte nop depending on the size of the jmp that was added.
--- Makefile | 7 + arch/Kconfig | 6 + scripts/Makefile | 1 + scripts/Makefile.build | 15 ++- scripts/update_jump_label.c | 335 +++++++++++++++++++++++++++++++++++++++++++ scripts/update_jump_label.h | 208 +++++++++++++++++++++++++++ 6 files changed, 570 insertions(+), 2 deletions(-) create mode 100644 scripts/update_jump_label.c create mode 100644 scripts/update_jump_label.h
Makefile +7 -0
@@ -611,6 +611,13 @@ ifdef CONFIG_DYNAMIC_FTRACE endif endif +ifdef CONFIG_JUMP_LABEL + ifdef CONFIG_HAVE_BUILD_TIME_JUMP_LABEL + BUILD_UPDATE_JUMP_LABEL := y + export BUILD_UPDATE_JUMP_LABEL + endif +endif + # We trigger additional mismatches with less inlining ifdef CONFIG_DEBUG_SECTION_MISMATCH KBUILD_CFLAGS += $(call cc-option, -fno-inline-functions-called-once)
arch/Kconfig +6 -0
@@ -169,6 +169,12 @@ 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_BUILD_TIME_JUMP_LABEL + bool + help + If an arch uses scripts/update_jump_label to patch in jump nops + at build time, then it must enable this option. + config HAVE_ARCH_JUMP_LABEL bool
scripts/Makefile +1 -0
@@ -13,6 +13,7 @@ hostprogs-$(CONFIG_LOGO) += pnmtologo hostprogs-$(CONFIG_VT) += conmakehash hostprogs-$(CONFIG_IKCONFIG) += bin2c hostprogs-$(BUILD_C_RECORDMCOUNT) += recordmcount +hostprogs-$(BUILD_UPDATE_JUMP_LABEL) += update_jump_label always := $(hostprogs-y) $(hostprogs-m)
scripts/Makefile.build +13 -2
@@ -258,6 +258,15 @@ cmd_modversions = \ fi; endif +ifdef BUILD_UPDATE_JUMP_LABEL +update_jump_label_source := $(srctree)/scripts/update_jump_label.c \ + $(srctree)/scripts/update_jump_label.h +cmd_update_jump_label = \ + if [ $(@) != "scripts/mod/empty.o" ]; then \ + $(objtree)/scripts/update_jump_label "$(@)"; \ + fi; +endif + ifdef CONFIG_FTRACE_MCOUNT_RECORD ifdef BUILD_C_RECORDMCOUNT ifeq ("$(origin RECORDMCOUNT_WARN)", "command line")
@@ -294,6 +303,7 @@ define rule_cc_o_c $(cmd_modversions) \ $(call echo-cmd,record_mcount) \ $(cmd_record_mcount) \ + $(cmd_update_jump_label) \ scripts/basic/fixdep $(depfile) $@ '$(call make-cmd,cc_o_c)' > \ $(dot-target).tmp; \ rm -f $(depfile); \
@@ -301,13 +311,14 @@ define rule_cc_o_c endef # Built-in and composite module parts -$(obj)/%.o: $(src)/%.c $(recordmcount_source) FORCE +$(obj)/%.o: $(src)/%.c $(recordmcount_source) $(update_jump_label_source) FORCE $(call cmd,force_checksrc) $(call if_changed_rule,cc_o_c) # Single-part modules are special since we need to mark them in $(MODVERDIR) -$(single-used-m): $(obj)/%.o: $(src)/%.c $(recordmcount_source) FORCE +$(single-used-m): $(obj)/%.o: $(src)/%.c $(recordmcount_source) \ + $(update_jump_label_source) FORCE $(call cmd,force_checksrc) $(call if_changed_rule,cc_o_c) @{ echo $(@:.o=.ko); echo $@; } > $(MODVERDIR)/$(@F:.o=.mod)
scripts/update_jump_label.c new file +335 -0
335 lines not on this page · raw message
scripts/update_jump_label.h new file +208 -0
208 lines not on this page · raw message
Signature · 1 line
1.7.8.3
Steven Rostedt
reviewerPATCH 5/5 jump labels/x86: Use etiher 5 byte or 2 byte jumps
In reply to Steven RostedtFrom: Steven Rostedt <[email protected]>
Have the jump labels add a "jmp" in the assembly instead of a default nop. This will cause the assembler to put in either a 2 byte or 5 byte jmp depending on where the target lable is.
Then at compile time, the update_jump_label code will replace the jmps with either 2 or 5 byte nops.
On boot up, the code can be examined to see if the jump label uses either a 2 or 5 byte nop and replace it.
By allowing the jump labels to be 2 bytes, it speeds up the nops, not only 2 byte nops are faster than 5 byte nops, but also because it saves on cache foot print.
text data bss dec hex filename
13403667 3666856 2998272 20068795 13239bb ../nobackup/mxtest/vmlinux-old 13398536 3666856 2998272 20063664 13225b0 ../nobackup/mxtest/vmlinux-new
Converting the current v3.2 trace points saved 5,131 bytes. As more places use jump labels, this will have a bigger savings.
--- arch/x86/Kconfig | 1 + arch/x86/include/asm/jump_label.h | 2 +- arch/x86/kernel/jump_label.c | 86 ++++++++++++++++++++++++++++-------- 3 files changed, 69 insertions(+), 20 deletions(-)
arch/x86/Kconfig +1 -0
@@ -61,6 +61,7 @@ config X86 select HAVE_ARCH_KMEMCHECK select HAVE_USER_RETURN_NOTIFIER select HAVE_ARCH_JUMP_LABEL + select HAVE_BUILD_TIME_JUMP_LABEL select HAVE_TEXT_POKE_SMP select HAVE_GENERIC_HARDIRQS select HAVE_SPARSE_IRQ
arch/x86/include/asm/jump_label.h +1 -1
@@ -19,7 +19,7 @@ static __always_inline bool arch_static_branch(struct jump_label_key *key) { asm goto("1:" - ".byte " __stringify(JUMP_LABEL_INIT_NOP) "\n\t" +"jmp %l[l_yes]\n" ".pushsection __jump_table, \"aw\" \n\t" _ASM_ALIGN"\n\t" _ASM_PTR "1b, %l[l_yes], %c0 \n\t"
arch/x86/kernel/jump_label.c +67 -19
127 lines not on this page · raw message
Signature · 1 line
1.7.8.3
Steven Rostedt
reviewerRe: [PATCH 5/5] jump labels/x86: Use etiher 5 byte or 2 byte jumps
arch/x86/kernel/jump_label.c line 23
Quoted 6 lines · Steven Rostedt
arch/x86/kernel/jump_label.c +5
-0+static unsigned char nop_short[] = {P6_NOP2 }; + +/* These are the nops added at compile time */ +static unsigned char default_nop[] = {JUMP_LABEL_INIT_NOP }; +
Andrew,
I also made the above two into static const. Just so you know ;-)
-- Steve