x86/jump labels: Add the 5 byte or 2 byte jumps
Subsystems (3)
Steven Rostedt
reviewercover x86/jump labels: Add the 5 byte or 2 byte jumps
As I said, I would post the patches that let the jmps used by jump labels be turn to 2 bytes where possible. These are a bit controversial due to the complexity of the update_jump_label code.
These patches are based off of tip's x86/jumplabel code.
But if someone cares to play with it, feel free. I'll push this up to my repo under: tip/perf/jump-label-7 (internally this is my 7th version).
I'll post a patch that does the counting as well as a reply to this post.
Enjoy,
-- Steve
git://git.kernel.org/pub/scm/linux/kernel/git/rostedt/linux-trace.git
tip/perf/jump-label-7
Head SHA1: 2a7235814df0cccda60a366fb8b1e5502055ea4d
Steven Rostedt (2):
jump labels: Add infrastructure to update jump labels at compile time x86/jump labels: Use etiher 5 byte or 2 byte jumps
----
Makefile | 7 + arch/Kconfig | 6 + arch/x86/Kconfig | 1 + arch/x86/include/asm/jump_label.h | 7 +- arch/x86/kernel/jump_label.c | 88 +++++++--- scripts/Makefile | 1 + scripts/Makefile.build | 15 +- scripts/update_jump_label.c | 341 +++++++++++++++++++++++++++++++++++++ scripts/update_jump_label.h | 208 ++++++++++++++++++++++ 9 files changed, 649 insertions(+), 25 deletions(-)
Steven Rostedt
reviewerRFC PATCH 1/2 jump labels: Add infrastructure to update jump labels at compile time
From: 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 | 341 +++++++++++++++++++++++++++++++++++++++++++ scripts/update_jump_label.h | 208 ++++++++++++++++++++++++++ 6 files changed, 576 insertions(+), 2 deletions(-) create mode 100644 scripts/update_jump_label.c create mode 100644 scripts/update_jump_label.h
Makefile +7 -0
@@ -638,6 +638,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
@@ -283,6 +283,12 @@ config HAVE_PERF_USER_STACK_DUMP access to the user stack pointer which is not unified across architectures. +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
@@ -20,6 +20,7 @@ hostprogs-$(CONFIG_ASN1) += asn1_compiler HOSTCFLAGS_sortextable.o = -I$(srctree)/tools/include HOSTCFLAGS_asn1_compiler.o = -I$(srctree)/include +hostprogs-$(BUILD_UPDATE_JUMP_LABEL) += update_jump_label always := $(hostprogs-y) $(hostprogs-m)
scripts/Makefile.build +13 -2
@@ -261,6 +261,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")
@@ -297,6 +306,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); \
@@ -304,13 +314,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 +341 -0
341 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.10.4
Steven Rostedt
reviewerRFC PATCH 2/2 x86/jump labels: 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 | 7 ++- arch/x86/kernel/jump_label.c | 88 +++++++++++++++++++++++++++---------- 3 files changed, 73 insertions(+), 23 deletions(-)
arch/x86/Kconfig +1 -0
@@ -81,6 +81,7 @@ config X86 select HAVE_USER_RETURN_NOTIFIER select ARCH_BINFMT_ELF_RANDOMIZE_PIE select HAVE_ARCH_JUMP_LABEL + select HAVE_BUILD_TIME_JUMP_LABEL select HAVE_TEXT_POKE_SMP select HAVE_GENERIC_HARDIRQS select ARCH_HAS_ATOMIC64_DEC_IF_POSITIVE
arch/x86/include/asm/jump_label.h +6 -1
@@ -10,6 +10,11 @@#define JUMP_LABEL_NOP_SIZE 5 +/* +* The STATIC_KEY_INIT_NOP must match the nops used in +* scripts/update_jump_label.c. Otherwise the boot time checks +* will fail and trigger a BUG() on boot up. +*/ #ifdef CONFIG_X86_64 # define STATIC_KEY_INIT_NOP P6_NOP5_ATOMIC#else
@@ -19,7 +24,7 @@ static __always_inline bool arch_static_branch(struct static_key *key) { asm goto("1:" - ".byte " __stringify(STATIC_KEY_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 +66 -22
129 lines not on this page · raw message
Signature · 1 line
1.7.10.4
Steven Rostedt
reviewerRe: [RFC][PATCH 2/2] x86/jump labels: Use etiher 5 byte or 2 byte jumps
Quoted 19 lines · Steven Rostedt
plain text document attachment (0002-x86-jump-labels-Use-etiher-5-byte-or-2-byte-jumps.patch)
From: 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_labelcode 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.
Quoted 7 lines
text data bss dec hex filename
13403667 3666856 2998272 20068795 13239bb ../nobackup/mxtest/vmlinux-old 13398536 3666856 2998272 20063664 13225b0 ../nobackup/mxtest/vmlinux-newConverting the current v3.2 trace points saved 5,131 bytes. As more places use jump labels, this will have a bigger savings.
FYI, I didn't bother changing the change logs, so these numbers may be totally bogus now.
-- Steve
Steven Rostedt
reviewer[RFC][PATCH 3/2] x86/jump labels: Count and display the short jumps used
In reply to Steven RostedtQuoted 11 lines · Steven Rostedt
As I said, I would post the patches that let the jmps used by jump labels be turn to 2 bytes where possible. These are a bit controversial due to the complexity of the
update_jump_labelcode.These patches are based off of tip's x86/jumplabel code.
But if someone cares to play with it, feel free. I'll push this up to my repo under: tip/perf/jump-label-7 (internally this is my 7th version).
I'll post a patch that does the counting as well as a reply to this post.
Here's the patch that forces the update and counts the number of short jumps vs long jumps. It also outputs the locations of the short jumps.
On another box, using a distro config, I had even better results:
[ 2.352448] short jumps: 193 [ 2.355407] long jumps: 219
arch/x86/kernel/jump_label.c +17 -2
@@ -44,6 +44,9 @@ static void bug_at(unsigned char *ip, inBUG (); } +static int short_nops; +static int long_nops; + static void __jump_label_transform(struct jump_entry *entry, enum jump_label_type type, void *(*poker)(void *, const void *, size_t),
@@ -82,9 +85,14 @@ static void __jump_label_transform(struc*/ if (init) {/* Ignore short nops, we do not change them */ -if (memcmp (ip, nop_short,2 ) ==0 ) +if (memcmp (ip, nop_short,2 ) ==0 ) { + short_nops++; +printk ("short jump at: %pS %p\n" , + (void *)ip, (void *)ip);return ; + } + long_nops++;/* We are initializing from the default nop */ if (unlikely (memcmp (ip, default_nop,5 ) !=0 ))bug_at (ip, __LINE__);
@@ -154,7 +162,7 @@ __init_or_module void arch_jump_label_trif (jlstate ==JL_STATE_START ) {const unsigned char *ideal_nop = ideal_nops[NOP_ATOMIC5 ]; -if (memcmp (ideal_nop, default_nop,5 ) !=0 ) +if (1 ||memcmp (ideal_nop, default_nop,5 ) !=0 ) jlstate =JL_STATE_UPDATE ;else jlstate =JL_STATE_NO_UPDATE ;
@@ -162,4 +170,11 @@ __init_or_module void arch_jump_label_trif (jlstate ==JL_STATE_UPDATE ) __jump_label_transform(entry, type, text_poke_early, 1); } + +static __init int output_jumps (void ) +{ +printk ("short jumps: %d\n" , short_nops); +printk (" long jumps: %d\n" , long_nops); +} +late_initcall (output_jumps);#endif
Re: [RFC][PATCH 3/2] x86/jump labels: Count and display the short jumps used
Quoted 5 lines · Steven Rostedt
On another box, using a distro config, I had even better results:
[ 2.352448] short jumps: 193 [ 2.355407] long jumps: 219
.. well, another way of looking at this is to say that all of this effort saves just 579 bytes.
Yes, maybe some of those bytes are in really hot paths, but the other side of *that* coin is that the 2-vs-5 byte jump doesn't much matter if it's already cached.
So I'd vote for not doing this. If we had some simple way to do the short jumps, I think it would be lovely. Or if we had to parse the ELF files and do instruction rewriting for various other reasons, and the jump rewriting was just one small detail.
But using 576 new lines (the diffstat for your patch 1/2 that adds the infrastructure to do the rewriting) in order to same just about exactly that many bytes in the binary - the effort just doesn't work out, imnsho.
Linus
H. Peter Anvin
reviewerRe: [RFC][PATCH 3/2] x86/jump labels: Count and display the short jumps used
Well we do... both to extract relocations and to sort the exception table. Perhaps we need to merge those kinds of postprocessing tools?
Quoted 26 lines · Linus Torvalds
Quoted 5 lines · Steven Rostedt
On another box, using a distro config, I had even better results:
[ 2.352448] short jumps: 193 [ 2.355407] long jumps: 219.. well, another way of looking at this is to say that all of this effort saves just 579 bytes.
Yes, maybe some of those bytes are in really hot paths, but the other side of *that* coin is that the 2-vs-5 byte jump doesn't much matter if it's already cached.
So I'd vote for not doing this. If we had some simple way to do the short jumps, I think it would be lovely. Or if we had to parse the ELF files and do instruction rewriting for various other reasons, and the jump rewriting was just one small detail.
But using 576 new lines (the diffstat for your patch 1/2 that adds the infrastructure to do the rewriting) in order to same just about exactly that many bytes in the binary - the effort just doesn't work out, imnsho.
Linus
Signature · 1 line
Sent from my mobile phone. Please excuse brevity and lack of formatting.
Re: [RFC][PATCH 3/2] x86/jump labels: Count and display the short jumps used
Quoted 1 line · H. Peter Anvin
Well we do... both to extract relocations and to sort the exception table. Perhaps we need to merge those kinds of postprocessing tools?
If we can do this generically and without adding 500 lines of specialized code, my argument obviously goes away.
Exactly where the "unnecessarily complex" line is is obviously a matter of taste.
Linus
Steven Rostedt
reviewerRe: [RFC][PATCH 3/2] x86/jump labels: Count and display the short jumps used
Quoted 5 lines · Linus Torvalds
Quoted 1 line · H. Peter Anvin
Well we do... both to extract relocations and to sort the exception table. Perhaps we need to merge those kinds of postprocessing tools?
If we can do this generically and without adding 500 lines of specialized code, my argument obviously goes away.
And the code that does this work is actually in the kernel today, although, seldom used.
The recordmount.[ch] can do this modification. It's main purpose was to create a table of mcount call locations that ftrace can use to convert to nops on boot. As it only converts white-listed sections (doesn't convert things like .init or .exit, as there exists no way today to generically know when they are removed), it only creates the pointers to those sections that ftrace will touch.
Those sections that do not get converted, can have a call to mcount that is not changed to a nop. Most places this is not the case, because things like __init have "notrace" defined to keep those functions from being profiled. But if another section is added that ftrace does not know about, it will get a call to mcount and cause a bit of a slow down. Note, mcount itself looks like:
mcount:
retq
To handle these missed cases, the recordmcount.c was modified to convert the call to mcount into a nop. There's a flag you can set to have this warn on locations that it converts:
make RECORDMCOUNT_WARN=1
It will give a warning at the locations that it converted, which should then be white-listed or have notrace added to it.
This is the code I based the update_jump_labels from. With a bit of work, all these ELF parsers could probably be consolidated, and this addition may not be that hard or complex to add.
-- Steve
Jason Baron
maintainerRe: [RFC][PATCH 3/2] x86/jump labels: Count and display the short jumps used
In reply to Linus TorvaldsQuoted 17 lines · Linus Torvalds
Quoted 4 lines · Steven Rostedt
On another box, using a distro config, I had even better results:
[ 2.352448] short jumps: 193 [ 2.355407] long jumps: 219.. well, another way of looking at this is to say that all of this effort saves just 579 bytes.
Yes, maybe some of those bytes are in really hot paths, but the other side of *that* coin is that the 2-vs-5 byte jump doesn't much matter if it's already cached.
So I'd vote for not doing this. If we had some simple way to do the short jumps, I think it would be lovely. Or if we had to parse the ELF files and do instruction rewriting for various other reasons, and the jump rewriting was just one small detail.
Quoted 6 lines
But using 576 new lines (the diffstat for your patch 1/2 that adds the infrastructure to do the rewriting) in order to same just about exactly that many bytes in the binary - the effort just doesn't work out, imnsho.
Linus
The whole point of the thread started with wanting to move the default 'disabled' branch further out-of-line. We could get there with better compiler support for the 'cold' label attribute. Thus, in theory the whole 2-byte jmp is just an intermediate step. (Yeah, I know that support doesn't seem to be happening anytime soon...)
Thanks,
-Jason
Steven Rostedt
reviewerRe: [RFC][PATCH 3/2] x86/jump labels: Count and display the short jumps used
Quoted 6 lines · Jason Baron
The whole point of the thread started with wanting to move the default 'disabled' branch further out-of-line. We could get there with better compiler support for the 'cold' label attribute. Thus, in theory the whole 2-byte jmp is just an intermediate step. (Yeah, I know that support doesn't seem to be happening anytime soon...)
Actually, Ideally, we would move the bulk of the tracing code out of line, but we can have the jump to the tracing code still in line, and the nop jump to it.
[ hot path]
jmp trace / nop
1:
[...]
trace:
jmp trace_main_code
jmp 1b
Then that jmp trace can still be a 2 byte op.
-- Steve
Re: [RFC][PATCH 3/2] x86/jump labels: Count and display the short jumps used
In reply to Jason BaronQuoted 3 lines · Jason Baron
The whole point of the thread started with wanting to move the default 'disabled' branch further out-of-line.
Yeah, but I always disagreed with that.
Putting the unusual code out-of-line (as in "at the end of the function") is a good idea, but putting it *far* out of line (as in "in a different section") likely makes little sense.
Now, the tracing code is admittedly specialized enough that we could have some "really cold" attribute and move it to that kind of "even further away" model, but most of the other uses of the static keys are not necessarily of the kind where the non-default case is completely or utterly unlikely - they want to use the static keys not because some codepath is basically never taken, but because the code-path is so critical that loading and testing a value from memory is considered to be excessive for when the feature is turned off (ie scheduler statistics etc).
So the code may not even be all that cold - some people may well run with statistics enabled all the time - it's just that the non-enabled case really *really* doesn't want to have the overhead of even bothering to test for this event.
So I really think that a short jump would be a good thing, it's just that I don't think it's *so* important that it's worth having several hundreds of lines of code to support it.
Linus
Jason Baron
maintainerRe: [RFC][PATCH 3/2] x86/jump labels: Count and display the short jumps used
Quoted 19 lines · Linus Torvalds
Quoted 2 lines · Jason Baron
The whole point of the thread started with wanting to move the default 'disabled' branch further out-of-line.
Yeah, but I always disagreed with that.
Putting the unusual code out-of-line (as in "at the end of the function") is a good idea, but putting it *far* out of line (as in "in a different section") likely makes little sense.
Now, the tracing code is admittedly specialized enough that we could have some "really cold" attribute and move it to that kind of "even further away" model, but most of the other uses of the static keys are not necessarily of the kind where the non-default case is completely or utterly unlikely - they want to use the static keys not because some codepath is basically never taken, but because the code-path is so critical that loading and testing a value from memory is considered to be excessive for when the feature is turned off (ie scheduler statistics etc).
Quoted 6 lines
So the code may not even be all that cold - some people may well run with statistics enabled all the time - it's just that the non-enabled case really *really* doesn't want to have the overhead of even bothering to test for this event.
ok - I can see 2 variants here as you mentioned:
1) 'Unbiased' - we want to treat both branches equally but don't want the load/test/jmp sequence. For things like the scheduler stats.
2) 'Biased' - where the unlikely path is moved completely out-of-line. And we have a strong 'bias' to optimize the default path.
If we can get the completely out-of-line thing working, we could make this distinction.
Thanks,
-Jason
Re: [RFC][PATCH 3/2] x86/jump labels: Count and display the short jumps used
Quoted 8 lines · Jason Baron
ok - I can see 2 variants here as you mentioned:
1) 'Unbiased' - we want to treat both branches equally but don't want the load/test/jmp sequence. For things like the scheduler stats.
2) 'Biased' - where the unlikely path is moved completely out-of-line. And we have a strong 'bias' to optimize the default path.
Actually, personally I'd much rather see *three* cases, and for the static keys usually only two are relevant:
- truly unbiased: a normal conditional that we don't really know
which one is the likely or unlikely path
This is likely *not* the normal case for a static key, since almost
always we have a lightweight case, and a heavy one. Most of the static keys are about things like statistics, which are inherently more expensive than not having statistics, so they aren't really "unbiased". There's a bias introduced by one side being more costly than the other. You want the light (non-statistics) case to be a fall-through, while the heavy case is going to be much more expensive and update global variables etc.
- "slightly biased". The normal kind of "unlikely()", where you want
to have one side of the branch be the fall-through and common case that doesn't interrupt the I$ prefetch etc. This is the likely case for most static key use, I think.
- "strongly biased": a "very unlikely" kind of thing, which is either
a major exception case (ie an assertion on BUG_ON() or other basically fatal thing), or is something like the tracing code that is uncommon and where we want basically minimal impact when it is not enabled, but when it's on, the impact is so big that the conditional branch no longer even matters.
The first one really doesn't care which way a branch goes. The second one prefers a fall-through case for the common case. And the third one is basically a "get this code away from me".
Both of the biased cases *might* also want things like "save register state in the unlikely path so that the *likely* path doesn't have to". Think things like "it's a leaf function, and the likely path doesn't need any temporaries, but the unlikely path ends up doing function calls and needs a stack frame". If the compiler can make likely path avoid the stack frame generation and be straight-line, that would be really nice.
Linus
Re: [RFC][PATCH 3/2] x86/jump labels: Count and display the short jumps used
Quoted 8 lines · Linus Torvalds
Both of the biased cases *might* also want things like "save register state in the unlikely path so that the *likely* path doesn't have to". Think things like "it's a leaf function, and the likely path doesn't need any temporaries, but the unlikely path ends up doing function calls and needs a stack frame". If the compiler can make likely path avoid the stack frame generation and be straight-line, that would be really nice.
This inspired me to see what happens when you call an __attribute__((noreturn)) function. The results are sad:
http://gcc.gnu.org/bugzilla/show_bug.cgi?id=10837
I think the explanations for why that bug is WONTFIX are bogus. And unless gcc fixes that, trying to get efficient code that intentionally jumps and never returns seems hard (at least without sticking the call/jump into inline assembly).
--Andy