* Kernel broken on processors without performance counters @ 2015-07-08 15:17 Mikulas Patocka 2015-07-08 16:07 ` Peter Zijlstra 2015-07-14 9:35 ` Borislav Petkov 0 siblings, 2 replies; 54+ messages in thread From: Mikulas Patocka @ 2015-07-08 15:17 UTC (permalink / raw) To: Andy Lutomirski, Peter Zijlstra Cc: Paul Mackerras, Arnaldo Carvalho de Melo, Kees Cook, Andrea Arcangeli, Vince Weaver, hillf.zj, Valdis Kletnieks, linux-kernel Hi I found out that the patch a66734297f78707ce39d756b656bfae861d53f62 breaks the kernel on processors without performance counters, such as AMD K6-3. Reverting the patch fixes the problem. The static key rdpmc_always_available somehow gets set (I couldn't really find out what is setting it, the function set_attr_rdpmc is not executed), cr4_set_bits(X86_CR4_PCE) is executed and that results in a crash on boot when attempting to execute init, because the proecssor doesn't support that bit in CR4. Mikulas ^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: Kernel broken on processors without performance counters 2015-07-08 15:17 Kernel broken on processors without performance counters Mikulas Patocka @ 2015-07-08 16:07 ` Peter Zijlstra 2015-07-08 16:54 ` Mikulas Patocka 2015-07-08 17:37 ` Kernel broken on processors without performance counters Andy Lutomirski 2015-07-14 9:35 ` Borislav Petkov 1 sibling, 2 replies; 54+ messages in thread From: Peter Zijlstra @ 2015-07-08 16:07 UTC (permalink / raw) To: Mikulas Patocka Cc: Andy Lutomirski, Paul Mackerras, Arnaldo Carvalho de Melo, Kees Cook, Andrea Arcangeli, Vince Weaver, hillf.zj, Valdis Kletnieks, linux-kernel On Wed, Jul 08, 2015 at 11:17:38AM -0400, Mikulas Patocka wrote: > Hi > > I found out that the patch a66734297f78707ce39d756b656bfae861d53f62 breaks > the kernel on processors without performance counters, such as AMD K6-3. > Reverting the patch fixes the problem. > > The static key rdpmc_always_available somehow gets set (I couldn't really > find out what is setting it, the function set_attr_rdpmc is not executed), > cr4_set_bits(X86_CR4_PCE) is executed and that results in a crash on boot > when attempting to execute init, because the proecssor doesn't support > that bit in CR4. Urgh, the static key trainwreck bites again. One is not supposed to mix static_key_true() and STATIC_KEY_INIT_FALSE. Does this make it go again? --- arch/x86/include/asm/mmu_context.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/x86/include/asm/mmu_context.h b/arch/x86/include/asm/mmu_context.h index 5e8daee7c5c9..804a3a6030ca 100644 --- a/arch/x86/include/asm/mmu_context.h +++ b/arch/x86/include/asm/mmu_context.h @@ -23,7 +23,7 @@ extern struct static_key rdpmc_always_available; static inline void load_mm_cr4(struct mm_struct *mm) { - if (static_key_true(&rdpmc_always_available) || + if (static_key_false(&rdpmc_always_available) || atomic_read(&mm->context.perf_rdpmc_allowed)) cr4_set_bits(X86_CR4_PCE); else ^ permalink raw reply related [flat|nested] 54+ messages in thread
* Re: Kernel broken on processors without performance counters 2015-07-08 16:07 ` Peter Zijlstra @ 2015-07-08 16:54 ` Mikulas Patocka 2015-07-09 17:23 ` [PATCH] x86: Fix static_key in load_mm_cr4() Peter Zijlstra 2015-07-08 17:37 ` Kernel broken on processors without performance counters Andy Lutomirski 1 sibling, 1 reply; 54+ messages in thread From: Mikulas Patocka @ 2015-07-08 16:54 UTC (permalink / raw) To: Peter Zijlstra Cc: Andy Lutomirski, Paul Mackerras, Arnaldo Carvalho de Melo, Kees Cook, Andrea Arcangeli, Vince Weaver, hillf.zj, Valdis Kletnieks, linux-kernel On Wed, 8 Jul 2015, Peter Zijlstra wrote: > On Wed, Jul 08, 2015 at 11:17:38AM -0400, Mikulas Patocka wrote: > > Hi > > > > I found out that the patch a66734297f78707ce39d756b656bfae861d53f62 breaks > > the kernel on processors without performance counters, such as AMD K6-3. > > Reverting the patch fixes the problem. > > > > The static key rdpmc_always_available somehow gets set (I couldn't really > > find out what is setting it, the function set_attr_rdpmc is not executed), > > cr4_set_bits(X86_CR4_PCE) is executed and that results in a crash on boot > > when attempting to execute init, because the proecssor doesn't support > > that bit in CR4. > > Urgh, the static key trainwreck bites again. > > One is not supposed to mix static_key_true() and STATIC_KEY_INIT_FALSE. > > Does this make it go again? Yes, this patch fixes the problem. Mikulas > --- > arch/x86/include/asm/mmu_context.h | 2 +- > 1 file changed, 1 insertion(+), 1 deletion(-) > > diff --git a/arch/x86/include/asm/mmu_context.h b/arch/x86/include/asm/mmu_context.h > index 5e8daee7c5c9..804a3a6030ca 100644 > --- a/arch/x86/include/asm/mmu_context.h > +++ b/arch/x86/include/asm/mmu_context.h > @@ -23,7 +23,7 @@ extern struct static_key rdpmc_always_available; > > static inline void load_mm_cr4(struct mm_struct *mm) > { > - if (static_key_true(&rdpmc_always_available) || > + if (static_key_false(&rdpmc_always_available) || > atomic_read(&mm->context.perf_rdpmc_allowed)) > cr4_set_bits(X86_CR4_PCE); > else > ^ permalink raw reply [flat|nested] 54+ messages in thread
* [PATCH] x86: Fix static_key in load_mm_cr4() 2015-07-08 16:54 ` Mikulas Patocka @ 2015-07-09 17:23 ` Peter Zijlstra 2015-07-09 19:11 ` Mikulas Patocka 2015-07-10 8:27 ` [tip:perf/urgent] x86, perf: Fix static_key bug " tip-bot for Peter Zijlstra 0 siblings, 2 replies; 54+ messages in thread From: Peter Zijlstra @ 2015-07-09 17:23 UTC (permalink / raw) To: Mikulas Patocka, Ingo Molnar Cc: Andy Lutomirski, Paul Mackerras, Arnaldo Carvalho de Melo, Kees Cook, Andrea Arcangeli, Vince Weaver, hillf.zj, Valdis Kletnieks, linux-kernel Mikulas reported his K6-3 not booting. This is because the static_key confusion struck and bit Andy, this wants to be static_key_false(). Fixes: a66734297f78 ("perf/x86: Add /sys/devices/cpu/rdpmc=2 to allow rdpmc for all tasks") Cc: Andy Lutomirski <luto@amacapital.net> Reported-by: Mikulas Patocka <mpatocka@redhat.com> Tested-by: Mikulas Patocka <mpatocka@redhat.com> Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org> --- arch/x86/include/asm/mmu_context.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/x86/include/asm/mmu_context.h b/arch/x86/include/asm/mmu_context.h index 5e8daee7c5c9..804a3a6030ca 100644 --- a/arch/x86/include/asm/mmu_context.h +++ b/arch/x86/include/asm/mmu_context.h @@ -23,7 +23,7 @@ extern struct static_key rdpmc_always_available; static inline void load_mm_cr4(struct mm_struct *mm) { - if (static_key_true(&rdpmc_always_available) || + if (static_key_false(&rdpmc_always_available) || atomic_read(&mm->context.perf_rdpmc_allowed)) cr4_set_bits(X86_CR4_PCE); else ^ permalink raw reply related [flat|nested] 54+ messages in thread
* Re: [PATCH] x86: Fix static_key in load_mm_cr4() 2015-07-09 17:23 ` [PATCH] x86: Fix static_key in load_mm_cr4() Peter Zijlstra @ 2015-07-09 19:11 ` Mikulas Patocka 2015-07-10 8:27 ` [tip:perf/urgent] x86, perf: Fix static_key bug " tip-bot for Peter Zijlstra 1 sibling, 0 replies; 54+ messages in thread From: Mikulas Patocka @ 2015-07-09 19:11 UTC (permalink / raw) To: Peter Zijlstra Cc: Ingo Molnar, Andy Lutomirski, Paul Mackerras, Arnaldo Carvalho de Melo, Kees Cook, Andrea Arcangeli, Vince Weaver, hillf.zj, Valdis Kletnieks, linux-kernel On Thu, 9 Jul 2015, Peter Zijlstra wrote: > > Mikulas reported his K6-3 not booting. This is because the static_key > confusion struck and bit Andy, this wants to be static_key_false(). > > Fixes: a66734297f78 ("perf/x86: Add /sys/devices/cpu/rdpmc=2 to allow rdpmc for all tasks") > Cc: Andy Lutomirski <luto@amacapital.net> > Reported-by: Mikulas Patocka <mpatocka@redhat.com> > Tested-by: Mikulas Patocka <mpatocka@redhat.com> > Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org> You should also add Cc: stable@vger.kernel.org # v4.0 so that it will be backported to 4.0 and 4.1. > --- > arch/x86/include/asm/mmu_context.h | 2 +- > 1 file changed, 1 insertion(+), 1 deletion(-) > > diff --git a/arch/x86/include/asm/mmu_context.h b/arch/x86/include/asm/mmu_context.h > index 5e8daee7c5c9..804a3a6030ca 100644 > --- a/arch/x86/include/asm/mmu_context.h > +++ b/arch/x86/include/asm/mmu_context.h > @@ -23,7 +23,7 @@ extern struct static_key rdpmc_always_available; > > static inline void load_mm_cr4(struct mm_struct *mm) > { > - if (static_key_true(&rdpmc_always_available) || > + if (static_key_false(&rdpmc_always_available) || > atomic_read(&mm->context.perf_rdpmc_allowed)) > cr4_set_bits(X86_CR4_PCE); > else > ^ permalink raw reply [flat|nested] 54+ messages in thread
* [tip:perf/urgent] x86, perf: Fix static_key bug in load_mm_cr4() 2015-07-09 17:23 ` [PATCH] x86: Fix static_key in load_mm_cr4() Peter Zijlstra 2015-07-09 19:11 ` Mikulas Patocka @ 2015-07-10 8:27 ` tip-bot for Peter Zijlstra 1 sibling, 0 replies; 54+ messages in thread From: tip-bot for Peter Zijlstra @ 2015-07-10 8:27 UTC (permalink / raw) To: linux-tip-commits Cc: linux-kernel, keescook, mpatocka, vince, acme, paulus, hpa, hillf.zj, Valdis.Kletnieks, stable, aarcange, brgerst, luto, bp, mingo, dvlasenk, tglx, peterz, torvalds Commit-ID: a833581e372a4adae2319d8dc379493edbc444e9 Gitweb: http://git.kernel.org/tip/a833581e372a4adae2319d8dc379493edbc444e9 Author: Peter Zijlstra <peterz@infradead.org> AuthorDate: Thu, 9 Jul 2015 19:23:38 +0200 Committer: Ingo Molnar <mingo@kernel.org> CommitDate: Fri, 10 Jul 2015 10:24:38 +0200 x86, perf: Fix static_key bug in load_mm_cr4() Mikulas reported his K6-3 not booting. This is because the static_key API confusion struck and bit Andy, this wants to be static_key_false(). Reported-by: Mikulas Patocka <mpatocka@redhat.com> Tested-by: Mikulas Patocka <mpatocka@redhat.com> Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org> Cc: <stable@vger.kernel.org> Cc: Andrea Arcangeli <aarcange@redhat.com> Cc: Andy Lutomirski <luto@amacapital.net> Cc: Arnaldo Carvalho de Melo <acme@kernel.org> Cc: Borislav Petkov <bp@alien8.de> Cc: Brian Gerst <brgerst@gmail.com> Cc: Denys Vlasenko <dvlasenk@redhat.com> Cc: H. Peter Anvin <hpa@zytor.com> Cc: Kees Cook <keescook@chromium.org> Cc: Linus Torvalds <torvalds@linux-foundation.org> Cc: Paul Mackerras <paulus@samba.org> Cc: Peter Zijlstra <peterz@infradead.org> Cc: Thomas Gleixner <tglx@linutronix.de> Cc: Valdis Kletnieks <Valdis.Kletnieks@vt.edu> Cc: Vince Weaver <vince@deater.net> Cc: hillf.zj <hillf.zj@alibaba-inc.com> Fixes: a66734297f78 ("perf/x86: Add /sys/devices/cpu/rdpmc=2 to allow rdpmc for all tasks") Link: http://lkml.kernel.org/r/20150709172338.GC19282@twins.programming.kicks-ass.net Signed-off-by: Ingo Molnar <mingo@kernel.org> --- arch/x86/include/asm/mmu_context.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/x86/include/asm/mmu_context.h b/arch/x86/include/asm/mmu_context.h index 5e8daee..804a3a6 100644 --- a/arch/x86/include/asm/mmu_context.h +++ b/arch/x86/include/asm/mmu_context.h @@ -23,7 +23,7 @@ extern struct static_key rdpmc_always_available; static inline void load_mm_cr4(struct mm_struct *mm) { - if (static_key_true(&rdpmc_always_available) || + if (static_key_false(&rdpmc_always_available) || atomic_read(&mm->context.perf_rdpmc_allowed)) cr4_set_bits(X86_CR4_PCE); else ^ permalink raw reply related [flat|nested] 54+ messages in thread
* Re: Kernel broken on processors without performance counters 2015-07-08 16:07 ` Peter Zijlstra 2015-07-08 16:54 ` Mikulas Patocka @ 2015-07-08 17:37 ` Andy Lutomirski 2015-07-08 20:04 ` Jason Baron 1 sibling, 1 reply; 54+ messages in thread From: Andy Lutomirski @ 2015-07-08 17:37 UTC (permalink / raw) To: Peter Zijlstra Cc: Mikulas Patocka, Paul Mackerras, Arnaldo Carvalho de Melo, Kees Cook, Andrea Arcangeli, Vince Weaver, hillf.zj, Valdis Kletnieks, linux-kernel@vger.kernel.org On Wed, Jul 8, 2015 at 9:07 AM, Peter Zijlstra <peterz@infradead.org> wrote: > On Wed, Jul 08, 2015 at 11:17:38AM -0400, Mikulas Patocka wrote: >> Hi >> >> I found out that the patch a66734297f78707ce39d756b656bfae861d53f62 breaks >> the kernel on processors without performance counters, such as AMD K6-3. >> Reverting the patch fixes the problem. >> >> The static key rdpmc_always_available somehow gets set (I couldn't really >> find out what is setting it, the function set_attr_rdpmc is not executed), >> cr4_set_bits(X86_CR4_PCE) is executed and that results in a crash on boot >> when attempting to execute init, because the proecssor doesn't support >> that bit in CR4. > > Urgh, the static key trainwreck bites again. > > One is not supposed to mix static_key_true() and STATIC_KEY_INIT_FALSE. > > Does this make it go again? > > --- > arch/x86/include/asm/mmu_context.h | 2 +- > 1 file changed, 1 insertion(+), 1 deletion(-) > > diff --git a/arch/x86/include/asm/mmu_context.h b/arch/x86/include/asm/mmu_context.h > index 5e8daee7c5c9..804a3a6030ca 100644 > --- a/arch/x86/include/asm/mmu_context.h > +++ b/arch/x86/include/asm/mmu_context.h > @@ -23,7 +23,7 @@ extern struct static_key rdpmc_always_available; > > static inline void load_mm_cr4(struct mm_struct *mm) > { > - if (static_key_true(&rdpmc_always_available) || > + if (static_key_false(&rdpmc_always_available) || In what universe is "static_key_false" a reasonable name for a function that returns true if a static key is true? Can we rename that function? And could we maybe make static keys type safe? I.e. there would be a type that starts out true and a type that starts out false. --Andy ^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: Kernel broken on processors without performance counters 2015-07-08 17:37 ` Kernel broken on processors without performance counters Andy Lutomirski @ 2015-07-08 20:04 ` Jason Baron 2015-07-09 0:36 ` Andy Lutomirski 2015-07-09 17:11 ` Peter Zijlstra 0 siblings, 2 replies; 54+ messages in thread From: Jason Baron @ 2015-07-08 20:04 UTC (permalink / raw) To: Andy Lutomirski, Peter Zijlstra Cc: Mikulas Patocka, Paul Mackerras, Arnaldo Carvalho de Melo, Kees Cook, Andrea Arcangeli, Vince Weaver, hillf.zj, Valdis Kletnieks, linux-kernel@vger.kernel.org On 07/08/2015 01:37 PM, Andy Lutomirski wrote: > On Wed, Jul 8, 2015 at 9:07 AM, Peter Zijlstra <peterz@infradead.org> wrote: >> On Wed, Jul 08, 2015 at 11:17:38AM -0400, Mikulas Patocka wrote: >>> Hi >>> >>> I found out that the patch a66734297f78707ce39d756b656bfae861d53f62 breaks >>> the kernel on processors without performance counters, such as AMD K6-3. >>> Reverting the patch fixes the problem. >>> >>> The static key rdpmc_always_available somehow gets set (I couldn't really >>> find out what is setting it, the function set_attr_rdpmc is not executed), >>> cr4_set_bits(X86_CR4_PCE) is executed and that results in a crash on boot >>> when attempting to execute init, because the proecssor doesn't support >>> that bit in CR4. >> Urgh, the static key trainwreck bites again. >> >> One is not supposed to mix static_key_true() and STATIC_KEY_INIT_FALSE. >> >> Does this make it go again? >> >> --- >> arch/x86/include/asm/mmu_context.h | 2 +- >> 1 file changed, 1 insertion(+), 1 deletion(-) >> >> diff --git a/arch/x86/include/asm/mmu_context.h b/arch/x86/include/asm/mmu_context.h >> index 5e8daee7c5c9..804a3a6030ca 100644 >> --- a/arch/x86/include/asm/mmu_context.h >> +++ b/arch/x86/include/asm/mmu_context.h >> @@ -23,7 +23,7 @@ extern struct static_key rdpmc_always_available; >> >> static inline void load_mm_cr4(struct mm_struct *mm) >> { >> - if (static_key_true(&rdpmc_always_available) || >> + if (static_key_false(&rdpmc_always_available) || > In what universe is "static_key_false" a reasonable name for a > function that returns true if a static key is true? > > Can we rename that function? And could we maybe make static keys type > safe? I.e. there would be a type that starts out true and a type that > starts out false. So the 'static_key_false' is really branch is initially false. We had a naming discussion before, but if ppl think its confusing, 'static_key_init_false', or 'static_key_default_false' might be better, or other ideas.... I agree its confusing. In terms of getting the type to match so we don't have these mismatches, I think we could introduce 'struct static_key_false' and 'struct static_key_true' with proper initializers. However, 'static_key_slow_inc()/dec()' would also have to add the true/false modifier. Or maybe we do: struct static_key_false { struct static_key key; } random_key; and then the 'static_key_sloc_inc()/dec()' would just take a &random_key.key.... If we were to change this, I don't think it would be too hard to introduce the new API, convert subsystems over time and then drop the old one. Thanks, -Jason ^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: Kernel broken on processors without performance counters 2015-07-08 20:04 ` Jason Baron @ 2015-07-09 0:36 ` Andy Lutomirski 2015-07-10 14:13 ` Peter Zijlstra 2015-07-09 17:11 ` Peter Zijlstra 1 sibling, 1 reply; 54+ messages in thread From: Andy Lutomirski @ 2015-07-09 0:36 UTC (permalink / raw) To: Jason Baron Cc: Peter Zijlstra, Mikulas Patocka, Paul Mackerras, Arnaldo Carvalho de Melo, Kees Cook, Andrea Arcangeli, Vince Weaver, hillf.zj, Valdis Kletnieks, linux-kernel@vger.kernel.org On Wed, Jul 8, 2015 at 1:04 PM, Jason Baron <jasonbaron0@gmail.com> wrote: > On 07/08/2015 01:37 PM, Andy Lutomirski wrote: >> On Wed, Jul 8, 2015 at 9:07 AM, Peter Zijlstra <peterz@infradead.org> wrote: >>> On Wed, Jul 08, 2015 at 11:17:38AM -0400, Mikulas Patocka wrote: >>>> Hi >>>> >>>> I found out that the patch a66734297f78707ce39d756b656bfae861d53f62 breaks >>>> the kernel on processors without performance counters, such as AMD K6-3. >>>> Reverting the patch fixes the problem. >>>> >>>> The static key rdpmc_always_available somehow gets set (I couldn't really >>>> find out what is setting it, the function set_attr_rdpmc is not executed), >>>> cr4_set_bits(X86_CR4_PCE) is executed and that results in a crash on boot >>>> when attempting to execute init, because the proecssor doesn't support >>>> that bit in CR4. >>> Urgh, the static key trainwreck bites again. >>> >>> One is not supposed to mix static_key_true() and STATIC_KEY_INIT_FALSE. >>> >>> Does this make it go again? >>> >>> --- >>> arch/x86/include/asm/mmu_context.h | 2 +- >>> 1 file changed, 1 insertion(+), 1 deletion(-) >>> >>> diff --git a/arch/x86/include/asm/mmu_context.h b/arch/x86/include/asm/mmu_context.h >>> index 5e8daee7c5c9..804a3a6030ca 100644 >>> --- a/arch/x86/include/asm/mmu_context.h >>> +++ b/arch/x86/include/asm/mmu_context.h >>> @@ -23,7 +23,7 @@ extern struct static_key rdpmc_always_available; >>> >>> static inline void load_mm_cr4(struct mm_struct *mm) >>> { >>> - if (static_key_true(&rdpmc_always_available) || >>> + if (static_key_false(&rdpmc_always_available) || >> In what universe is "static_key_false" a reasonable name for a >> function that returns true if a static key is true? >> >> Can we rename that function? And could we maybe make static keys type >> safe? I.e. there would be a type that starts out true and a type that >> starts out false. > > So the 'static_key_false' is really branch is initially false. We had > a naming discussion before, but if ppl think its confusing, > 'static_key_init_false', or 'static_key_default_false' might be better, > or other ideas.... I agree its confusing. I think the current naming is almost maximally bad. The naming would be less critical if it were typesafe, though. > > In terms of getting the type to match so we don't have these > mismatches, I think we could introduce 'struct static_key_false' > and 'struct static_key_true' with proper initializers. However, > 'static_key_slow_inc()/dec()' would also have to add the > true/false modifier. I think that would be okay. > Or maybe we do: > > struct static_key_false { > struct static_key key; > } random_key; > > and then the 'static_key_sloc_inc()/dec()' would just take > a &random_key.key.... That would be okay, too. Or it could be a macro that has the same effect. > > If we were to change this, I don't think it would be too hard to > introduce the new API, convert subsystems over time and then > drop the old one. And we might discover a bug or three while doing it :) --Andy ^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: Kernel broken on processors without performance counters 2015-07-09 0:36 ` Andy Lutomirski @ 2015-07-10 14:13 ` Peter Zijlstra 2015-07-10 15:29 ` Jason Baron 2015-07-21 8:21 ` Peter Zijlstra 0 siblings, 2 replies; 54+ messages in thread From: Peter Zijlstra @ 2015-07-10 14:13 UTC (permalink / raw) To: Andy Lutomirski Cc: Jason Baron, Mikulas Patocka, Paul Mackerras, Arnaldo Carvalho de Melo, Kees Cook, Andrea Arcangeli, Vince Weaver, hillf.zj, Valdis Kletnieks, linux-kernel@vger.kernel.org On Wed, Jul 08, 2015 at 05:36:43PM -0700, Andy Lutomirski wrote: > >> In what universe is "static_key_false" a reasonable name for a > >> function that returns true if a static key is true? > I think the current naming is almost maximally bad. The naming would > be less critical if it were typesafe, though. How about something like so on top? It will allow us to slowly migrate existing and new users over to the hopefully saner interface? --- include/linux/jump_label.h | 67 +++++++++++++++++++++++++++++++++++++++++++++- kernel/sched/core.c | 18 ++++++------- 2 files changed, 74 insertions(+), 11 deletions(-) diff --git a/include/linux/jump_label.h b/include/linux/jump_label.h index f4de473f226b..98ed3c2ec78d 100644 --- a/include/linux/jump_label.h +++ b/include/linux/jump_label.h @@ -213,6 +213,71 @@ static inline bool static_key_enabled(struct static_key *key) return static_key_count(key) > 0; } -#endif /* _LINUX_JUMP_LABEL_H */ +static inline void static_key_enable(struct static_key *key) +{ + int count = static_key_count(key); + + WARN_ON_ONCE(count < 0 || count > 1); + + if (!count) + static_key_slow_inc(key); +} + +static inline void static_key_disable(struct static_key *key) +{ + int count = static_key_count(key); + + WARN_ON_ONCE(count < 0 || count > 1); + + if (count) + static_key_slow_dec(key); +} + +/* -------------------------------------------------------------------------- */ + +/* + * likely -- default enabled, puts the branch body in-line + */ + +struct static_likely_key { + struct static_key key; +}; + +#define STATIC_LIKELY_KEY_INIT (struct static_likely_key){ .key = STATIC_KEY_INIT_TRUE, } + +static inline bool static_likely_branch(struct static_likely_key *key) +{ + return static_key_true(&key->key); +} + +/* + * unlikely -- default disabled, puts the branch body out-of-line + */ + +struct static_unlikely_key { + struct static_key key; +}; + +#define STATIC_UNLIKELY_KEY_INIT (struct static_unlikely_key){ .key = STATIC_KEY_INIT_FALSE, } + +static inline bool static_unlikely_branch(struct static_unlikely_key *key) +{ + return static_key_false(&key->key); +} + +/* + * Advanced usage; refcount, branch is enabled when: count != 0 + */ + +#define static_branch_inc(_k) static_key_slow_inc(&(_k)->key) +#define static_branch_dec(_k) static_key_slow_dec(&(_k)->key) + +/* + * Normal usage; boolean enable/disable. + */ + +#define static_branch_enable(_k) static_key_enable(&(_k)->key) +#define static_branch_disable(_k) static_key_disable(&(_k)->key) #endif /* __ASSEMBLY__ */ +#endif /* _LINUX_JUMP_LABEL_H */ diff --git a/kernel/sched/core.c b/kernel/sched/core.c index 78b4bad10081..22ba92297375 100644 --- a/kernel/sched/core.c +++ b/kernel/sched/core.c @@ -164,14 +164,12 @@ struct static_key sched_feat_keys[__SCHED_FEAT_NR] = { static void sched_feat_disable(int i) { - if (static_key_enabled(&sched_feat_keys[i])) - static_key_slow_dec(&sched_feat_keys[i]); + static_key_enable(&sched_feat_keys[i]); } static void sched_feat_enable(int i) { - if (!static_key_enabled(&sched_feat_keys[i])) - static_key_slow_inc(&sched_feat_keys[i]); + static_key_disable(&sched_feat_keys[i]); } #else static void sched_feat_disable(int i) { }; @@ -2318,17 +2316,17 @@ void wake_up_new_task(struct task_struct *p) #ifdef CONFIG_PREEMPT_NOTIFIERS -static struct static_key preempt_notifier_key = STATIC_KEY_INIT_FALSE; +static struct static_unlikely_key preempt_notifier_key = STATIC_UNLIKELY_KEY_INIT; void preempt_notifier_inc(void) { - static_key_slow_inc(&preempt_notifier_key); + static_branch_inc(&preempt_notifier_key); } EXPORT_SYMBOL_GPL(preempt_notifier_inc); void preempt_notifier_dec(void) { - static_key_slow_dec(&preempt_notifier_key); + static_branch_dec(&preempt_notifier_key); } EXPORT_SYMBOL_GPL(preempt_notifier_dec); @@ -2338,7 +2336,7 @@ EXPORT_SYMBOL_GPL(preempt_notifier_dec); */ void preempt_notifier_register(struct preempt_notifier *notifier) { - if (!static_key_false(&preempt_notifier_key)) + if (!static_unlikely_branch(&preempt_notifier_key)) WARN(1, "registering preempt_notifier while notifiers disabled\n"); hlist_add_head(¬ifier->link, ¤t->preempt_notifiers); @@ -2367,7 +2365,7 @@ static void __fire_sched_in_preempt_notifiers(struct task_struct *curr) static __always_inline void fire_sched_in_preempt_notifiers(struct task_struct *curr) { - if (static_key_false(&preempt_notifier_key)) + if (static_unlikely_branch(&preempt_notifier_key)) __fire_sched_in_preempt_notifiers(curr); } @@ -2385,7 +2383,7 @@ static __always_inline void fire_sched_out_preempt_notifiers(struct task_struct *curr, struct task_struct *next) { - if (static_key_false(&preempt_notifier_key)) + if (static_unlikely_branch(&preempt_notifier_key)) __fire_sched_out_preempt_notifiers(curr, next); } ^ permalink raw reply related [flat|nested] 54+ messages in thread
* Re: Kernel broken on processors without performance counters 2015-07-10 14:13 ` Peter Zijlstra @ 2015-07-10 15:29 ` Jason Baron 2015-07-21 8:21 ` Peter Zijlstra 1 sibling, 0 replies; 54+ messages in thread From: Jason Baron @ 2015-07-10 15:29 UTC (permalink / raw) To: Peter Zijlstra, Andy Lutomirski Cc: Mikulas Patocka, Paul Mackerras, Arnaldo Carvalho de Melo, Kees Cook, Andrea Arcangeli, Vince Weaver, hillf.zj, Valdis Kletnieks, linux-kernel@vger.kernel.org On 07/10/2015 10:13 AM, Peter Zijlstra wrote: > On Wed, Jul 08, 2015 at 05:36:43PM -0700, Andy Lutomirski wrote: >>>> In what universe is "static_key_false" a reasonable name for a >>>> function that returns true if a static key is true? >> I think the current naming is almost maximally bad. The naming would >> be less critical if it were typesafe, though. > How about something like so on top? It will allow us to slowly migrate > existing and new users over to the hopefully saner interface? > > --- > include/linux/jump_label.h | 67 +++++++++++++++++++++++++++++++++++++++++++++- > kernel/sched/core.c | 18 ++++++------- > 2 files changed, 74 insertions(+), 11 deletions(-) > > diff --git a/include/linux/jump_label.h b/include/linux/jump_label.h > index f4de473f226b..98ed3c2ec78d 100644 > --- a/include/linux/jump_label.h > +++ b/include/linux/jump_label.h > @@ -213,6 +213,71 @@ static inline bool static_key_enabled(struct static_key *key) > return static_key_count(key) > 0; > } > > -#endif /* _LINUX_JUMP_LABEL_H */ > +static inline void static_key_enable(struct static_key *key) > +{ > + int count = static_key_count(key); > + > + WARN_ON_ONCE(count < 0 || count > 1); > + > + if (!count) > + static_key_slow_inc(key); > +} > + > +static inline void static_key_disable(struct static_key *key) > +{ > + int count = static_key_count(key); > + > + WARN_ON_ONCE(count < 0 || count > 1); > + > + if (count) > + static_key_slow_dec(key); > +} should those be __static_key_enable()/disable() to indicate that we don't that we don't want ppl using these directly. Similarly for other 'internal' functions. > + > +/* -------------------------------------------------------------------------- */ > + > +/* > + * likely -- default enabled, puts the branch body in-line > + */ > + > +struct static_likely_key { > + struct static_key key; > +}; > + > +#define STATIC_LIKELY_KEY_INIT (struct static_likely_key){ .key = STATIC_KEY_INIT_TRUE, } > + > +static inline bool static_likely_branch(struct static_likely_key *key) > +{ > + return static_key_true(&key->key); > +} > + > +/* > + * unlikely -- default disabled, puts the branch body out-of-line > + */ > + > +struct static_unlikely_key { > + struct static_key key; > +}; > + > +#define STATIC_UNLIKELY_KEY_INIT (struct static_unlikely_key){ .key = STATIC_KEY_INIT_FALSE, } > + > +static inline bool static_unlikely_branch(struct static_unlikely_key *key) > +{ > + return static_key_false(&key->key); > +} > + > +/* > + * Advanced usage; refcount, branch is enabled when: count != 0 > + */ > + > +#define static_branch_inc(_k) static_key_slow_inc(&(_k)->key) > +#define static_branch_dec(_k) static_key_slow_dec(&(_k)->key) > + I think of these as operations on the 'static_key' so I still like 'static_key_inc()/dec()' (removing the 'slow' makes them different still). > +/* > + * Normal usage; boolean enable/disable. > + */ > + > +#define static_branch_enable(_k) static_key_enable(&(_k)->key) > +#define static_branch_disable(_k) static_key_disable(&(_k)->key) > Same here maybe: static_key_set_true()/false()? Thanks, -Jason ^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: Kernel broken on processors without performance counters 2015-07-10 14:13 ` Peter Zijlstra 2015-07-10 15:29 ` Jason Baron @ 2015-07-21 8:21 ` Peter Zijlstra 2015-07-21 15:43 ` Thomas Gleixner 1 sibling, 1 reply; 54+ messages in thread From: Peter Zijlstra @ 2015-07-21 8:21 UTC (permalink / raw) To: Andy Lutomirski Cc: Jason Baron, Mikulas Patocka, Paul Mackerras, Arnaldo Carvalho de Melo, Kees Cook, Andrea Arcangeli, Vince Weaver, hillf.zj, Valdis Kletnieks, linux-kernel@vger.kernel.org, Borislav Petkov, Steven Rostedt, Thomas Gleixner On Fri, Jul 10, 2015 at 04:13:59PM +0200, Peter Zijlstra wrote: > On Wed, Jul 08, 2015 at 05:36:43PM -0700, Andy Lutomirski wrote: > > >> In what universe is "static_key_false" a reasonable name for a > > >> function that returns true if a static key is true? > > > I think the current naming is almost maximally bad. The naming would > > be less critical if it were typesafe, though. > > How about something like so on top? It will allow us to slowly migrate > existing and new users over to the hopefully saner interface? Anybody? (Adding more Cc's) > --- > include/linux/jump_label.h | 67 +++++++++++++++++++++++++++++++++++++++++++++- > kernel/sched/core.c | 18 ++++++------- > 2 files changed, 74 insertions(+), 11 deletions(-) > > diff --git a/include/linux/jump_label.h b/include/linux/jump_label.h > index f4de473f226b..98ed3c2ec78d 100644 > --- a/include/linux/jump_label.h > +++ b/include/linux/jump_label.h > @@ -213,6 +213,71 @@ static inline bool static_key_enabled(struct static_key *key) > return static_key_count(key) > 0; > } > > -#endif /* _LINUX_JUMP_LABEL_H */ > +static inline void static_key_enable(struct static_key *key) > +{ > + int count = static_key_count(key); > + > + WARN_ON_ONCE(count < 0 || count > 1); > + > + if (!count) > + static_key_slow_inc(key); > +} > + > +static inline void static_key_disable(struct static_key *key) > +{ > + int count = static_key_count(key); > + > + WARN_ON_ONCE(count < 0 || count > 1); > + > + if (count) > + static_key_slow_dec(key); > +} > + > +/* -------------------------------------------------------------------------- */ > + > +/* > + * likely -- default enabled, puts the branch body in-line > + */ > + > +struct static_likely_key { > + struct static_key key; > +}; > + > +#define STATIC_LIKELY_KEY_INIT (struct static_likely_key){ .key = STATIC_KEY_INIT_TRUE, } > + > +static inline bool static_likely_branch(struct static_likely_key *key) > +{ > + return static_key_true(&key->key); > +} > + > +/* > + * unlikely -- default disabled, puts the branch body out-of-line > + */ > + > +struct static_unlikely_key { > + struct static_key key; > +}; > + > +#define STATIC_UNLIKELY_KEY_INIT (struct static_unlikely_key){ .key = STATIC_KEY_INIT_FALSE, } > + > +static inline bool static_unlikely_branch(struct static_unlikely_key *key) > +{ > + return static_key_false(&key->key); > +} > + > +/* > + * Advanced usage; refcount, branch is enabled when: count != 0 > + */ > + > +#define static_branch_inc(_k) static_key_slow_inc(&(_k)->key) > +#define static_branch_dec(_k) static_key_slow_dec(&(_k)->key) > + > +/* > + * Normal usage; boolean enable/disable. > + */ > + > +#define static_branch_enable(_k) static_key_enable(&(_k)->key) > +#define static_branch_disable(_k) static_key_disable(&(_k)->key) > > #endif /* __ASSEMBLY__ */ > +#endif /* _LINUX_JUMP_LABEL_H */ > diff --git a/kernel/sched/core.c b/kernel/sched/core.c > index 78b4bad10081..22ba92297375 100644 > --- a/kernel/sched/core.c > +++ b/kernel/sched/core.c > @@ -164,14 +164,12 @@ struct static_key sched_feat_keys[__SCHED_FEAT_NR] = { > > static void sched_feat_disable(int i) > { > - if (static_key_enabled(&sched_feat_keys[i])) > - static_key_slow_dec(&sched_feat_keys[i]); > + static_key_enable(&sched_feat_keys[i]); > } > > static void sched_feat_enable(int i) > { > - if (!static_key_enabled(&sched_feat_keys[i])) > - static_key_slow_inc(&sched_feat_keys[i]); > + static_key_disable(&sched_feat_keys[i]); > } > #else > static void sched_feat_disable(int i) { }; > @@ -2318,17 +2316,17 @@ void wake_up_new_task(struct task_struct *p) > > #ifdef CONFIG_PREEMPT_NOTIFIERS > > -static struct static_key preempt_notifier_key = STATIC_KEY_INIT_FALSE; > +static struct static_unlikely_key preempt_notifier_key = STATIC_UNLIKELY_KEY_INIT; > > void preempt_notifier_inc(void) > { > - static_key_slow_inc(&preempt_notifier_key); > + static_branch_inc(&preempt_notifier_key); > } > EXPORT_SYMBOL_GPL(preempt_notifier_inc); > > void preempt_notifier_dec(void) > { > - static_key_slow_dec(&preempt_notifier_key); > + static_branch_dec(&preempt_notifier_key); > } > EXPORT_SYMBOL_GPL(preempt_notifier_dec); > > @@ -2338,7 +2336,7 @@ EXPORT_SYMBOL_GPL(preempt_notifier_dec); > */ > void preempt_notifier_register(struct preempt_notifier *notifier) > { > - if (!static_key_false(&preempt_notifier_key)) > + if (!static_unlikely_branch(&preempt_notifier_key)) > WARN(1, "registering preempt_notifier while notifiers disabled\n"); > > hlist_add_head(¬ifier->link, ¤t->preempt_notifiers); > @@ -2367,7 +2365,7 @@ static void __fire_sched_in_preempt_notifiers(struct task_struct *curr) > > static __always_inline void fire_sched_in_preempt_notifiers(struct task_struct *curr) > { > - if (static_key_false(&preempt_notifier_key)) > + if (static_unlikely_branch(&preempt_notifier_key)) > __fire_sched_in_preempt_notifiers(curr); > } > > @@ -2385,7 +2383,7 @@ static __always_inline void > fire_sched_out_preempt_notifiers(struct task_struct *curr, > struct task_struct *next) > { > - if (static_key_false(&preempt_notifier_key)) > + if (static_unlikely_branch(&preempt_notifier_key)) > __fire_sched_out_preempt_notifiers(curr, next); > } > ^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: Kernel broken on processors without performance counters 2015-07-21 8:21 ` Peter Zijlstra @ 2015-07-21 15:43 ` Thomas Gleixner 2015-07-21 15:49 ` Peter Zijlstra 0 siblings, 1 reply; 54+ messages in thread From: Thomas Gleixner @ 2015-07-21 15:43 UTC (permalink / raw) To: Peter Zijlstra Cc: Andy Lutomirski, Jason Baron, Mikulas Patocka, Paul Mackerras, Arnaldo Carvalho de Melo, Kees Cook, Andrea Arcangeli, Vince Weaver, hillf.zj, Valdis Kletnieks, linux-kernel@vger.kernel.org, Borislav Petkov, Steven Rostedt On Tue, 21 Jul 2015, Peter Zijlstra wrote: > On Fri, Jul 10, 2015 at 04:13:59PM +0200, Peter Zijlstra wrote: > > On Wed, Jul 08, 2015 at 05:36:43PM -0700, Andy Lutomirski wrote: > > > >> In what universe is "static_key_false" a reasonable name for a > > > >> function that returns true if a static key is true? It might be very well our universe, you just need access to the proper drugs to feel comfortable with it. > > > I think the current naming is almost maximally bad. The naming would > > > be less critical if it were typesafe, though. > > > > How about something like so on top? It will allow us to slowly migrate > > existing and new users over to the hopefully saner interface? > > -#endif /* _LINUX_JUMP_LABEL_H */ > > +static inline void static_key_enable(struct static_key *key) > > +{ > > + int count = static_key_count(key); > > + > > + WARN_ON_ONCE(count < 0 || count > 1); > > + > > + if (!count) > > + static_key_slow_inc(key); > > +} > > + > > +static inline void static_key_disable(struct static_key *key) > > +{ > > + int count = static_key_count(key); > > + > > + WARN_ON_ONCE(count < 0 || count > 1); > > + > > + if (count) > > + static_key_slow_dec(key); > > +} The functions above are not part of the interface which should be used in code, right? To avoid further confusion, can we please move all the existing mess and the new helpers into jump_label_internals.h and just put the new interfaces in jump_label.h? > > +/* -------------------------------------------------------------------------- */ > > + > > +/* > > + * likely -- default enabled, puts the branch body in-line > > + */ > > + > > +struct static_likely_key { > > + struct static_key key; > > +}; > > + > > +#define STATIC_LIKELY_KEY_INIT (struct static_likely_key){ .key = STATIC_KEY_INIT_TRUE, } > > + > > +static inline bool static_likely_branch(struct static_likely_key *key) > > +{ > > + return static_key_true(&key->key); > > +} > > + > > +/* > > + * unlikely -- default disabled, puts the branch body out-of-line > > + */ > > + > > +struct static_unlikely_key { > > + struct static_key key; > > +}; > > + > > +#define STATIC_UNLIKELY_KEY_INIT (struct static_unlikely_key){ .key = STATIC_KEY_INIT_FALSE, } > > + > > +static inline bool static_unlikely_branch(struct static_unlikely_key *key) > > +{ > > + return static_key_false(&key->key); > > +} > > + > > +/* > > + * Advanced usage; refcount, branch is enabled when: count != 0 > > + */ > > + > > +#define static_branch_inc(_k) static_key_slow_inc(&(_k)->key) > > +#define static_branch_dec(_k) static_key_slow_dec(&(_k)->key) Inlines please > > +/* > > + * Normal usage; boolean enable/disable. > > + */ > > + > > +#define static_branch_enable(_k) static_key_enable(&(_k)->key) > > +#define static_branch_disable(_k) static_key_disable(&(_k)->key) Ditto All in all much more understandable than the existing horror. Thanks, tglx ^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: Kernel broken on processors without performance counters 2015-07-21 15:43 ` Thomas Gleixner @ 2015-07-21 15:49 ` Peter Zijlstra 2015-07-21 15:51 ` Andy Lutomirski ` (2 more replies) 0 siblings, 3 replies; 54+ messages in thread From: Peter Zijlstra @ 2015-07-21 15:49 UTC (permalink / raw) To: Thomas Gleixner Cc: Andy Lutomirski, Jason Baron, Mikulas Patocka, Paul Mackerras, Arnaldo Carvalho de Melo, Kees Cook, Andrea Arcangeli, Vince Weaver, hillf.zj, Valdis Kletnieks, linux-kernel@vger.kernel.org, Borislav Petkov, Steven Rostedt On Tue, Jul 21, 2015 at 05:43:27PM +0200, Thomas Gleixner wrote: > On Tue, 21 Jul 2015, Peter Zijlstra wrote: > > > > -#endif /* _LINUX_JUMP_LABEL_H */ > > > +static inline void static_key_enable(struct static_key *key) > > > +{ > > > + int count = static_key_count(key); > > > + > > > + WARN_ON_ONCE(count < 0 || count > 1); > > > + > > > + if (!count) > > > + static_key_slow_inc(key); > > > +} > > > + > > > +static inline void static_key_disable(struct static_key *key) > > > +{ > > > + int count = static_key_count(key); > > > + > > > + WARN_ON_ONCE(count < 0 || count > 1); > > > + > > > + if (count) > > > + static_key_slow_dec(key); > > > +} > > The functions above are not part of the interface which should be used > in code, right? They are in fact. They make it easier to deal with the refcount thing when all you want is boolean states. > > > +/* -------------------------------------------------------------------------- */ > > > + > > > +/* > > > + * likely -- default enabled, puts the branch body in-line > > > + */ > > > + > > > +struct static_likely_key { > > > + struct static_key key; > > > +}; > > > + > > > +#define STATIC_LIKELY_KEY_INIT (struct static_likely_key){ .key = STATIC_KEY_INIT_TRUE, } > > > + > > > +static inline bool static_likely_branch(struct static_likely_key *key) > > > +{ > > > + return static_key_true(&key->key); > > > +} > > > + > > > +/* > > > + * unlikely -- default disabled, puts the branch body out-of-line > > > + */ > > > + > > > +struct static_unlikely_key { > > > + struct static_key key; > > > +}; > > > + > > > +#define STATIC_UNLIKELY_KEY_INIT (struct static_unlikely_key){ .key = STATIC_KEY_INIT_FALSE, } > > > + > > > +static inline bool static_unlikely_branch(struct static_unlikely_key *key) > > > +{ > > > + return static_key_false(&key->key); > > > +} > > > + > > > +/* > > > + * Advanced usage; refcount, branch is enabled when: count != 0 > > > + */ > > > + > > > +#define static_branch_inc(_k) static_key_slow_inc(&(_k)->key) > > > +#define static_branch_dec(_k) static_key_slow_dec(&(_k)->key) > > Inlines please > > > > +/* > > > + * Normal usage; boolean enable/disable. > > > + */ > > > + > > > +#define static_branch_enable(_k) static_key_enable(&(_k)->key) > > > +#define static_branch_disable(_k) static_key_disable(&(_k)->key) > > Ditto > > All in all much more understandable than the existing horror. They cannot in fact be inlines because we play type tricks. Note how _k can be either struct static_likely_key or struct static_unlikely_key. ^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: Kernel broken on processors without performance counters 2015-07-21 15:49 ` Peter Zijlstra @ 2015-07-21 15:51 ` Andy Lutomirski 2015-07-21 16:12 ` Peter Zijlstra 2015-07-21 15:53 ` Thomas Gleixner 2015-07-21 15:54 ` Peter Zijlstra 2 siblings, 1 reply; 54+ messages in thread From: Andy Lutomirski @ 2015-07-21 15:51 UTC (permalink / raw) To: Peter Zijlstra Cc: Thomas Gleixner, Jason Baron, Mikulas Patocka, Paul Mackerras, Arnaldo Carvalho de Melo, Kees Cook, Andrea Arcangeli, Vince Weaver, hillf.zj, Valdis Kletnieks, linux-kernel@vger.kernel.org, Borislav Petkov, Steven Rostedt On Tue, Jul 21, 2015 at 8:49 AM, Peter Zijlstra <peterz@infradead.org> wrote: > On Tue, Jul 21, 2015 at 05:43:27PM +0200, Thomas Gleixner wrote: >> On Tue, 21 Jul 2015, Peter Zijlstra wrote: >> >> > > -#endif /* _LINUX_JUMP_LABEL_H */ >> > > +static inline void static_key_enable(struct static_key *key) >> > > +{ >> > > + int count = static_key_count(key); >> > > + >> > > + WARN_ON_ONCE(count < 0 || count > 1); >> > > + >> > > + if (!count) >> > > + static_key_slow_inc(key); >> > > +} >> > > + >> > > +static inline void static_key_disable(struct static_key *key) >> > > +{ >> > > + int count = static_key_count(key); >> > > + >> > > + WARN_ON_ONCE(count < 0 || count > 1); >> > > + >> > > + if (count) >> > > + static_key_slow_dec(key); >> > > +} >> >> The functions above are not part of the interface which should be used >> in code, right? > > They are in fact. They make it easier to deal with the refcount thing > when all you want is boolean states. > >> > > +/* -------------------------------------------------------------------------- */ >> > > + >> > > +/* >> > > + * likely -- default enabled, puts the branch body in-line >> > > + */ >> > > + >> > > +struct static_likely_key { >> > > + struct static_key key; >> > > +}; >> > > + >> > > +#define STATIC_LIKELY_KEY_INIT (struct static_likely_key){ .key = STATIC_KEY_INIT_TRUE, } >> > > + >> > > +static inline bool static_likely_branch(struct static_likely_key *key) >> > > +{ >> > > + return static_key_true(&key->key); >> > > +} >> > > + >> > > +/* >> > > + * unlikely -- default disabled, puts the branch body out-of-line >> > > + */ >> > > + >> > > +struct static_unlikely_key { >> > > + struct static_key key; >> > > +}; >> > > + >> > > +#define STATIC_UNLIKELY_KEY_INIT (struct static_unlikely_key){ .key = STATIC_KEY_INIT_FALSE, } >> > > + >> > > +static inline bool static_unlikely_branch(struct static_unlikely_key *key) >> > > +{ >> > > + return static_key_false(&key->key); >> > > +} >> > > + >> > > +/* >> > > + * Advanced usage; refcount, branch is enabled when: count != 0 >> > > + */ >> > > + >> > > +#define static_branch_inc(_k) static_key_slow_inc(&(_k)->key) >> > > +#define static_branch_dec(_k) static_key_slow_dec(&(_k)->key) >> >> Inlines please >> >> > > +/* >> > > + * Normal usage; boolean enable/disable. >> > > + */ >> > > + >> > > +#define static_branch_enable(_k) static_key_enable(&(_k)->key) >> > > +#define static_branch_disable(_k) static_key_disable(&(_k)->key) >> >> Ditto >> >> All in all much more understandable than the existing horror. > > They cannot in fact be inlines because we play type tricks. Note how _k > can be either struct static_likely_key or struct static_unlikely_key. > > To clarify my (mis-)understanding: There are two degrees of freedom in a static_key. They can start out true or false, and they can be unlikely or likely. Are those two degrees of freedom in fact tied together? --Andy ^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: Kernel broken on processors without performance counters 2015-07-21 15:51 ` Andy Lutomirski @ 2015-07-21 16:12 ` Peter Zijlstra 2015-07-21 16:57 ` Jason Baron 2015-07-21 18:15 ` Borislav Petkov 0 siblings, 2 replies; 54+ messages in thread From: Peter Zijlstra @ 2015-07-21 16:12 UTC (permalink / raw) To: Andy Lutomirski Cc: Thomas Gleixner, Jason Baron, Mikulas Patocka, Paul Mackerras, Arnaldo Carvalho de Melo, Kees Cook, Andrea Arcangeli, Vince Weaver, hillf.zj, Valdis Kletnieks, linux-kernel@vger.kernel.org, Borislav Petkov, Steven Rostedt On Tue, Jul 21, 2015 at 08:51:51AM -0700, Andy Lutomirski wrote: > To clarify my (mis-)understanding: > > There are two degrees of freedom in a static_key. They can start out > true or false, and they can be unlikely or likely. Are those two > degrees of freedom in fact tied together? Yes, if you start out false, you must be unlikely. If you start out true, you must be likely. We could maybe try and untangle that if there really is a good use case, but this is the current state. The whole reason this happened is because 'false' is like: ... <nop> 1: ... label: <unlikely code> jmp 1b Where the code if out-of-line by default. The enable will rewrite the <nop> with a jmp label. Of course, if you have code that is on by default, you don't want to pay that out-of-line penalty all the time. So the on by default generates: ... <nop> <likely code> label: ... Where, if we disable, we replace the nop with jmp label. Or rather, that all is the intent, GCC doesn't actually honour hot/cold attributes on asm labels very well last time I tried. ^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: Kernel broken on processors without performance counters 2015-07-21 16:12 ` Peter Zijlstra @ 2015-07-21 16:57 ` Jason Baron 2015-07-23 14:54 ` Steven Rostedt 2015-07-21 18:15 ` Borislav Petkov 1 sibling, 1 reply; 54+ messages in thread From: Jason Baron @ 2015-07-21 16:57 UTC (permalink / raw) To: Peter Zijlstra, Andy Lutomirski Cc: Thomas Gleixner, Mikulas Patocka, Paul Mackerras, Arnaldo Carvalho de Melo, Kees Cook, Andrea Arcangeli, Vince Weaver, hillf.zj, Valdis Kletnieks, linux-kernel@vger.kernel.org, Borislav Petkov, Steven Rostedt On 07/21/2015 12:12 PM, Peter Zijlstra wrote: > On Tue, Jul 21, 2015 at 08:51:51AM -0700, Andy Lutomirski wrote: >> To clarify my (mis-)understanding: >> >> There are two degrees of freedom in a static_key. They can start out >> true or false, and they can be unlikely or likely. Are those two >> degrees of freedom in fact tied together? > Yes, if you start out false, you must be unlikely. If you start out > true, you must be likely. > > We could maybe try and untangle that if there really is a good use case, > but this is the current state. We could potentially allow all combinations but it requires a more complex implementation. Perhaps, by rewriting no-ops to jmps during build-time? Steve Rostedt posted an implementation a while back to do that, but in part it wasn't merged due to its complexity and the fact that it increased the kernel text, which I don't think we ever got to the bottom of. Steve was actually trying to reduce the size of the no-ops by first letting the compiler pick the 'jmp' instruction size (short jumps of only 2-bytes on x86, instead of the hard-coded 5 bytes we currently have). However, we could use the same idea here to allow the mixed branch label and initial variable state. That said, it seems easy enough to reverse the direction of the branch in an __init or so when we boot, if required. > The whole reason this happened is because 'false' is like: > > > ... > <nop> > 1: > ... > > > > label: > <unlikely code> > jmp 1b > > > Where the code if out-of-line by default. The enable will rewrite the > <nop> with a jmp label. > > Of course, if you have code that is on by default, you don't want to pay > that out-of-line penalty all the time. So the on by default generates: > > > ... > <nop> > <likely code> > label: > ... > > > Where, if we disable, we replace the nop with jmp label. > > Or rather, that all is the intent, GCC doesn't actually honour hot/cold > attributes on asm labels very well last time I tried. I tried this too not that long ago, and didn't seem to make any difference. Ideally this could allow us to make variations where 'cold' code is moved further out-of-line. Thanks, -Jason ^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: Kernel broken on processors without performance counters 2015-07-21 16:57 ` Jason Baron @ 2015-07-23 14:54 ` Steven Rostedt 0 siblings, 0 replies; 54+ messages in thread From: Steven Rostedt @ 2015-07-23 14:54 UTC (permalink / raw) To: Jason Baron Cc: Peter Zijlstra, Andy Lutomirski, Thomas Gleixner, Mikulas Patocka, Paul Mackerras, Arnaldo Carvalho de Melo, Kees Cook, Andrea Arcangeli, Vince Weaver, hillf.zj, Valdis Kletnieks, linux-kernel@vger.kernel.org, Borislav Petkov On Tue, 21 Jul 2015 12:57:08 -0400 Jason Baron <jasonbaron0@gmail.com> wrote: > On 07/21/2015 12:12 PM, Peter Zijlstra wrote: > > On Tue, Jul 21, 2015 at 08:51:51AM -0700, Andy Lutomirski wrote: > >> To clarify my (mis-)understanding: > >> > >> There are two degrees of freedom in a static_key. They can start out > >> true or false, and they can be unlikely or likely. Are those two > >> degrees of freedom in fact tied together? > > Yes, if you start out false, you must be unlikely. If you start out > > true, you must be likely. > > > > We could maybe try and untangle that if there really is a good use case, > > but this is the current state. > > We could potentially allow all combinations but it requires a more > complex implementation. Perhaps, by rewriting no-ops to jmps > during build-time? Steve Rostedt posted an implementation a while > back to do that, but in part it wasn't merged due to its > complexity and the fact that it increased the kernel text, which > I don't think we ever got to the bottom of. Steve was actually I think that happened because we didn't save enough with the nops to compensate for the code that it took to implement that change. Perhaps in the future that will be different as the implementation is a fixed size, while the usage of jump labels increase. -- Steve > trying to reduce the size of the no-ops by first letting the compiler > pick the 'jmp' instruction size (short jumps of only 2-bytes on > x86, instead of the hard-coded 5 bytes we currently have). > However, we could use the same idea here to allow the mixed > branch label and initial variable state. > > That said, it seems easy enough to reverse the direction of > the branch in an __init or so when we boot, if required. > ^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: Kernel broken on processors without performance counters 2015-07-21 16:12 ` Peter Zijlstra 2015-07-21 16:57 ` Jason Baron @ 2015-07-21 18:15 ` Borislav Petkov 2015-07-21 18:50 ` Jason Baron 1 sibling, 1 reply; 54+ messages in thread From: Borislav Petkov @ 2015-07-21 18:15 UTC (permalink / raw) To: Peter Zijlstra Cc: Andy Lutomirski, Thomas Gleixner, Jason Baron, Mikulas Patocka, Paul Mackerras, Arnaldo Carvalho de Melo, Kees Cook, Andrea Arcangeli, Vince Weaver, hillf.zj, Valdis Kletnieks, linux-kernel@vger.kernel.org, Steven Rostedt On Tue, Jul 21, 2015 at 06:12:15PM +0200, Peter Zijlstra wrote: > Yes, if you start out false, you must be unlikely. If you start out > true, you must be likely. > > We could maybe try and untangle that if there really is a good use case, > but this is the current state. > > The whole reason this happened is because 'false' is like: > > > ... > <nop> > 1: > ... > > > > label: > <unlikely code> > jmp 1b > > > Where the code if out-of-line by default. The enable will rewrite the > <nop> with a jmp label. Btw, native_sched_clock() is kinda botched because of that, see below. I'd want that RDTSC to come first with a NOP preceding it which can become a JMP in case some idiotic CPU can't do RDTSC and needs to use jiffies. Instead, we *unconditionally* jump to RDTSC which is WTF?! We can just as well do a normal unlikely() without the static_key: .globl native_sched_clock .type native_sched_clock, @function native_sched_clock: pushq %rbp # movq %rsp, %rbp #, #APP # 21 "./arch/x86/include/asm/jump_label.h" 1 1:.byte 0x0f,0x1f,0x44,0x00,0 .pushsection __jump_table, "aw" .balign 8 .quad 1b, .L122, __use_tsc #, .popsection # 0 "" 2 #NO_APP movabsq $-4294667296000000, %rax #, tmp118 popq %rbp # imulq $1000000, jiffies_64(%rip), %rdx #, jiffies_64, D.28443 addq %rdx, %rax # D.28443, D.28443 ret .L122: #APP # 118 "./arch/x86/include/asm/msr.h" 1 rdtsc # 0 "" 2 -- Regards/Gruss, Boris. ECO tip #101: Trim your mails when you reply. -- ^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: Kernel broken on processors without performance counters 2015-07-21 18:15 ` Borislav Petkov @ 2015-07-21 18:50 ` Jason Baron 2015-07-21 18:54 ` Andy Lutomirski 2015-07-22 4:24 ` Borislav Petkov 0 siblings, 2 replies; 54+ messages in thread From: Jason Baron @ 2015-07-21 18:50 UTC (permalink / raw) To: Borislav Petkov, Peter Zijlstra Cc: Andy Lutomirski, Thomas Gleixner, Mikulas Patocka, Paul Mackerras, Arnaldo Carvalho de Melo, Kees Cook, Andrea Arcangeli, Vince Weaver, hillf.zj, Valdis Kletnieks, linux-kernel@vger.kernel.org, Steven Rostedt On 07/21/2015 02:15 PM, Borislav Petkov wrote: > On Tue, Jul 21, 2015 at 06:12:15PM +0200, Peter Zijlstra wrote: >> Yes, if you start out false, you must be unlikely. If you start out >> true, you must be likely. >> >> We could maybe try and untangle that if there really is a good use case, >> but this is the current state. >> >> The whole reason this happened is because 'false' is like: >> >> >> ... >> <nop> >> 1: >> ... >> >> >> >> label: >> <unlikely code> >> jmp 1b >> >> >> Where the code if out-of-line by default. The enable will rewrite the >> <nop> with a jmp label. > Btw, native_sched_clock() is kinda botched because of that, see below. > > I'd want that RDTSC to come first with a NOP preceding it which can > become a JMP in case some idiotic CPU can't do RDTSC and needs to use > jiffies. Instead, we *unconditionally* jump to RDTSC which is WTF?! We > can just as well do a normal unlikely() without the static_key: hmmm...so this is a case where need to the default the branch to the out-of-line branch at boot. That is, we can't just enable the out-of-line branch at boot time, b/c it might be too late at that point? IE native_sched_clock() gets called very early? Thanks, -Jason > > .globl native_sched_clock > .type native_sched_clock, @function > native_sched_clock: > pushq %rbp # > movq %rsp, %rbp #, > #APP > # 21 "./arch/x86/include/asm/jump_label.h" 1 > 1:.byte 0x0f,0x1f,0x44,0x00,0 > .pushsection __jump_table, "aw" > .balign 8 > .quad 1b, .L122, __use_tsc #, > .popsection > > # 0 "" 2 > #NO_APP > movabsq $-4294667296000000, %rax #, tmp118 > popq %rbp # > imulq $1000000, jiffies_64(%rip), %rdx #, jiffies_64, D.28443 > addq %rdx, %rax # D.28443, D.28443 > ret > .L122: > #APP > # 118 "./arch/x86/include/asm/msr.h" 1 > rdtsc > # 0 "" 2 > ^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: Kernel broken on processors without performance counters 2015-07-21 18:50 ` Jason Baron @ 2015-07-21 18:54 ` Andy Lutomirski 2015-07-21 19:00 ` Valdis.Kletnieks 2015-07-22 4:24 ` Borislav Petkov 1 sibling, 1 reply; 54+ messages in thread From: Andy Lutomirski @ 2015-07-21 18:54 UTC (permalink / raw) To: Jason Baron Cc: Borislav Petkov, Peter Zijlstra, Thomas Gleixner, Mikulas Patocka, Paul Mackerras, Arnaldo Carvalho de Melo, Kees Cook, Andrea Arcangeli, Vince Weaver, hillf.zj, Valdis Kletnieks, linux-kernel@vger.kernel.org, Steven Rostedt On Tue, Jul 21, 2015 at 11:50 AM, Jason Baron <jasonbaron0@gmail.com> wrote: > > > On 07/21/2015 02:15 PM, Borislav Petkov wrote: >> On Tue, Jul 21, 2015 at 06:12:15PM +0200, Peter Zijlstra wrote: >>> Yes, if you start out false, you must be unlikely. If you start out >>> true, you must be likely. >>> >>> We could maybe try and untangle that if there really is a good use case, >>> but this is the current state. >>> >>> The whole reason this happened is because 'false' is like: >>> >>> >>> ... >>> <nop> >>> 1: >>> ... >>> >>> >>> >>> label: >>> <unlikely code> >>> jmp 1b >>> >>> >>> Where the code if out-of-line by default. The enable will rewrite the >>> <nop> with a jmp label. >> Btw, native_sched_clock() is kinda botched because of that, see below. >> >> I'd want that RDTSC to come first with a NOP preceding it which can >> become a JMP in case some idiotic CPU can't do RDTSC and needs to use >> jiffies. Instead, we *unconditionally* jump to RDTSC which is WTF?! We >> can just as well do a normal unlikely() without the static_key: > > hmmm...so this is a case where need to the default the branch > to the out-of-line branch at boot. That is, we can't just enable > the out-of-line branch at boot time, b/c it might be too late at > that point? IE native_sched_clock() gets called very early? > Could this be done at link time, or perhaps when compressing the kernel image, instead of at boot time? --Andy ^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: Kernel broken on processors without performance counters 2015-07-21 18:54 ` Andy Lutomirski @ 2015-07-21 19:00 ` Valdis.Kletnieks 2015-07-21 19:29 ` Andy Lutomirski 0 siblings, 1 reply; 54+ messages in thread From: Valdis.Kletnieks @ 2015-07-21 19:00 UTC (permalink / raw) To: Andy Lutomirski Cc: Jason Baron, Borislav Petkov, Peter Zijlstra, Thomas Gleixner, Mikulas Patocka, Paul Mackerras, Arnaldo Carvalho de Melo, Kees Cook, Andrea Arcangeli, Vince Weaver, hillf.zj, linux-kernel@vger.kernel.org, Steven Rostedt [-- Attachment #1: Type: text/plain, Size: 439 bytes --] On Tue, 21 Jul 2015 11:54:30 -0700, Andy Lutomirski said: > Could this be done at link time, or perhaps when compressing the > kernel image, instead of at boot time? That's only safe to do if the kernel is built for one specific CPU - if it's a generic kernel that boots on multiple hardware designs, it will be wrong for some systems. In other words - safe to do if you're building it for *your* hardware. Totally unsafe for a distro. [-- Attachment #2: Type: application/pgp-signature, Size: 848 bytes --] ^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: Kernel broken on processors without performance counters 2015-07-21 19:00 ` Valdis.Kletnieks @ 2015-07-21 19:29 ` Andy Lutomirski 2015-07-21 23:49 ` Valdis.Kletnieks 0 siblings, 1 reply; 54+ messages in thread From: Andy Lutomirski @ 2015-07-21 19:29 UTC (permalink / raw) To: Valdis Kletnieks Cc: Jason Baron, Borislav Petkov, Peter Zijlstra, Thomas Gleixner, Mikulas Patocka, Paul Mackerras, Arnaldo Carvalho de Melo, Kees Cook, Andrea Arcangeli, Vince Weaver, hillf.zj, linux-kernel@vger.kernel.org, Steven Rostedt On Tue, Jul 21, 2015 at 12:00 PM, <Valdis.Kletnieks@vt.edu> wrote: > On Tue, 21 Jul 2015 11:54:30 -0700, Andy Lutomirski said: > >> Could this be done at link time, or perhaps when compressing the >> kernel image, instead of at boot time? > > That's only safe to do if the kernel is built for one specific CPU - if it's > a generic kernel that boots on multiple hardware designs, it will be wrong > for some systems. > > In other words - safe to do if you're building it for *your* hardware. Totally > unsafe for a distro. That's not what I meant. We do something in the C code that tells the build step which way the initial state goes. At link time, we make the initial state actually work like that. Then, at run time, we can still switch it again if needed. --Andy ^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: Kernel broken on processors without performance counters 2015-07-21 19:29 ` Andy Lutomirski @ 2015-07-21 23:49 ` Valdis.Kletnieks 0 siblings, 0 replies; 54+ messages in thread From: Valdis.Kletnieks @ 2015-07-21 23:49 UTC (permalink / raw) To: Andy Lutomirski Cc: Jason Baron, Borislav Petkov, Peter Zijlstra, Thomas Gleixner, Mikulas Patocka, Paul Mackerras, Arnaldo Carvalho de Melo, Kees Cook, Andrea Arcangeli, Vince Weaver, hillf.zj, linux-kernel@vger.kernel.org, Steven Rostedt [-- Attachment #1: Type: text/plain, Size: 705 bytes --] On Tue, 21 Jul 2015 12:29:21 -0700, Andy Lutomirski said: > That's not what I meant. We do something in the C code that tells the > build step which way the initial state goes. At link time, we make > the initial state actually work like that. Then, at run time, we can > still switch it again if needed. OK, that's something different than what I read the first time around. I'm thinking that a good adjective would be "brittle", in the face of recalcitrant GCC releases. Look at the fun we've had over the years getting things that *should* be fairly intuitive to work correctly (such as "inline"). Having said that, if somebody comes up with something that actually works, I'd be OK with it... [-- Attachment #2: Type: application/pgp-signature, Size: 848 bytes --] ^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: Kernel broken on processors without performance counters 2015-07-21 18:50 ` Jason Baron 2015-07-21 18:54 ` Andy Lutomirski @ 2015-07-22 4:24 ` Borislav Petkov 2015-07-22 17:06 ` Jason Baron 2015-07-22 20:43 ` Mikulas Patocka 1 sibling, 2 replies; 54+ messages in thread From: Borislav Petkov @ 2015-07-22 4:24 UTC (permalink / raw) To: Jason Baron Cc: Peter Zijlstra, Andy Lutomirski, Thomas Gleixner, Mikulas Patocka, Paul Mackerras, Arnaldo Carvalho de Melo, Kees Cook, Andrea Arcangeli, Vince Weaver, hillf.zj, Valdis Kletnieks, linux-kernel@vger.kernel.org, Steven Rostedt On Tue, Jul 21, 2015 at 02:50:25PM -0400, Jason Baron wrote: > hmmm...so this is a case where need to the default the branch > to the out-of-line branch at boot. That is, we can't just enable > the out-of-line branch at boot time, b/c it might be too late at > that point? IE native_sched_clock() gets called very early? Well, even the layout is wrong here. The optimal thing would be to have: NOP rdtsc unlikely: /* read jiffies */ at build time. And then at boot time, patch in the JMP over the NOP on !use_tsc boxes. And RDTSC works always, no matter how early. I'm fairly sure we can do that now with alternatives instead of jump labels. The problem currently is that the 5-byte NOP gets patched in with a JMP so we have an unconditional forwards JMP to the RDTSC. Now I'd put my money on most arches handling NOPs better then unconditional JMPs and this is a hot path... -- Regards/Gruss, Boris. ECO tip #101: Trim your mails when you reply. -- ^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: Kernel broken on processors without performance counters 2015-07-22 4:24 ` Borislav Petkov @ 2015-07-22 17:06 ` Jason Baron 2015-07-23 10:42 ` Peter Zijlstra 2015-07-22 20:43 ` Mikulas Patocka 1 sibling, 1 reply; 54+ messages in thread From: Jason Baron @ 2015-07-22 17:06 UTC (permalink / raw) To: Borislav Petkov Cc: Peter Zijlstra, Andy Lutomirski, Thomas Gleixner, Mikulas Patocka, Paul Mackerras, Arnaldo Carvalho de Melo, Kees Cook, Andrea Arcangeli, Vince Weaver, hillf.zj, Valdis Kletnieks, linux-kernel@vger.kernel.org, Steven Rostedt On 07/22/2015 12:24 AM, Borislav Petkov wrote: > On Tue, Jul 21, 2015 at 02:50:25PM -0400, Jason Baron wrote: >> hmmm...so this is a case where need to the default the branch >> to the out-of-line branch at boot. That is, we can't just enable >> the out-of-line branch at boot time, b/c it might be too late at >> that point? IE native_sched_clock() gets called very early? > Well, even the layout is wrong here. The optimal thing would be to have: > > NOP > rdtsc > > unlikely: > /* read jiffies */ > > at build time. And then at boot time, patch in the JMP over the NOP on > !use_tsc boxes. And RDTSC works always, no matter how early. > > I'm fairly sure we can do that now with alternatives instead of jump > labels. > > The problem currently is that the 5-byte NOP gets patched in with a JMP > so we have an unconditional forwards JMP to the RDTSC. > > Now I'd put my money on most arches handling NOPs better then > unconditional JMPs and this is a hot path... > Ok, So we could add all 4 possible initial states, where the branches would be: static_likely_init_true_branch(struct static_likely_init_true_key *key) static_likely_init_false_branch(struct static_likely_init_false_key *key) static_unlikely_init_false_branch(struct static_unlikely_init_false_key *key) static_unlikely_init_true_branch(struct static_unlikely_init_true_key *key) So, this last one means 'inline' the false branch, but start the branch as true. Which is, I think what you want here. So this addresses the use-case of 'initial' branch direction doesn't match the the default branch bias. We could also do this with link time time patching (and potentially reduce the need for 4 different types), but the implementation below doesn't seem too bad :) Its based upon Peter's initial patch. It unfortunately does require some arch specific bits (so we probably need a 'HAVE_ARCH', wrapper until we have support. Now, the native_sched_clock() starts as: ffffffff8200bf10 <native_sched_clock>: ffffffff8200bf10: 55 push %rbp ffffffff8200bf11: 48 89 e5 mov %rsp,%rbp ffffffff8200bf14: eb 30 jmp ffffffff8200bf46 <native_sched_clock+0x36> ffffffff8200bf16: 0f 31 rdtsc And then the '0x3b' gets patch to a 2-byte no-op Thanks, -Jason diff --git a/arch/x86/include/asm/jump_label.h b/arch/x86/include/asm/jump_label.h index a4c1cf7..030cf52 100644 --- a/arch/x86/include/asm/jump_label.h +++ b/arch/x86/include/asm/jump_label.h @@ -30,6 +30,20 @@ l_yes: return true; } +static __always_inline bool arch_static_branch_jump(struct static_key *key) +{ + asm_volatile_goto("1:" + "jmp %l[l_yes]\n\t" + ".pushsection __jump_table, \"aw\" \n\t" + _ASM_ALIGN "\n\t" + _ASM_PTR "1b, %l[l_yes], %c0 \n\t" + ".popsection \n\t" + : : "i" (key) : : l_yes); + return false; +l_yes: + return true; +} + #ifdef CONFIG_X86_64 typedef u64 jump_label_t; #else diff --git a/arch/x86/kernel/jump_label.c b/arch/x86/kernel/jump_label.c index 26d5a55..d40b19d 100644 --- a/arch/x86/kernel/jump_label.c +++ b/arch/x86/kernel/jump_label.c @@ -22,6 +22,10 @@ union jump_code_union { char jump; int offset; } __attribute__((packed)); + struct { + char jump_short; + char offset_short; + } __attribute__((packed)); }; static void bug_at(unsigned char *ip, int line) @@ -36,6 +40,8 @@ static void bug_at(unsigned char *ip, int line) BUG(); } +static const unsigned char short_nop[] = { P6_NOP2 }; + static void __jump_label_transform(struct jump_entry *entry, enum jump_label_type type, void *(*poker)(void *, const void *, size_t), @@ -44,47 +50,44 @@ static void __jump_label_transform(struct jump_entry *entry, union jump_code_union code; const unsigned char default_nop[] = { STATIC_KEY_INIT_NOP }; const unsigned char *ideal_nop = ideal_nops[NOP_ATOMIC5]; + void *instruct = (void *)entry->code; + unsigned size = JUMP_LABEL_NOP_SIZE; + unsigned char opcode = *(unsigned char *)instruct; if (type == JUMP_LABEL_ENABLE) { - if (init) { - /* - * Jump label is enabled for the first time. - * So we expect a default_nop... - */ - if (unlikely(memcmp((void *)entry->code, default_nop, 5) - != 0)) - bug_at((void *)entry->code, __LINE__); - } else { - /* - * ...otherwise expect an ideal_nop. Otherwise - * something went horribly wrong. - */ - if (unlikely(memcmp((void *)entry->code, ideal_nop, 5) - != 0)) - bug_at((void *)entry->code, __LINE__); - } + if (opcode == 0xe9 || opcode == 0xeb) + return; - code.jump = 0xe9; - code.offset = entry->target - - (entry->code + JUMP_LABEL_NOP_SIZE); - } 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) { - if (unlikely(memcmp((void *)entry->code, default_nop, 5) != 0)) + if (memcmp(instruct, short_nop, 2) == 0) + size = 2; + else if (!(memcmp(instruct, ideal_nop, 5) == 0) && + !(memcmp(instruct, default_nop, 5) == 0)) bug_at((void *)entry->code, __LINE__); + + if (size != JUMP_LABEL_NOP_SIZE) { + code.jump_short = 0xeb; + code.offset = entry->target - (entry->code + size); } else { code.jump = 0xe9; code.offset = entry->target - - (entry->code + JUMP_LABEL_NOP_SIZE); - if (unlikely(memcmp((void *)entry->code, &code, 5) != 0)) - bug_at((void *)entry->code, __LINE__); + (entry->code + size); } - memcpy(&code, ideal_nops[NOP_ATOMIC5], JUMP_LABEL_NOP_SIZE); + } else { + if (memcmp(instruct, short_nop, 2) == 0) + return; + + if (memcmp(instruct, ideal_nops[NOP_ATOMIC5], JUMP_LABEL_NOP_SIZE) == 0) + return; + + if (opcode == 0xeb) + size = 2; + else if ((opcode != 0xe9) && !(memcmp(instruct, default_nop, JUMP_LABEL_NOP_SIZE) == 0)) + bug_at((void *)entry->code, __LINE__); + + if (size != JUMP_LABEL_NOP_SIZE) + memcpy(&code, short_nop, size); + else + memcpy(&code, ideal_nops[NOP_ATOMIC5], size); } /* @@ -96,10 +99,10 @@ static void __jump_label_transform(struct jump_entry *entry, * */ if (poker) - (*poker)((void *)entry->code, &code, JUMP_LABEL_NOP_SIZE); + (*poker)((void *)entry->code, &code, size); else - text_poke_bp((void *)entry->code, &code, JUMP_LABEL_NOP_SIZE, - (void *)entry->code + JUMP_LABEL_NOP_SIZE); + text_poke_bp((void *)entry->code, &code, size, + (void *)entry->code + size); } void arch_jump_label_transform(struct jump_entry *entry, diff --git a/arch/x86/kernel/tsc.c b/arch/x86/kernel/tsc.c index 5054497..192c92f 100644 --- a/arch/x86/kernel/tsc.c +++ b/arch/x86/kernel/tsc.c @@ -38,7 +38,7 @@ static int __read_mostly tsc_unstable; erroneous rdtsc usage on !cpu_has_tsc processors */ static int __read_mostly tsc_disabled = -1; -static struct static_key __use_tsc = STATIC_KEY_INIT; +static struct static_unlikely_init_true_key __use_tsc = STATIC_KEY_UNLIKELY_INIT_TRUE; int tsc_clocksource_reliable; @@ -284,7 +284,7 @@ u64 native_sched_clock(void) * very important for it to be as fast as the platform * can achieve it. ) */ - if (!static_key_false(&__use_tsc)) { + if (static_unlikely_init_true_branch(&__use_tsc)) { /* No locking but a rare wrong value is not a big deal: */ return (jiffies_64 - INITIAL_JIFFIES) * (1000000000 / HZ); } @@ -1201,7 +1201,7 @@ void __init tsc_init(void) /* now allow native_sched_clock() to use rdtsc */ tsc_disabled = 0; - static_key_slow_inc(&__use_tsc); + static_branch_dec(&__use_tsc); if (!no_sched_irq_time) enable_sched_clock_irqtime(); diff --git a/include/linux/jump_label.h b/include/linux/jump_label.h index f4de473..558fef0 100644 --- a/include/linux/jump_label.h +++ b/include/linux/jump_label.h @@ -130,6 +130,16 @@ static __always_inline bool static_key_true(struct static_key *key) return !static_key_false(key); } +static __always_inline bool static_key_false_jump(struct static_key *key) +{ + return arch_static_branch_jump(key); +} + +static __always_inline bool static_key_true_jump(struct static_key *key) +{ + return !static_key_false_jump(key); +} + extern struct jump_entry __start___jump_table[]; extern struct jump_entry __stop___jump_table[]; @@ -152,6 +162,20 @@ extern void jump_label_apply_nops(struct module *mod); { .enabled = ATOMIC_INIT(0), \ .entries = (void *)JUMP_LABEL_TYPE_FALSE_BRANCH }) +#define STATIC_KEY_LIKEY_INIT_TRUE ((struct static_unlikely_init_true_key) \ + { .key.enabled = ATOMIC_INIT(1), \ + .key.entries = (void *)JUMP_LABEL_TYPE_TRUE_BRANCH }) +#define STATIC_KEY_LIKEY_INIT_FALSE ((struct static_unlikely_init_false_key) \ + { .key.enabled = ATOMIC_INIT(0), \ + .key.entries = (void *)JUMP_LABEL_TYPE_TRUE_BRANCH }) + +#define STATIC_KEY_UNLIKELY_INIT_FALSE ((struct static_unlikely_init_false_key) \ + { .key.enabled = ATOMIC_INIT(0), \ + .key.entries = (void *)JUMP_LABEL_TYPE_FALSE_BRANCH }) +#define STATIC_KEY_UNLIKELY_INIT_TRUE ((struct static_unlikely_init_true_key) \ + { .key.enabled = ATOMIC_INIT(1), \ + .key.entries = (void *)JUMP_LABEL_TYPE_FALSE_BRANCH }) + #else /* !HAVE_JUMP_LABEL */ static __always_inline void jump_label_init(void) @@ -165,6 +189,7 @@ static __always_inline bool static_key_false(struct static_key *key) return true; return false; } +#define static_key_false_jump static_key_false static __always_inline bool static_key_true(struct static_key *key) { @@ -172,6 +197,7 @@ static __always_inline bool static_key_true(struct static_key *key) return true; return false; } +#define static_key_true_jump static_key_true static inline void static_key_slow_inc(struct static_key *key) { @@ -203,6 +229,15 @@ static inline int jump_label_apply_nops(struct module *mod) #define STATIC_KEY_INIT_FALSE ((struct static_key) \ { .enabled = ATOMIC_INIT(0) }) +#define STATIC_KEY_LIKEY_INIT_TRUE ((struct static_key) \ + { .enabled = ATOMIC_INIT(1) }) +#define STATIC_KEY_LIKEY_INIT_FALSE ((struct static_key) \ + { .enabled = ATOMIC_INIT(0) }) +#define STATIC_KEY_UNLIKELY_INIT_FALSE ((struct static_key) \ + { .enabled = ATOMIC_INIT(0) }) +#define STATIC_KEY_UNLIKELY_INIT_TRUE ((struct static_key) \ + { .enabled = ATOMIC_INIT(1) }) + #endif /* HAVE_JUMP_LABEL */ #define STATIC_KEY_INIT STATIC_KEY_INIT_FALSE @@ -213,6 +248,85 @@ static inline bool static_key_enabled(struct static_key *key) return static_key_count(key) > 0; } -#endif /* _LINUX_JUMP_LABEL_H */ +static inline void static_key_enable(struct static_key *key) +{ + int count = static_key_count(key); + + WARN_ON_ONCE(count < 0 || count > 1); + + if (!count) + static_key_slow_inc(key); +} + +static inline void static_key_disable(struct static_key *key) +{ + int count = static_key_count(key); + + WARN_ON_ONCE(count < 0 || count > 1); + + if (count) + static_key_slow_dec(key); +} + +/* -------------------------------------------------------------------------- */ + +/* + * likely -- default enabled, puts the branch body in-line + */ + +struct static_likely_init_true_key { + struct static_key key; +}; + +struct static_likely_init_false_key { + struct static_key key; +}; + +static inline bool static_likely_init_true_branch(struct static_likely_init_true_key *key) +{ + return static_key_true(&key->key); +} + +static inline bool static_likely_init_false_branch(struct static_likely_init_false_key *key) +{ + return static_key_true_jump(&key->key); +} + +/* + * unlikely -- default disabled, puts the branch body out-of-line + */ + +struct static_unlikely_init_false_key { + struct static_key key; +}; + +struct static_unlikely_init_true_key { + struct static_key key; +}; + +static inline bool static_unlikely_init_false_branch(struct static_unlikely_init_false_key *key) +{ + return static_key_false(&key->key); +} + +static inline bool static_unlikely_init_true_branch(struct static_unlikely_init_true_key *key) +{ + return static_key_false_jump(&key->key); +} + +/* + * Advanced usage; refcount, branch is enabled when: count != 0 + */ + +#define static_branch_inc(_k) static_key_slow_inc(&(_k)->key) +#define static_branch_dec(_k) static_key_slow_dec(&(_k)->key) + +/* + * Normal usage; boolean enable/disable. + */ + +#define static_branch_enable(_k) static_key_enable(&(_k)->key) +#define static_branch_disable(_k) static_key_disable(&(_k)->key) #endif /* __ASSEMBLY__ */ +#endif /* _LINUX_JUMP_LABEL_H */ diff --git a/kernel/jump_label.c b/kernel/jump_label.c index 9019f15..47a6478 100644 --- a/kernel/jump_label.c +++ b/kernel/jump_label.c @@ -203,7 +203,8 @@ void __init jump_label_init(void) struct static_key *iterk; iterk = (struct static_key *)(unsigned long)iter->key; - arch_jump_label_transform_static(iter, jump_label_type(iterk)); + if (jump_label_type(iterk) == JUMP_LABEL_DISABLE) + arch_jump_label_transform_static(iter, JUMP_LABEL_DISABLE); if (iterk == key) continue; @@ -318,8 +319,7 @@ static int jump_label_add_module(struct module *mod) jlm->next = key->next; key->next = jlm; - if (jump_label_type(key) == JUMP_LABEL_ENABLE) - __jump_label_update(key, iter, iter_stop, JUMP_LABEL_ENABLE); + __jump_label_update(key, iter, iter_stop, jump_label_type(key)); } return 0; ^ permalink raw reply related [flat|nested] 54+ messages in thread
* Re: Kernel broken on processors without performance counters 2015-07-22 17:06 ` Jason Baron @ 2015-07-23 10:42 ` Peter Zijlstra 2015-07-23 10:53 ` Borislav Petkov ` (2 more replies) 0 siblings, 3 replies; 54+ messages in thread From: Peter Zijlstra @ 2015-07-23 10:42 UTC (permalink / raw) To: Jason Baron Cc: Borislav Petkov, Andy Lutomirski, Thomas Gleixner, Mikulas Patocka, Paul Mackerras, Arnaldo Carvalho de Melo, Kees Cook, Andrea Arcangeli, Vince Weaver, hillf.zj, Valdis Kletnieks, linux-kernel@vger.kernel.org, Steven Rostedt On Wed, Jul 22, 2015 at 01:06:44PM -0400, Jason Baron wrote: > Ok, > > So we could add all 4 possible initial states, where the > branches would be: > > static_likely_init_true_branch(struct static_likely_init_true_key *key) > static_likely_init_false_branch(struct static_likely_init_false_key *key) > static_unlikely_init_false_branch(struct static_unlikely_init_false_key *key) > static_unlikely_init_true_branch(struct static_unlikely_init_true_key *key) I'm sorely tempted to go quote cypress hill here... And I realize part of the problem is that we're wanting to use jump labels before we can patch them. But surely we can do better. extern bool ____wrong_branch_error(void); struct static_key_true; struct static_key_false; #define static_branch_likely(x) \ ({ \ bool branch; \ if (__builtin_types_compatible_p(typeof(x), struct static_key_true)) \ branch = !arch_static_branch(&(x)->key); \ else if (__builtin_types_compatible_p(typeof(x), struct static_key_false)) \ branch = !arch_static_branch_jump(&(x)->key); \ else \ branch = ____wrong_branch_error(); \ branch; \ }) #define static_branch_unlikely(x) \ ({ \ bool branch; \ if (__builtin_types_compatible_p(typeof(x), struct static_key_true)) \ branch = arch_static_branch(&(x)->key); \ else if (__builtin_types_compatible_p(typeof(x), struct static_key_false)) \ branch = arch_static_branch_jump(&(x)->key); \ else \ branch = ____wrong_branch_error(); \ branch; \ }) Can't we make something like that work? So the immediate problem appears to be the 4 different key inits, which don't seem very supportive of this separation: +#define STATIC_KEY_LIKEY_INIT_TRUE ((struct static_unlikely_init_true_key) \ + { .key.enabled = ATOMIC_INIT(1), \ + .key.entries = (void *)JUMP_LABEL_TYPE_TRUE_BRANCH }) +#define STATIC_KEY_LIKEY_INIT_FALSE ((struct static_unlikely_init_false_key) \ + { .key.enabled = ATOMIC_INIT(0), \ + .key.entries = (void *)JUMP_LABEL_TYPE_TRUE_BRANCH }) +#define STATIC_KEY_UNLIKELY_INIT_TRUE ((struct static_unlikely_init_true_key) \ + { .key.enabled = ATOMIC_INIT(1), \ + .key.entries = (void *)JUMP_LABEL_TYPE_FALSE_BRANCH }) +#define STATIC_KEY_UNLIKELY_INIT_FALSE ((struct static_unlikely_init_false_key) \ + { .key.enabled = ATOMIC_INIT(0), \ + .key.entries = (void *)JUMP_LABEL_TYPE_FALSE_BRANCH }) But I think we can fix that by using a second __jump_table section, then we can augment the LABEL_TYPE_{TRUE,FALSE} thing with the section we find the jump_entry in. Then we can do: #define STATIC_KEY_TRUE_INIT (struct static_key_true) { .key = STATIC_KEY_INIT_TRUE, } #define STATIC_KEY_FALSE_INIT (struct static_key_false){ .key = STATIC_KEY_INIT_FALSE, } And invert the jump_label_type if we're in the second section. I think we'll need a second argument to the arch_static_branch*() functions to indicate which section it needs to go in. static __always_inline bool arch_static_branch(struct static_key *key, bool inv) { if (!inv) { asm_volatile_goto("1:" ".byte " __stringify(STATIC_KEY_INIT_NOP) "\n\t" ".pushsection __jump_table, \"aw\" \n\t" _ASM_ALIGN "\n\t" _ASM_PTR "1b, %l[l_yes], %c0 \n\t" ".popsection \n\t" : : "i" (key) : : l_yes); } else { asm_volatile_goto("1:" ".byte " __stringify(STATIC_KEY_INIT_NOP) "\n\t" ".pushsection __jump_table_inv, \"aw\" \n\t" _ASM_ALIGN "\n\t" _ASM_PTR "1b, %l[l_yes], %c0 \n\t" ".popsection \n\t" : : "i" (key) : : l_yes); } return false; l_yes: return true; } static __always_inline bool arch_static_branch_jump(struct static_key *key, bool inv) { if (!inv) { asm_volatile_goto("1:" "jmp %l[l_yes]\n\t" ".pushsection __jump_table, \"aw\" \n\t" _ASM_ALIGN "\n\t" _ASM_PTR "1b, %l[l_yes], %c0 \n\t" ".popsection \n\t" : : "i" (key) : : l_yes); } else { asm_volatile_goto("1:" "jmp %l[l_yes]\n\t" ".pushsection __jump_table_inv, \"aw\" \n\t" _ASM_ALIGN "\n\t" _ASM_PTR "1b, %l[l_yes], %c0 \n\t" ".popsection \n\t" : : "i" (key) : : l_yes); } return false; l_yes: return true; } And change the branch macros thusly: #define static_branch_likely(x) \ ({ \ bool branch; \ if (__builtin_types_compatible_p(typeof(x), struct static_key_true)) \ branch = !arch_static_branch(&(x)->key, false); \ else if (__builtin_types_compatible_p(typeof(x), struct static_key_false)) \ branch = !arch_static_branch_jump(&(x)->key, true); \ else \ branch = ____wrong_branch_error(); \ branch; \ }) #define static_branch_unlikely(x) \ ({ \ bool branch; \ if (__builtin_types_compatible_p(typeof(x), struct static_key_true)) \ branch = arch_static_branch(&(x)->key, true); \ else if (__builtin_types_compatible_p(typeof(x), struct static_key_false)) \ branch = arch_static_branch_jump(&(x)->key, false); \ else \ branch = ____wrong_branch_error(); \ branch; \ }) And I think it'll all work. Hmm? ^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: Kernel broken on processors without performance counters 2015-07-23 10:42 ` Peter Zijlstra @ 2015-07-23 10:53 ` Borislav Petkov 2015-07-23 14:19 ` Jason Baron 2015-07-23 15:34 ` Steven Rostedt 2 siblings, 0 replies; 54+ messages in thread From: Borislav Petkov @ 2015-07-23 10:53 UTC (permalink / raw) To: Peter Zijlstra Cc: Jason Baron, Andy Lutomirski, Thomas Gleixner, Mikulas Patocka, Paul Mackerras, Arnaldo Carvalho de Melo, Kees Cook, Andrea Arcangeli, Vince Weaver, hillf.zj, Valdis Kletnieks, linux-kernel@vger.kernel.org, Steven Rostedt On Thu, Jul 23, 2015 at 12:42:15PM +0200, Peter Zijlstra wrote: > > static_likely_init_true_branch(struct static_likely_init_true_key *key) > > static_likely_init_false_branch(struct static_likely_init_false_key *key) > > static_unlikely_init_false_branch(struct static_unlikely_init_false_key *key) > > static_unlikely_init_true_branch(struct static_unlikely_init_true_key *key) > > I'm sorely tempted to go quote cypress hill here... Yah, those are at least too long and nuts. > And I realize part of the problem is that we're wanting to use jump > labels before we can patch them. But surely we can do better. > > extern bool ____wrong_branch_error(void); > > struct static_key_true; > struct static_key_false; > > #define static_branch_likely(x) \ > ({ \ > bool branch; \ > if (__builtin_types_compatible_p(typeof(x), struct static_key_true)) \ > branch = !arch_static_branch(&(x)->key); \ > else if (__builtin_types_compatible_p(typeof(x), struct static_key_false)) \ > branch = !arch_static_branch_jump(&(x)->key); \ > else \ > branch = ____wrong_branch_error(); \ > branch; \ > }) > > #define static_branch_unlikely(x) \ > ({ \ > bool branch; \ > if (__builtin_types_compatible_p(typeof(x), struct static_key_true)) \ > branch = arch_static_branch(&(x)->key); \ > else if (__builtin_types_compatible_p(typeof(x), struct static_key_false)) \ > branch = arch_static_branch_jump(&(x)->key); \ > else \ > branch = ____wrong_branch_error(); \ > branch; \ > }) > > Can't we make something like that work? > > So the immediate problem appears to be the 4 different key inits, which don't > seem very supportive of this separation: > > +#define STATIC_KEY_LIKEY_INIT_TRUE ((struct static_unlikely_init_true_key) \ LIKELY > + { .key.enabled = ATOMIC_INIT(1), \ > + .key.entries = (void *)JUMP_LABEL_TYPE_TRUE_BRANCH }) > > +#define STATIC_KEY_LIKEY_INIT_FALSE ((struct static_unlikely_init_false_key) \ Yuck, those struct names are still too long IMO. > + { .key.enabled = ATOMIC_INIT(0), \ > + .key.entries = (void *)JUMP_LABEL_TYPE_TRUE_BRANCH }) > > +#define STATIC_KEY_UNLIKELY_INIT_TRUE ((struct static_unlikely_init_true_key) \ > + { .key.enabled = ATOMIC_INIT(1), \ > + .key.entries = (void *)JUMP_LABEL_TYPE_FALSE_BRANCH }) > > +#define STATIC_KEY_UNLIKELY_INIT_FALSE ((struct static_unlikely_init_false_key) \ > + { .key.enabled = ATOMIC_INIT(0), \ > + .key.entries = (void *)JUMP_LABEL_TYPE_FALSE_BRANCH }) > > > But I think we can fix that by using a second __jump_table section, then > we can augment the LABEL_TYPE_{TRUE,FALSE} thing with the section we > find the jump_entry in. > > Then we can do: > > #define STATIC_KEY_TRUE_INIT (struct static_key_true) { .key = STATIC_KEY_INIT_TRUE, } > #define STATIC_KEY_FALSE_INIT (struct static_key_false){ .key = STATIC_KEY_INIT_FALSE, } Let's abbreviate that "STATIC_KEY" thing too: SK_TRUE_INIT SK_FALSE_INIT ... -- Regards/Gruss, Boris. ECO tip #101: Trim your mails when you reply. -- ^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: Kernel broken on processors without performance counters 2015-07-23 10:42 ` Peter Zijlstra 2015-07-23 10:53 ` Borislav Petkov @ 2015-07-23 14:19 ` Jason Baron 2015-07-23 14:33 ` Peter Zijlstra 2015-07-23 14:58 ` Peter Zijlstra 2015-07-23 15:34 ` Steven Rostedt 2 siblings, 2 replies; 54+ messages in thread From: Jason Baron @ 2015-07-23 14:19 UTC (permalink / raw) To: Peter Zijlstra Cc: Borislav Petkov, Andy Lutomirski, Thomas Gleixner, Mikulas Patocka, Paul Mackerras, Arnaldo Carvalho de Melo, Kees Cook, Andrea Arcangeli, Vince Weaver, hillf.zj, Valdis Kletnieks, linux-kernel@vger.kernel.org, Steven Rostedt On 07/23/2015 06:42 AM, Peter Zijlstra wrote: > On Wed, Jul 22, 2015 at 01:06:44PM -0400, Jason Baron wrote: >> Ok, >> >> So we could add all 4 possible initial states, where the >> branches would be: >> >> static_likely_init_true_branch(struct static_likely_init_true_key *key) >> static_likely_init_false_branch(struct static_likely_init_false_key *key) >> static_unlikely_init_false_branch(struct static_unlikely_init_false_key *key) >> static_unlikely_init_true_branch(struct static_unlikely_init_true_key *key) > I'm sorely tempted to go quote cypress hill here... > > > And I realize part of the problem is that we're wanting to use jump > labels before we can patch them. But surely we can do better. > > extern bool ____wrong_branch_error(void); > > struct static_key_true; > struct static_key_false; > > #define static_branch_likely(x) \ > ({ \ > bool branch; \ > if (__builtin_types_compatible_p(typeof(x), struct static_key_true)) \ > branch = !arch_static_branch(&(x)->key); \ > else if (__builtin_types_compatible_p(typeof(x), struct static_key_false)) \ > branch = !arch_static_branch_jump(&(x)->key); \ > else \ > branch = ____wrong_branch_error(); \ > branch; \ > }) > > #define static_branch_unlikely(x) \ > ({ \ > bool branch; \ > if (__builtin_types_compatible_p(typeof(x), struct static_key_true)) \ > branch = arch_static_branch(&(x)->key); \ > else if (__builtin_types_compatible_p(typeof(x), struct static_key_false)) \ > branch = arch_static_branch_jump(&(x)->key); \ > else \ > branch = ____wrong_branch_error(); \ > branch; \ > }) > > Can't we make something like that work? > > So the immediate problem appears to be the 4 different key inits, which don't > seem very supportive of this separation: > > +#define STATIC_KEY_LIKEY_INIT_TRUE ((struct static_unlikely_init_true_key) \ > + { .key.enabled = ATOMIC_INIT(1), \ > + .key.entries = (void *)JUMP_LABEL_TYPE_TRUE_BRANCH }) > > +#define STATIC_KEY_LIKEY_INIT_FALSE ((struct static_unlikely_init_false_key) \ > + { .key.enabled = ATOMIC_INIT(0), \ > + .key.entries = (void *)JUMP_LABEL_TYPE_TRUE_BRANCH }) > > +#define STATIC_KEY_UNLIKELY_INIT_TRUE ((struct static_unlikely_init_true_key) \ > + { .key.enabled = ATOMIC_INIT(1), \ > + .key.entries = (void *)JUMP_LABEL_TYPE_FALSE_BRANCH }) > > +#define STATIC_KEY_UNLIKELY_INIT_FALSE ((struct static_unlikely_init_false_key) \ > + { .key.enabled = ATOMIC_INIT(0), \ > + .key.entries = (void *)JUMP_LABEL_TYPE_FALSE_BRANCH }) > > > But I think we can fix that by using a second __jump_table section, then > we can augment the LABEL_TYPE_{TRUE,FALSE} thing with the section we > find the jump_entry in. > > Then we can do: > > #define STATIC_KEY_TRUE_INIT (struct static_key_true) { .key = STATIC_KEY_INIT_TRUE, } > #define STATIC_KEY_FALSE_INIT (struct static_key_false){ .key = STATIC_KEY_INIT_FALSE, } > > And invert the jump_label_type if we're in the second section. > > I think we'll need a second argument to the arch_static_branch*() > functions to indicate which section it needs to go in. > > > static __always_inline bool arch_static_branch(struct static_key *key, bool inv) > { > if (!inv) { > asm_volatile_goto("1:" > ".byte " __stringify(STATIC_KEY_INIT_NOP) "\n\t" > ".pushsection __jump_table, \"aw\" \n\t" > _ASM_ALIGN "\n\t" > _ASM_PTR "1b, %l[l_yes], %c0 \n\t" > ".popsection \n\t" > : : "i" (key) : : l_yes); > } else { > asm_volatile_goto("1:" > ".byte " __stringify(STATIC_KEY_INIT_NOP) "\n\t" > ".pushsection __jump_table_inv, \"aw\" \n\t" > _ASM_ALIGN "\n\t" > _ASM_PTR "1b, %l[l_yes], %c0 \n\t" > ".popsection \n\t" > : : "i" (key) : : l_yes); > } > return false; > l_yes: > return true; > } > > static __always_inline bool arch_static_branch_jump(struct static_key *key, bool inv) > { > if (!inv) { > asm_volatile_goto("1:" > "jmp %l[l_yes]\n\t" > ".pushsection __jump_table, \"aw\" \n\t" > _ASM_ALIGN "\n\t" > _ASM_PTR "1b, %l[l_yes], %c0 \n\t" > ".popsection \n\t" > : : "i" (key) : : l_yes); > } else { > asm_volatile_goto("1:" > "jmp %l[l_yes]\n\t" > ".pushsection __jump_table_inv, \"aw\" \n\t" > _ASM_ALIGN "\n\t" > _ASM_PTR "1b, %l[l_yes], %c0 \n\t" > ".popsection \n\t" > : : "i" (key) : : l_yes); > } > return false; > l_yes: > return true; > } > > > And change the branch macros thusly: > > > #define static_branch_likely(x) \ > ({ \ > bool branch; \ > if (__builtin_types_compatible_p(typeof(x), struct static_key_true)) \ > branch = !arch_static_branch(&(x)->key, false); \ > else if (__builtin_types_compatible_p(typeof(x), struct static_key_false)) \ > branch = !arch_static_branch_jump(&(x)->key, true); \ > else \ > branch = ____wrong_branch_error(); \ > branch; \ > }) > > #define static_branch_unlikely(x) \ > ({ \ > bool branch; \ > if (__builtin_types_compatible_p(typeof(x), struct static_key_true)) \ > branch = arch_static_branch(&(x)->key, true); \ > else if (__builtin_types_compatible_p(typeof(x), struct static_key_false)) \ > branch = arch_static_branch_jump(&(x)->key, false); \ > else \ > branch = ____wrong_branch_error(); \ > branch; \ > }) > In 'static_branch_unlikely()', I think arch_static_branch() and arch_static_branch_jump() are reversed. Another way to do the 'inv' thing is to simply have the likely() branches all be in one table and the unlikely() in the other. Then, for example for a given _inc() operation we would no-op all the likely() branches and jmp all the unlikely(). > And I think it'll all work. Hmm? Cool. This also gives an extra degree of freedom in that it allows keys to be arbitrarily mixed with the likely/unlikely branch types. I'm not sure that's come up as a use-case, but seems like it would be good. It also implies that the LABEL_TYPE_{TRUE,FALSE}, is no longer associated with the key b/c a key could be used in both and unlikely() or likely() branch. So that would eventually go away, and the 'struct static_key()', I guess could point to its relevant entries in both tables. Although, that means an extra pointer in the 'struct static_key'. It may be simpler to simply add another field to the jump table that specifies if the branch is likely/unlikely, and then we are back to one table? IE arch_static_branch() could add that field to the jump table. Thanks, -Jason ^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: Kernel broken on processors without performance counters 2015-07-23 14:19 ` Jason Baron @ 2015-07-23 14:33 ` Peter Zijlstra 2015-07-23 14:49 ` Peter Zijlstra 2015-07-23 14:58 ` Peter Zijlstra 1 sibling, 1 reply; 54+ messages in thread From: Peter Zijlstra @ 2015-07-23 14:33 UTC (permalink / raw) To: Jason Baron Cc: Borislav Petkov, Andy Lutomirski, Thomas Gleixner, Mikulas Patocka, Paul Mackerras, Arnaldo Carvalho de Melo, Kees Cook, Andrea Arcangeli, Vince Weaver, hillf.zj, Valdis Kletnieks, linux-kernel@vger.kernel.org, Steven Rostedt On Thu, Jul 23, 2015 at 10:19:52AM -0400, Jason Baron wrote: > > And I think it'll all work. Hmm? > > Cool. This also gives an extra degree of freedom in that it allows keys to > be arbitrarily mixed with the likely/unlikely branch types. I'm not sure that's > come up as a use-case, but seems like it would be good. It also implies > that the LABEL_TYPE_{TRUE,FALSE}, is no longer associated with the key > b/c a key could be used in both and unlikely() or likely() branch. So that > would eventually go away, and the 'struct static_key()', I guess could point > to its relevant entries in both tables. Although, that means an extra > pointer in the 'struct static_key'. It may be simpler to simply add another > field to the jump table that specifies if the branch is likely/unlikely, > and then we are back to one table? IE arch_static_branch() could add > that field to the jump table. Way ahead of you, while implementing the dual section I ran into trouble and found that it would be far easier to indeed stick it in the jump_entry. However, instead of growing the thing, I've used the LSB of the key field, that's a pointer so it has at least two bits free anyhow. I've also implemented it for all archs (+- compile failures, I've not gotten that far). Lemme finish this and I'll post it. ^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: Kernel broken on processors without performance counters 2015-07-23 14:33 ` Peter Zijlstra @ 2015-07-23 14:49 ` Peter Zijlstra 2015-07-23 19:14 ` Jason Baron 0 siblings, 1 reply; 54+ messages in thread From: Peter Zijlstra @ 2015-07-23 14:49 UTC (permalink / raw) To: Jason Baron Cc: Borislav Petkov, Andy Lutomirski, Thomas Gleixner, Mikulas Patocka, Paul Mackerras, Arnaldo Carvalho de Melo, Kees Cook, Andrea Arcangeli, Vince Weaver, hillf.zj, Valdis Kletnieks, linux-kernel@vger.kernel.org, Steven Rostedt On Thu, Jul 23, 2015 at 04:33:08PM +0200, Peter Zijlstra wrote: > Lemme finish this and I'll post it. Compile tested on x86_64 only.. Please have a look, I think you said I got some of the logic wrong, I've not double checked that. I'll go write comments and double check things. --- arch/arm/include/asm/jump_label.h | 22 +++++++- arch/arm64/include/asm/jump_label.h | 22 +++++++- arch/mips/include/asm/jump_label.h | 23 +++++++- arch/powerpc/include/asm/jump_label.h | 23 +++++++- arch/s390/include/asm/jump_label.h | 23 +++++++- arch/sparc/include/asm/jump_label.h | 38 ++++++++++--- arch/x86/include/asm/jump_label.h | 24 +++++++- include/linux/jump_label.h | 101 +++++++++++++++++++++++++++++++++- kernel/jump_label.c | 89 +++++++++++++++--------------- 9 files changed, 297 insertions(+), 68 deletions(-) diff --git a/arch/arm/include/asm/jump_label.h b/arch/arm/include/asm/jump_label.h index 5f337dc5c108..6c9789b33497 100644 --- a/arch/arm/include/asm/jump_label.h +++ b/arch/arm/include/asm/jump_label.h @@ -13,14 +13,32 @@ #define JUMP_LABEL_NOP "nop" #endif -static __always_inline bool arch_static_branch(struct static_key *key) +static __always_inline bool arch_static_branch(struct static_key *key, bool inv) { + unsigned long kval = (unsigned long)key + inv; + asm_volatile_goto("1:\n\t" JUMP_LABEL_NOP "\n\t" ".pushsection __jump_table, \"aw\"\n\t" ".word 1b, %l[l_yes], %c0\n\t" ".popsection\n\t" - : : "i" (key) : : l_yes); + : : "i" (kval) : : l_yes); + + return false; +l_yes: + return true; +} + +static __always_inline bool arch_static_branch_jump(struct static_key *key, bool inv) +{ + unsigned long kval = (unsigned long)key + inv; + + asm_volatile_goto("1:\n\t" + "b %l[l_yes]\n\t" + ".pushsection __jump_table, \"aw\"\n\t" + ".word 1b, %l[l_yes], %c0\n\t" + ".popsection\n\t" + : : "i" (kval) : : l_yes); return false; l_yes: diff --git a/arch/arm64/include/asm/jump_label.h b/arch/arm64/include/asm/jump_label.h index c0e5165c2f76..e5cda5d75c62 100644 --- a/arch/arm64/include/asm/jump_label.h +++ b/arch/arm64/include/asm/jump_label.h @@ -26,14 +26,32 @@ #define JUMP_LABEL_NOP_SIZE AARCH64_INSN_SIZE -static __always_inline bool arch_static_branch(struct static_key *key) +static __always_inline bool arch_static_branch(struct static_key *key, bool inv) { + unsigned long kval = (unsigned long)key + inv; + asm goto("1: nop\n\t" ".pushsection __jump_table, \"aw\"\n\t" ".align 3\n\t" ".quad 1b, %l[l_yes], %c0\n\t" ".popsection\n\t" - : : "i"(key) : : l_yes); + : : "i"(kval) : : l_yes); + + return false; +l_yes: + return true; +} + +static __always_inline bool arch_static_branch_jump(struct static_key *key, bool inv) +{ + unsigned long kval = (unsigned long)key + inv; + + asm goto("1: b %l[l_yes]\n\t" + ".pushsection __jump_table, \"aw\"\n\t" + ".align 3\n\t" + ".quad 1b, %l[l_yes], %c0\n\t" + ".popsection\n\t" + : : "i"(kval) : : l_yes); return false; l_yes: diff --git a/arch/mips/include/asm/jump_label.h b/arch/mips/include/asm/jump_label.h index 608aa57799c8..d9fca6f52a93 100644 --- a/arch/mips/include/asm/jump_label.h +++ b/arch/mips/include/asm/jump_label.h @@ -26,14 +26,33 @@ #define NOP_INSN "nop" #endif -static __always_inline bool arch_static_branch(struct static_key *key) +static __always_inline bool arch_static_branch(struct static_key *key, bool inv) { + unsigned long kval = (unsigned long)key + inv; + asm_volatile_goto("1:\t" NOP_INSN "\n\t" "nop\n\t" ".pushsection __jump_table, \"aw\"\n\t" WORD_INSN " 1b, %l[l_yes], %0\n\t" ".popsection\n\t" - : : "i" (key) : : l_yes); + : : "i" (kval) : : l_yes); + + return false; +l_yes: + return true; +} + +static __always_inline bool arch_static_branch_jump(struct static_key *key, bool inv) +{ + unsigned long kval = (unsigned long)key + inv; + + asm_volatile_goto("1:\tj %l[l_yes]\n\t" + "nop\n\t" + ".pushsection __jump_table, \"aw\"\n\t" + WORD_INSN " 1b, %l[l_yes], %0\n\t" + ".popsection\n\t" + : : "i" (kval) : : l_yes); + return false; l_yes: return true; diff --git a/arch/powerpc/include/asm/jump_label.h b/arch/powerpc/include/asm/jump_label.h index efbf9a322a23..c7cffedb1801 100644 --- a/arch/powerpc/include/asm/jump_label.h +++ b/arch/powerpc/include/asm/jump_label.h @@ -18,14 +18,33 @@ #define JUMP_ENTRY_TYPE stringify_in_c(FTR_ENTRY_LONG) #define JUMP_LABEL_NOP_SIZE 4 -static __always_inline bool arch_static_branch(struct static_key *key) +static __always_inline bool arch_static_branch(struct static_key *key, bool inv) { + unsigned long kval = (unsigned long)key + inv; + asm_volatile_goto("1:\n\t" "nop\n\t" ".pushsection __jump_table, \"aw\"\n\t" JUMP_ENTRY_TYPE "1b, %l[l_yes], %c0\n\t" ".popsection \n\t" - : : "i" (key) : : l_yes); + : : "i" (kval) : : l_yes); + + return false; +l_yes: + return true; +} + +static __always_inline bool arch_static_branch_jump(struct static_key *key, bool inv) +{ + unsigned long kval = (unsigned long)key + inv; + + asm_volatile_goto("1:\n\t" + "b %l[l_yes]\n\t" + ".pushsection __jump_table, \"aw\"\n\t" + JUMP_ENTRY_TYPE "1b, %l[l_yes], %c0\n\t" + ".popsection \n\t" + : : "i" (kval) : : l_yes); + return false; l_yes: return true; diff --git a/arch/s390/include/asm/jump_label.h b/arch/s390/include/asm/jump_label.h index 69972b7957ee..4734b09b9fce 100644 --- a/arch/s390/include/asm/jump_label.h +++ b/arch/s390/include/asm/jump_label.h @@ -12,14 +12,33 @@ * We use a brcl 0,2 instruction for jump labels at compile time so it * can be easily distinguished from a hotpatch generated instruction. */ -static __always_inline bool arch_static_branch(struct static_key *key) +static __always_inline bool arch_static_branch(struct static_key *key, bool inv) { + unsigned long kval = (unsigned long)key + inv; + asm_volatile_goto("0: brcl 0,"__stringify(JUMP_LABEL_NOP_OFFSET)"\n" ".pushsection __jump_table, \"aw\"\n" ".balign 8\n" ".quad 0b, %l[label], %0\n" ".popsection\n" - : : "X" (key) : : label); + : : "X" (kval) : : label); + + return false; +label: + return true; +} + +static __always_inline bool arch_static_branch_jump(struct static_key *key, bool inv) +{ + unsigned long kval = (unsigned long)key + inv; + + asm_volatile_goto("0: j %l[l_yes]\n" + ".pushsection __jump_table, \"aw\"\n" + ".balign 8\n" + ".quad 0b, %l[label], %0\n" + ".popsection\n" + : : "X" (kval) : : label); + return false; label: return true; diff --git a/arch/sparc/include/asm/jump_label.h b/arch/sparc/include/asm/jump_label.h index cc9b04a2b11b..764f3c21da21 100644 --- a/arch/sparc/include/asm/jump_label.h +++ b/arch/sparc/include/asm/jump_label.h @@ -7,16 +7,36 @@ #define JUMP_LABEL_NOP_SIZE 4 -static __always_inline bool arch_static_branch(struct static_key *key) +static __always_inline bool arch_static_branch(struct static_key *key, bool inv) { - asm_volatile_goto("1:\n\t" - "nop\n\t" - "nop\n\t" - ".pushsection __jump_table, \"aw\"\n\t" - ".align 4\n\t" - ".word 1b, %l[l_yes], %c0\n\t" - ".popsection \n\t" - : : "i" (key) : : l_yes); + unsigned long kval = (unsigned long)key + inv; + + asm_volatile_goto("1:\n\t" + "nop\n\t" + "nop\n\t" + ".pushsection __jump_table, \"aw\"\n\t" + ".align 4\n\t" + ".word 1b, %l[l_yes], %c0\n\t" + ".popsection \n\t" + : : "i" (kval) : : l_yes); + + return false; +l_yes: + return true; +} + +static __always_inline bool arch_static_branch_jump(struct static_key *key, bool inv) +{ + unsigned long kval = (unsigned long)key + inv; + + asm_volatile_goto("1:\n\t" + "b %l[l_yes]\n\t" + ".pushsection __jump_table, \"aw\"\n\t" + ".align 4\n\t" + ".word 1b, %l[l_yes], %c0\n\t" + ".popsection \n\t" + : : "i" (kval) : : l_yes); + return false; l_yes: return true; diff --git a/arch/x86/include/asm/jump_label.h b/arch/x86/include/asm/jump_label.h index a4c1cf7e93f8..43531baaee38 100644 --- a/arch/x86/include/asm/jump_label.h +++ b/arch/x86/include/asm/jump_label.h @@ -16,15 +16,35 @@ # define STATIC_KEY_INIT_NOP GENERIC_NOP5_ATOMIC #endif -static __always_inline bool arch_static_branch(struct static_key *key) +static __always_inline bool arch_static_branch(struct static_key *key, bool inv) { + unsigned long kval = (unsigned long)key + inv; + asm_volatile_goto("1:" ".byte " __stringify(STATIC_KEY_INIT_NOP) "\n\t" ".pushsection __jump_table, \"aw\" \n\t" _ASM_ALIGN "\n\t" _ASM_PTR "1b, %l[l_yes], %c0 \n\t" ".popsection \n\t" - : : "i" (key) : : l_yes); + : : "i" (kval) : : l_yes); + + return false; +l_yes: + return true; +} + +static __always_inline bool arch_static_branch_jump(struct static_key *key, bool inv) +{ + unsigned long kval = (unsigned long)key + inv; + + asm_volatile_goto("1:" + "jmp %l[l_yes]\n\t" + ".pushsection __jump_table, \"aw\" \n\t" + _ASM_ALIGN "\n\t" + _ASM_PTR "1b, %l[l_yes], %c0 \n\t" + ".popsection \n\t" + : : "i" (kval) : : l_yes); + return false; l_yes: return true; diff --git a/include/linux/jump_label.h b/include/linux/jump_label.h index f4de473f226b..4c97fed7acea 100644 --- a/include/linux/jump_label.h +++ b/include/linux/jump_label.h @@ -122,7 +122,7 @@ static inline bool jump_label_get_branch_default(struct static_key *key) static __always_inline bool static_key_false(struct static_key *key) { - return arch_static_branch(key); + return arch_static_branch(key, false); } static __always_inline bool static_key_true(struct static_key *key) @@ -213,6 +213,105 @@ static inline bool static_key_enabled(struct static_key *key) return static_key_count(key) > 0; } +static inline void static_key_enable(struct static_key *key) +{ + int count = static_key_count(key); + + WARN_ON_ONCE(count < 0 || count > 1); + + if (!count) + static_key_slow_inc(key); +} + +static inline void static_key_disable(struct static_key *key) +{ + int count = static_key_count(key); + + WARN_ON_ONCE(count < 0 || count > 1); + + if (count) + static_key_slow_dec(key); +} + + +/* -------------------------------------------------------------------------- */ + +/* + * Two type wrappers around static_key, such that we can use compile time + * type differentiation to emit the right code. + * + * All the below code is macros in order to play type games. + */ + +struct static_key_true { + struct static_key key; +}; + +struct static_key_false { + struct static_key key; +}; + +#define STATIC_KEY_TRUE_INIT (struct static_key_true) { .key = STATIC_KEY_INIT_TRUE, } +#define STATIC_KEY_FALSE_INIT (struct static_key_false){ .key = STATIC_KEY_INIT_FALSE, } + +#define DEFINE_STATIC_KEY_TRUE(name) \ + struct static_key_true name = STATIC_KEY_TRUE_INIT + +#define DEFINE_STATIC_KEY_FALSE(name) \ + struct static_key_false name = STATIC_KEY_FALSE_INIT + +#ifdef HAVE_JUMP_LABEL + +/* + * Combine the right initial value (type) with the right branch order and section + * to generate the desired result. + */ + +#define static_branch_likely(x) \ +({ \ + bool branch; \ + if (__builtin_types_compatible_p(typeof(x), struct static_key_true)) \ + branch = !arch_static_branch(&(x)->key, false); \ + else if (__builtin_types_compatible_p(typeof(x), struct static_key_false)) \ + branch = !arch_static_branch_jump(&(x)->key, true); \ + else \ + branch = ____wrong_branch_error(); \ + branch; \ +}) + +#define static_branch_unlikely(x) \ +({ \ + bool branch; \ + if (__builtin_types_compatible_p(typeof(x), struct static_key_true)) \ + branch = arch_static_branch(&(x)->key, true); \ + else if (__builtin_types_compatible_p(typeof(x), struct static_key_false)) \ + branch = arch_static_branch_jump(&(x)->key, false); \ + else \ + branch = ____wrong_branch_error(); \ + branch; \ +}) + +#else /* !HAVE_JUMP_LABEL */ + +#define static_branch_likely(x) static_key_true(&(x)->key) +#define static_branch_unlikely(x) static_key_false(&(x)->key) + +#endif /* HAVE_JUMP_LABEL */ + +/* + * Advanced usage; refcount, branch is enabled when: count != 0 + */ + +#define static_branch_inc(x) static_key_slow_inc(&(x)->key) +#define static_branch_dec(x) static_key_slow_dec(&(x)->key) + +/* + * Normal usage; boolean enable/disable. + */ + +#define static_branch_enable(x) static_key_enable(&(x)->key) +#define static_branch_disable(x) static_key_disable(&(x)->key) + #endif /* _LINUX_JUMP_LABEL_H */ #endif /* __ASSEMBLY__ */ diff --git a/kernel/jump_label.c b/kernel/jump_label.c index 52ebaca1b9fc..8d0cb621c5bc 100644 --- a/kernel/jump_label.c +++ b/kernel/jump_label.c @@ -54,7 +54,7 @@ jump_label_sort_entries(struct jump_entry *start, struct jump_entry *stop) sort(start, size, sizeof(struct jump_entry), jump_label_cmp, NULL); } -static void jump_label_update(struct static_key *key, int enable); +static void jump_label_update(struct static_key *key); void static_key_slow_inc(struct static_key *key) { @@ -63,13 +63,8 @@ void static_key_slow_inc(struct static_key *key) return; jump_label_lock(); - if (atomic_read(&key->enabled) == 0) { - if (!jump_label_get_branch_default(key)) - jump_label_update(key, JUMP_LABEL_ENABLE); - else - jump_label_update(key, JUMP_LABEL_DISABLE); - } - atomic_inc(&key->enabled); + if (atomic_inc_and_test(&key->enabled)) + jump_label_update(key); jump_label_unlock(); } EXPORT_SYMBOL_GPL(static_key_slow_inc); @@ -87,10 +82,7 @@ static void __static_key_slow_dec(struct static_key *key, atomic_inc(&key->enabled); schedule_delayed_work(work, rate_limit); } else { - if (!jump_label_get_branch_default(key)) - jump_label_update(key, JUMP_LABEL_DISABLE); - else - jump_label_update(key, JUMP_LABEL_ENABLE); + jump_label_update(key); } jump_label_unlock(); } @@ -149,7 +141,7 @@ static int __jump_label_text_reserved(struct jump_entry *iter_start, return 0; } -/* +/* * Update code which is definitely not currently executing. * Architectures which need heavyweight synchronization to modify * running code can override this to make the non-live update case @@ -158,37 +150,44 @@ static int __jump_label_text_reserved(struct jump_entry *iter_start, void __weak __init_or_module arch_jump_label_transform_static(struct jump_entry *entry, enum jump_label_type type) { - arch_jump_label_transform(entry, type); + arch_jump_label_transform(entry, type); +} + +static struct static_key *static_key_cast(jump_label_t key_addr) +{ + return (struct static_key *)(key_addr & ~1UL); +} + +static bool static_key_inv(jump_label_t key_addr) +{ + return key_addr & 1UL; +} + +static enum jump_label_type jump_label_type(struct jump_entry *entry) +{ + struct static_key *key = static_key_cast(iter->key); + bool true_branch = jump_label_get_branch_default(key); + bool state = static_key_enabled(key); + bool inv = static_key_inv(iter->key); + + return (true_branch ^ state) ^ inv; } static void __jump_label_update(struct static_key *key, struct jump_entry *entry, - struct jump_entry *stop, int enable) + struct jump_entry *stop) { - for (; (entry < stop) && - (entry->key == (jump_label_t)(unsigned long)key); - entry++) { + for (; (entry < stop) && (static_key_cast(entry->key) == key); entry++) { /* * entry->code set to 0 invalidates module init text sections * kernel_text_address() verifies we are not in core kernel * init code, see jump_label_invalidate_module_init(). */ if (entry->code && kernel_text_address(entry->code)) - arch_jump_label_transform(entry, enable); + arch_jump_label_transform(entry, jump_label_type(entry)); } } -static enum jump_label_type jump_label_type(struct static_key *key) -{ - bool true_branch = jump_label_get_branch_default(key); - bool state = static_key_enabled(key); - - if ((!true_branch && state) || (true_branch && !state)) - return JUMP_LABEL_ENABLE; - - return JUMP_LABEL_DISABLE; -} - void __init jump_label_init(void) { struct jump_entry *iter_start = __start___jump_table; @@ -202,8 +201,8 @@ void __init jump_label_init(void) for (iter = iter_start; iter < iter_stop; iter++) { struct static_key *iterk; - iterk = (struct static_key *)(unsigned long)iter->key; - arch_jump_label_transform_static(iter, jump_label_type(iterk)); + arch_jump_label_transform_static(iter, jump_label_type(iter)); + iterk = static_key_cast(iter->key); if (iterk == key) continue; @@ -243,17 +242,15 @@ static int __jump_label_mod_text_reserved(void *start, void *end) start, end); } -static void __jump_label_mod_update(struct static_key *key, int enable) +static void __jump_label_mod_update(struct static_key *key) { - struct static_key_mod *mod = key->next; + struct static_key_mod *mod; - while (mod) { + for (mod = key->next; mod; mod = mod->next) { struct module *m = mod->mod; __jump_label_update(key, mod->entries, - m->jump_entries + m->num_jump_entries, - enable); - mod = mod->next; + m->jump_entries + m->num_jump_entries) } } @@ -276,7 +273,8 @@ void jump_label_apply_nops(struct module *mod) return; for (iter = iter_start; iter < iter_stop; iter++) { - arch_jump_label_transform_static(iter, JUMP_LABEL_DISABLE); + if (jump_label_type(iter) == JUMP_LABEL_DISABLE) + arch_jump_label_transform_static(iter, JUMP_LABEL_DISABLE); } } @@ -297,7 +295,7 @@ static int jump_label_add_module(struct module *mod) for (iter = iter_start; iter < iter_stop; iter++) { struct static_key *iterk; - iterk = (struct static_key *)(unsigned long)iter->key; + iterk = static_key_cast(iter->key); if (iterk == key) continue; @@ -318,8 +316,7 @@ static int jump_label_add_module(struct module *mod) jlm->next = key->next; key->next = jlm; - if (jump_label_type(key) == JUMP_LABEL_ENABLE) - __jump_label_update(key, iter, iter_stop, JUMP_LABEL_ENABLE); + __jump_label_update(key, iter, iter_stop); } return 0; @@ -334,10 +331,10 @@ static void jump_label_del_module(struct module *mod) struct static_key_mod *jlm, **prev; for (iter = iter_start; iter < iter_stop; iter++) { - if (iter->key == (jump_label_t)(unsigned long)key) + if (static_key_cast(iter->key) == key) continue; - key = (struct static_key *)(unsigned long)iter->key; + key = static_key_cast(iter->key); if (within_module(iter->key, mod)) continue; @@ -439,7 +436,7 @@ int jump_label_text_reserved(void *start, void *end) return ret; } -static void jump_label_update(struct static_key *key, int enable) +static void jump_label_update(struct static_key *key) { struct jump_entry *stop = __stop___jump_table; struct jump_entry *entry = jump_label_get_entries(key); @@ -456,7 +453,7 @@ static void jump_label_update(struct static_key *key, int enable) #endif /* if there are no users, entry can be NULL */ if (entry) - __jump_label_update(key, entry, stop, enable); + __jump_label_update(key, entry, stop); } #endif ^ permalink raw reply related [flat|nested] 54+ messages in thread
* Re: Kernel broken on processors without performance counters 2015-07-23 14:49 ` Peter Zijlstra @ 2015-07-23 19:14 ` Jason Baron 2015-07-24 10:56 ` Peter Zijlstra 0 siblings, 1 reply; 54+ messages in thread From: Jason Baron @ 2015-07-23 19:14 UTC (permalink / raw) To: Peter Zijlstra Cc: Borislav Petkov, Andy Lutomirski, Thomas Gleixner, Mikulas Patocka, Paul Mackerras, Arnaldo Carvalho de Melo, Kees Cook, Andrea Arcangeli, Vince Weaver, hillf.zj, Valdis Kletnieks, linux-kernel@vger.kernel.org, Steven Rostedt On 07/23/2015 10:49 AM, Peter Zijlstra wrote: > On Thu, Jul 23, 2015 at 04:33:08PM +0200, Peter Zijlstra wrote: >> Lemme finish this and I'll post it. > Compile tested on x86_64 only.. > > Please have a look, I think you said I got some of the logic wrong, I've > not double checked that. > > I'll go write comments and double check things. > > --- > arch/arm/include/asm/jump_label.h | 22 +++++++- > arch/arm64/include/asm/jump_label.h | 22 +++++++- > arch/mips/include/asm/jump_label.h | 23 +++++++- > arch/powerpc/include/asm/jump_label.h | 23 +++++++- > arch/s390/include/asm/jump_label.h | 23 +++++++- > arch/sparc/include/asm/jump_label.h | 38 ++++++++++--- > arch/x86/include/asm/jump_label.h | 24 +++++++- > include/linux/jump_label.h | 101 +++++++++++++++++++++++++++++++++- > kernel/jump_label.c | 89 +++++++++++++++--------------- > 9 files changed, 297 insertions(+), 68 deletions(-) > > diff --git a/arch/arm/include/asm/jump_label.h b/arch/arm/include/asm/jump_label.h > index 5f337dc5c108..6c9789b33497 100644 > --- a/arch/arm/include/asm/jump_label.h > +++ b/arch/arm/include/asm/jump_label.h > @@ -13,14 +13,32 @@ > #define JUMP_LABEL_NOP "nop" > #endif > > -static __always_inline bool arch_static_branch(struct static_key *key) > +static __always_inline bool arch_static_branch(struct static_key *key, bool inv) > { > + unsigned long kval = (unsigned long)key + inv; > + > asm_volatile_goto("1:\n\t" > JUMP_LABEL_NOP "\n\t" > ".pushsection __jump_table, \"aw\"\n\t" > ".word 1b, %l[l_yes], %c0\n\t" > ".popsection\n\t" > - : : "i" (key) : : l_yes); > + : : "i" (kval) : : l_yes); > + > + return false; > +l_yes: > + return true; > +} > + > +static __always_inline bool arch_static_branch_jump(struct static_key *key, bool inv) > +{ > + unsigned long kval = (unsigned long)key + inv; > + > + asm_volatile_goto("1:\n\t" > + "b %l[l_yes]\n\t" > + ".pushsection __jump_table, \"aw\"\n\t" > + ".word 1b, %l[l_yes], %c0\n\t" > + ".popsection\n\t" > + : : "i" (kval) : : l_yes); > > return false; > l_yes: > diff --git a/arch/arm64/include/asm/jump_label.h b/arch/arm64/include/asm/jump_label.h > index c0e5165c2f76..e5cda5d75c62 100644 > --- a/arch/arm64/include/asm/jump_label.h > +++ b/arch/arm64/include/asm/jump_label.h > @@ -26,14 +26,32 @@ > > #define JUMP_LABEL_NOP_SIZE AARCH64_INSN_SIZE > > -static __always_inline bool arch_static_branch(struct static_key *key) > +static __always_inline bool arch_static_branch(struct static_key *key, bool inv) > { > + unsigned long kval = (unsigned long)key + inv; > + > asm goto("1: nop\n\t" > ".pushsection __jump_table, \"aw\"\n\t" > ".align 3\n\t" > ".quad 1b, %l[l_yes], %c0\n\t" > ".popsection\n\t" > - : : "i"(key) : : l_yes); > + : : "i"(kval) : : l_yes); > + > + return false; > +l_yes: > + return true; > +} > + > +static __always_inline bool arch_static_branch_jump(struct static_key *key, bool inv) > +{ > + unsigned long kval = (unsigned long)key + inv; > + > + asm goto("1: b %l[l_yes]\n\t" > + ".pushsection __jump_table, \"aw\"\n\t" > + ".align 3\n\t" > + ".quad 1b, %l[l_yes], %c0\n\t" > + ".popsection\n\t" > + : : "i"(kval) : : l_yes); > > return false; > l_yes: > diff --git a/arch/mips/include/asm/jump_label.h b/arch/mips/include/asm/jump_label.h > index 608aa57799c8..d9fca6f52a93 100644 > --- a/arch/mips/include/asm/jump_label.h > +++ b/arch/mips/include/asm/jump_label.h > @@ -26,14 +26,33 @@ > #define NOP_INSN "nop" > #endif > > -static __always_inline bool arch_static_branch(struct static_key *key) > +static __always_inline bool arch_static_branch(struct static_key *key, bool inv) > { > + unsigned long kval = (unsigned long)key + inv; > + > asm_volatile_goto("1:\t" NOP_INSN "\n\t" > "nop\n\t" > ".pushsection __jump_table, \"aw\"\n\t" > WORD_INSN " 1b, %l[l_yes], %0\n\t" > ".popsection\n\t" > - : : "i" (key) : : l_yes); > + : : "i" (kval) : : l_yes); > + > + return false; > +l_yes: > + return true; > +} > + > +static __always_inline bool arch_static_branch_jump(struct static_key *key, bool inv) > +{ > + unsigned long kval = (unsigned long)key + inv; > + > + asm_volatile_goto("1:\tj %l[l_yes]\n\t" > + "nop\n\t" > + ".pushsection __jump_table, \"aw\"\n\t" > + WORD_INSN " 1b, %l[l_yes], %0\n\t" > + ".popsection\n\t" > + : : "i" (kval) : : l_yes); > + > return false; > l_yes: > return true; > diff --git a/arch/powerpc/include/asm/jump_label.h b/arch/powerpc/include/asm/jump_label.h > index efbf9a322a23..c7cffedb1801 100644 > --- a/arch/powerpc/include/asm/jump_label.h > +++ b/arch/powerpc/include/asm/jump_label.h > @@ -18,14 +18,33 @@ > #define JUMP_ENTRY_TYPE stringify_in_c(FTR_ENTRY_LONG) > #define JUMP_LABEL_NOP_SIZE 4 > > -static __always_inline bool arch_static_branch(struct static_key *key) > +static __always_inline bool arch_static_branch(struct static_key *key, bool inv) > { > + unsigned long kval = (unsigned long)key + inv; > + > asm_volatile_goto("1:\n\t" > "nop\n\t" > ".pushsection __jump_table, \"aw\"\n\t" > JUMP_ENTRY_TYPE "1b, %l[l_yes], %c0\n\t" > ".popsection \n\t" > - : : "i" (key) : : l_yes); > + : : "i" (kval) : : l_yes); > + > + return false; > +l_yes: > + return true; > +} > + > +static __always_inline bool arch_static_branch_jump(struct static_key *key, bool inv) > +{ > + unsigned long kval = (unsigned long)key + inv; > + > + asm_volatile_goto("1:\n\t" > + "b %l[l_yes]\n\t" > + ".pushsection __jump_table, \"aw\"\n\t" > + JUMP_ENTRY_TYPE "1b, %l[l_yes], %c0\n\t" > + ".popsection \n\t" > + : : "i" (kval) : : l_yes); > + > return false; > l_yes: > return true; > diff --git a/arch/s390/include/asm/jump_label.h b/arch/s390/include/asm/jump_label.h > index 69972b7957ee..4734b09b9fce 100644 > --- a/arch/s390/include/asm/jump_label.h > +++ b/arch/s390/include/asm/jump_label.h > @@ -12,14 +12,33 @@ > * We use a brcl 0,2 instruction for jump labels at compile time so it > * can be easily distinguished from a hotpatch generated instruction. > */ > -static __always_inline bool arch_static_branch(struct static_key *key) > +static __always_inline bool arch_static_branch(struct static_key *key, bool inv) > { > + unsigned long kval = (unsigned long)key + inv; > + > asm_volatile_goto("0: brcl 0,"__stringify(JUMP_LABEL_NOP_OFFSET)"\n" > ".pushsection __jump_table, \"aw\"\n" > ".balign 8\n" > ".quad 0b, %l[label], %0\n" > ".popsection\n" > - : : "X" (key) : : label); > + : : "X" (kval) : : label); > + > + return false; > +label: > + return true; > +} > + > +static __always_inline bool arch_static_branch_jump(struct static_key *key, bool inv) > +{ > + unsigned long kval = (unsigned long)key + inv; > + > + asm_volatile_goto("0: j %l[l_yes]\n" > + ".pushsection __jump_table, \"aw\"\n" > + ".balign 8\n" > + ".quad 0b, %l[label], %0\n" > + ".popsection\n" > + : : "X" (kval) : : label); > + > return false; > label: > return true; > diff --git a/arch/sparc/include/asm/jump_label.h b/arch/sparc/include/asm/jump_label.h > index cc9b04a2b11b..764f3c21da21 100644 > --- a/arch/sparc/include/asm/jump_label.h > +++ b/arch/sparc/include/asm/jump_label.h > @@ -7,16 +7,36 @@ > > #define JUMP_LABEL_NOP_SIZE 4 > > -static __always_inline bool arch_static_branch(struct static_key *key) > +static __always_inline bool arch_static_branch(struct static_key *key, bool inv) > { > - asm_volatile_goto("1:\n\t" > - "nop\n\t" > - "nop\n\t" > - ".pushsection __jump_table, \"aw\"\n\t" > - ".align 4\n\t" > - ".word 1b, %l[l_yes], %c0\n\t" > - ".popsection \n\t" > - : : "i" (key) : : l_yes); > + unsigned long kval = (unsigned long)key + inv; > + > + asm_volatile_goto("1:\n\t" > + "nop\n\t" > + "nop\n\t" > + ".pushsection __jump_table, \"aw\"\n\t" > + ".align 4\n\t" > + ".word 1b, %l[l_yes], %c0\n\t" > + ".popsection \n\t" > + : : "i" (kval) : : l_yes); > + > + return false; > +l_yes: > + return true; > +} > + > +static __always_inline bool arch_static_branch_jump(struct static_key *key, bool inv) > +{ > + unsigned long kval = (unsigned long)key + inv; > + > + asm_volatile_goto("1:\n\t" > + "b %l[l_yes]\n\t" > + ".pushsection __jump_table, \"aw\"\n\t" > + ".align 4\n\t" > + ".word 1b, %l[l_yes], %c0\n\t" > + ".popsection \n\t" > + : : "i" (kval) : : l_yes); > + > return false; > l_yes: > return true; > diff --git a/arch/x86/include/asm/jump_label.h b/arch/x86/include/asm/jump_label.h > index a4c1cf7e93f8..43531baaee38 100644 > --- a/arch/x86/include/asm/jump_label.h > +++ b/arch/x86/include/asm/jump_label.h > @@ -16,15 +16,35 @@ > # define STATIC_KEY_INIT_NOP GENERIC_NOP5_ATOMIC > #endif > > -static __always_inline bool arch_static_branch(struct static_key *key) > +static __always_inline bool arch_static_branch(struct static_key *key, bool inv) > { > + unsigned long kval = (unsigned long)key + inv; > + > asm_volatile_goto("1:" > ".byte " __stringify(STATIC_KEY_INIT_NOP) "\n\t" > ".pushsection __jump_table, \"aw\" \n\t" > _ASM_ALIGN "\n\t" > _ASM_PTR "1b, %l[l_yes], %c0 \n\t" > ".popsection \n\t" > - : : "i" (key) : : l_yes); > + : : "i" (kval) : : l_yes); > + > + return false; > +l_yes: > + return true; > +} > + > +static __always_inline bool arch_static_branch_jump(struct static_key *key, bool inv) > +{ > + unsigned long kval = (unsigned long)key + inv; > + > + asm_volatile_goto("1:" > + "jmp %l[l_yes]\n\t" > + ".pushsection __jump_table, \"aw\" \n\t" > + _ASM_ALIGN "\n\t" > + _ASM_PTR "1b, %l[l_yes], %c0 \n\t" > + ".popsection \n\t" > + : : "i" (kval) : : l_yes); > + > return false; > l_yes: > return true; > diff --git a/include/linux/jump_label.h b/include/linux/jump_label.h > index f4de473f226b..4c97fed7acea 100644 > --- a/include/linux/jump_label.h > +++ b/include/linux/jump_label.h > @@ -122,7 +122,7 @@ static inline bool jump_label_get_branch_default(struct static_key *key) > > static __always_inline bool static_key_false(struct static_key *key) > { > - return arch_static_branch(key); > + return arch_static_branch(key, false); > } > > static __always_inline bool static_key_true(struct static_key *key) > @@ -213,6 +213,105 @@ static inline bool static_key_enabled(struct static_key *key) > return static_key_count(key) > 0; > } > > +static inline void static_key_enable(struct static_key *key) > +{ > + int count = static_key_count(key); > + > + WARN_ON_ONCE(count < 0 || count > 1); > + > + if (!count) > + static_key_slow_inc(key); > +} > + > +static inline void static_key_disable(struct static_key *key) > +{ > + int count = static_key_count(key); > + > + WARN_ON_ONCE(count < 0 || count > 1); > + > + if (count) > + static_key_slow_dec(key); > +} > + > + > +/* -------------------------------------------------------------------------- */ > + > +/* > + * Two type wrappers around static_key, such that we can use compile time > + * type differentiation to emit the right code. > + * > + * All the below code is macros in order to play type games. > + */ > + > +struct static_key_true { > + struct static_key key; > +}; > + > +struct static_key_false { > + struct static_key key; > +}; > + > +#define STATIC_KEY_TRUE_INIT (struct static_key_true) { .key = STATIC_KEY_INIT_TRUE, } > +#define STATIC_KEY_FALSE_INIT (struct static_key_false){ .key = STATIC_KEY_INIT_FALSE, } > + > +#define DEFINE_STATIC_KEY_TRUE(name) \ > + struct static_key_true name = STATIC_KEY_TRUE_INIT > + > +#define DEFINE_STATIC_KEY_FALSE(name) \ > + struct static_key_false name = STATIC_KEY_FALSE_INIT > + > +#ifdef HAVE_JUMP_LABEL > + > +/* > + * Combine the right initial value (type) with the right branch order and section > + * to generate the desired result. > + */ > + > +#define static_branch_likely(x) \ > +({ \ > + bool branch; \ > + if (__builtin_types_compatible_p(typeof(x), struct static_key_true)) \ > + branch = !arch_static_branch(&(x)->key, false); \ > + else if (__builtin_types_compatible_p(typeof(x), struct static_key_false)) \ > + branch = !arch_static_branch_jump(&(x)->key, true); \ > + else \ > + branch = ____wrong_branch_error(); \ > + branch; \ > +}) > + > +#define static_branch_unlikely(x) \ > +({ \ > + bool branch; \ > + if (__builtin_types_compatible_p(typeof(x), struct static_key_true)) \ > + branch = arch_static_branch(&(x)->key, true); \ > + else if (__builtin_types_compatible_p(typeof(x), struct static_key_false)) \ > + branch = arch_static_branch_jump(&(x)->key, false); \ > + else \ > + branch = ____wrong_branch_error(); \ > + branch; \ > +}) > + > +#else /* !HAVE_JUMP_LABEL */ > + > +#define static_branch_likely(x) static_key_true(&(x)->key) > +#define static_branch_unlikely(x) static_key_false(&(x)->key) > + > +#endif /* HAVE_JUMP_LABEL */ > + > +/* > + * Advanced usage; refcount, branch is enabled when: count != 0 > + */ > + > +#define static_branch_inc(x) static_key_slow_inc(&(x)->key) > +#define static_branch_dec(x) static_key_slow_dec(&(x)->key) > + > +/* > + * Normal usage; boolean enable/disable. > + */ > + > +#define static_branch_enable(x) static_key_enable(&(x)->key) > +#define static_branch_disable(x) static_key_disable(&(x)->key) > + > #endif /* _LINUX_JUMP_LABEL_H */ > > #endif /* __ASSEMBLY__ */ > diff --git a/kernel/jump_label.c b/kernel/jump_label.c > index 52ebaca1b9fc..8d0cb621c5bc 100644 > --- a/kernel/jump_label.c > +++ b/kernel/jump_label.c > @@ -54,7 +54,7 @@ jump_label_sort_entries(struct jump_entry *start, struct jump_entry *stop) > sort(start, size, sizeof(struct jump_entry), jump_label_cmp, NULL); > } > > -static void jump_label_update(struct static_key *key, int enable); > +static void jump_label_update(struct static_key *key); > > void static_key_slow_inc(struct static_key *key) > { > @@ -63,13 +63,8 @@ void static_key_slow_inc(struct static_key *key) > return; > > jump_label_lock(); > - if (atomic_read(&key->enabled) == 0) { > - if (!jump_label_get_branch_default(key)) > - jump_label_update(key, JUMP_LABEL_ENABLE); > - else > - jump_label_update(key, JUMP_LABEL_DISABLE); > - } > - atomic_inc(&key->enabled); > + if (atomic_inc_and_test(&key->enabled)) > + jump_label_update(key); > jump_label_unlock(); > } > EXPORT_SYMBOL_GPL(static_key_slow_inc); > @@ -87,10 +82,7 @@ static void __static_key_slow_dec(struct static_key *key, > atomic_inc(&key->enabled); > schedule_delayed_work(work, rate_limit); > } else { > - if (!jump_label_get_branch_default(key)) > - jump_label_update(key, JUMP_LABEL_DISABLE); > - else > - jump_label_update(key, JUMP_LABEL_ENABLE); > + jump_label_update(key); > } > jump_label_unlock(); > } > @@ -149,7 +141,7 @@ static int __jump_label_text_reserved(struct jump_entry *iter_start, > return 0; > } > > -/* > +/* > * Update code which is definitely not currently executing. > * Architectures which need heavyweight synchronization to modify > * running code can override this to make the non-live update case > @@ -158,37 +150,44 @@ static int __jump_label_text_reserved(struct jump_entry *iter_start, > void __weak __init_or_module arch_jump_label_transform_static(struct jump_entry *entry, > enum jump_label_type type) > { > - arch_jump_label_transform(entry, type); > + arch_jump_label_transform(entry, type); > +} > + > +static struct static_key *static_key_cast(jump_label_t key_addr) > +{ > + return (struct static_key *)(key_addr & ~1UL); > +} > + > +static bool static_key_inv(jump_label_t key_addr) > +{ > + return key_addr & 1UL; > +} > + > +static enum jump_label_type jump_label_type(struct jump_entry *entry) > +{ > + struct static_key *key = static_key_cast(iter->key); > + bool true_branch = jump_label_get_branch_default(key); > + bool state = static_key_enabled(key); > + bool inv = static_key_inv(iter->key); > + > + return (true_branch ^ state) ^ inv; iiuc...this allows both the old style keys to co-exist with the new ones. IE state wouldn't be set for the new ones. And inv shouldn't be set for the old ones. Is that correct? Thanks, -Jason ^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: Kernel broken on processors without performance counters 2015-07-23 19:14 ` Jason Baron @ 2015-07-24 10:56 ` Peter Zijlstra 2015-07-24 12:36 ` Peter Zijlstra 0 siblings, 1 reply; 54+ messages in thread From: Peter Zijlstra @ 2015-07-24 10:56 UTC (permalink / raw) To: Jason Baron Cc: Borislav Petkov, Andy Lutomirski, Thomas Gleixner, Mikulas Patocka, Paul Mackerras, Arnaldo Carvalho de Melo, Kees Cook, Andrea Arcangeli, Vince Weaver, hillf.zj, Valdis Kletnieks, linux-kernel@vger.kernel.org, Steven Rostedt On Thu, Jul 23, 2015 at 03:14:13PM -0400, Jason Baron wrote: > > +static enum jump_label_type jump_label_type(struct jump_entry *entry) > > +{ > > + struct static_key *key = static_key_cast(iter->key); > > + bool true_branch = jump_label_get_branch_default(key); > > + bool state = static_key_enabled(key); > > + bool inv = static_key_inv(iter->key); > > + > > + return (true_branch ^ state) ^ inv; > > iiuc...this allows both the old style keys to co-exist with the > new ones. IE state wouldn't be set for the new ones. And inv > shouldn't be set for the old ones. Is that correct? @state is the dynamic variable here, the other two are compile time. @true_branch denotes the default (compile time) value, and @inv denotes the (compile time) branch preference. So @state will still be set for the new ones, but you're right in that @inv will not be set for the 'legacy' stuff. This one little line needs a big comment. ^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: Kernel broken on processors without performance counters 2015-07-24 10:56 ` Peter Zijlstra @ 2015-07-24 12:36 ` Peter Zijlstra 2015-07-24 14:15 ` Jason Baron 0 siblings, 1 reply; 54+ messages in thread From: Peter Zijlstra @ 2015-07-24 12:36 UTC (permalink / raw) To: Jason Baron Cc: Borislav Petkov, Andy Lutomirski, Thomas Gleixner, Mikulas Patocka, Paul Mackerras, Arnaldo Carvalho de Melo, Kees Cook, Andrea Arcangeli, Vince Weaver, hillf.zj, Valdis Kletnieks, linux-kernel@vger.kernel.org, Steven Rostedt On Fri, Jul 24, 2015 at 12:56:02PM +0200, Peter Zijlstra wrote: > On Thu, Jul 23, 2015 at 03:14:13PM -0400, Jason Baron wrote: > > > +static enum jump_label_type jump_label_type(struct jump_entry *entry) > > > +{ > > > + struct static_key *key = static_key_cast(iter->key); > > > + bool true_branch = jump_label_get_branch_default(key); > > > + bool state = static_key_enabled(key); > > > + bool inv = static_key_inv(iter->key); > > > + > > > + return (true_branch ^ state) ^ inv; > > > > iiuc...this allows both the old style keys to co-exist with the > > new ones. IE state wouldn't be set for the new ones. And inv > > shouldn't be set for the old ones. Is that correct? > > @state is the dynamic variable here, the other two are compile time. > @true_branch denotes the default (compile time) value, and @inv denotes > the (compile time) branch preference. Ha!, so that wasn't entirely correct, it turned out @inv means arch_static_branch_jump(). That would let us remove the whole argument to the arch functions. That said, I generated the logic table for @inv meaning the branch type and then found a logic similar to what you outlined: /* * Combine the right initial value (type) with the right branch order * to generate the desired result. * * * type likely (1) unlikely (0) * -----------+-----------------------+------------------ * | | * true (1) | ... | ... * | NOP | JMP L * | <br-stmts> | 1: ... * | L: ... | * | | * | | L: <br-stmts> * | | jmp 1b * | | * -----------+-----------------------+------------------ * | | * false (0) | ... | ... * | JMP L | NOP * | <br-stmts> | 1: ... * | L: ... | * | | * | | L: <br-stmts> * | | jmp 1b * | | * -----------+-----------------------+------------------ * * The initial value is encoded in the LSB of static_key::entries, * type: 0 = false, 1 = true. * * The branch type is encoded in the LSB of jump_entry::key, * branch: 0 = unlikely, 1 = likely. * * This gives the following logic table: * * enabled type branch instuction * -----------------------------+----------- * 0 0 0 | NOP * 0 0 1 | JMP * 0 1 0 | NOP * 0 1 1 | JMP * * 1 0 0 | JMP * 1 0 1 | NOP * 1 1 0 | JMP * 1 1 1 | NOP * */ This gives a map: ins = enabled ^ branch, which shows @type to be redundant. And we can trivially switch over the old static_key_{true,false}() to emit the right branch type. Whcih would mean we could remove the type encoding entirely, but I'll leave that be for now, maybe it'll come in handy later or whatnot. ^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: Kernel broken on processors without performance counters 2015-07-24 12:36 ` Peter Zijlstra @ 2015-07-24 14:15 ` Jason Baron 0 siblings, 0 replies; 54+ messages in thread From: Jason Baron @ 2015-07-24 14:15 UTC (permalink / raw) To: Peter Zijlstra Cc: Borislav Petkov, Andy Lutomirski, Thomas Gleixner, Mikulas Patocka, Paul Mackerras, Arnaldo Carvalho de Melo, Kees Cook, Andrea Arcangeli, Vince Weaver, hillf.zj, Valdis Kletnieks, linux-kernel@vger.kernel.org, Steven Rostedt On 07/24/2015 08:36 AM, Peter Zijlstra wrote: > On Fri, Jul 24, 2015 at 12:56:02PM +0200, Peter Zijlstra wrote: >> On Thu, Jul 23, 2015 at 03:14:13PM -0400, Jason Baron wrote: >>>> +static enum jump_label_type jump_label_type(struct jump_entry *entry) >>>> +{ >>>> + struct static_key *key = static_key_cast(iter->key); >>>> + bool true_branch = jump_label_get_branch_default(key); >>>> + bool state = static_key_enabled(key); >>>> + bool inv = static_key_inv(iter->key); >>>> + >>>> + return (true_branch ^ state) ^ inv; >>> iiuc...this allows both the old style keys to co-exist with the >>> new ones. IE state wouldn't be set for the new ones. And inv >>> shouldn't be set for the old ones. Is that correct? >> @state is the dynamic variable here, the other two are compile time. >> @true_branch denotes the default (compile time) value, and @inv denotes >> the (compile time) branch preference. > Ha!, so that wasn't entirely correct, it turned out @inv means > arch_static_branch_jump(). > > That would let us remove the whole argument to the arch functions. > > That said, I generated the logic table for @inv meaning the branch type > and then found a logic similar to what you outlined: > > > /* > * Combine the right initial value (type) with the right branch order > * to generate the desired result. > * > * > * type likely (1) unlikely (0) > * -----------+-----------------------+------------------ > * | | > * true (1) | ... | ... > * | NOP | JMP L > * | <br-stmts> | 1: ... > * | L: ... | > * | | > * | | L: <br-stmts> > * | | jmp 1b > * | | > * -----------+-----------------------+------------------ > * | | > * false (0) | ... | ... > * | JMP L | NOP > * | <br-stmts> | 1: ... > * | L: ... | > * | | > * | | L: <br-stmts> > * | | jmp 1b > * | | > * -----------+-----------------------+------------------ > * > * The initial value is encoded in the LSB of static_key::entries, > * type: 0 = false, 1 = true. > * > * The branch type is encoded in the LSB of jump_entry::key, > * branch: 0 = unlikely, 1 = likely. > * > * This gives the following logic table: > * > * enabled type branch instuction > * -----------------------------+----------- > * 0 0 0 | NOP > * 0 0 1 | JMP > * 0 1 0 | NOP > * 0 1 1 | JMP > * > * 1 0 0 | JMP > * 1 0 1 | NOP > * 1 1 0 | JMP > * 1 1 1 | NOP > * > */ > > This gives a map: ins = enabled ^ branch, which shows @type to be > redundant. > > And we can trivially switch over the old static_key_{true,false}() to > emit the right branch type. > > Whcih would mean we could remove the type encoding entirely, but I'll > leave that be for now, maybe it'll come in handy later or whatnot. > I would just remove 'type'. Essentially, before we were storing the 'branch' with the key. However, in this new model the 'branch' is really associated with the branch location, since the same key can be used now with different branches. Thanks, -Jason ^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: Kernel broken on processors without performance counters 2015-07-23 14:19 ` Jason Baron 2015-07-23 14:33 ` Peter Zijlstra @ 2015-07-23 14:58 ` Peter Zijlstra 1 sibling, 0 replies; 54+ messages in thread From: Peter Zijlstra @ 2015-07-23 14:58 UTC (permalink / raw) To: Jason Baron Cc: Borislav Petkov, Andy Lutomirski, Thomas Gleixner, Mikulas Patocka, Paul Mackerras, Arnaldo Carvalho de Melo, Kees Cook, Andrea Arcangeli, Vince Weaver, hillf.zj, Valdis Kletnieks, linux-kernel@vger.kernel.org, Steven Rostedt On Thu, Jul 23, 2015 at 10:19:52AM -0400, Jason Baron wrote: > > > > #define static_branch_likely(x) \ > > ({ \ > > bool branch; \ > > if (__builtin_types_compatible_p(typeof(x), struct static_key_true)) \ > > branch = !arch_static_branch(&(x)->key, false); \ > > else if (__builtin_types_compatible_p(typeof(x), struct static_key_false)) \ > > branch = !arch_static_branch_jump(&(x)->key, true); \ > > else \ > > branch = ____wrong_branch_error(); \ > > branch; \ > > }) > > > > #define static_branch_unlikely(x) \ > > ({ \ > > bool branch; \ > > if (__builtin_types_compatible_p(typeof(x), struct static_key_true)) \ > > branch = arch_static_branch(&(x)->key, true); \ > > else if (__builtin_types_compatible_p(typeof(x), struct static_key_false)) \ > > branch = arch_static_branch_jump(&(x)->key, false); \ > > else \ > > branch = ____wrong_branch_error(); \ > > branch; \ > > }) > > > > In 'static_branch_unlikely()', I think arch_static_branch() and > arch_static_branch_jump() are reversed. Yes, you're right. But I think I need a nap before touching this stuff again :-) ^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: Kernel broken on processors without performance counters 2015-07-23 10:42 ` Peter Zijlstra 2015-07-23 10:53 ` Borislav Petkov 2015-07-23 14:19 ` Jason Baron @ 2015-07-23 15:34 ` Steven Rostedt 2015-07-23 17:08 ` Peter Zijlstra 2 siblings, 1 reply; 54+ messages in thread From: Steven Rostedt @ 2015-07-23 15:34 UTC (permalink / raw) To: Peter Zijlstra Cc: Jason Baron, Borislav Petkov, Andy Lutomirski, Thomas Gleixner, Mikulas Patocka, Paul Mackerras, Arnaldo Carvalho de Melo, Kees Cook, Andrea Arcangeli, Vince Weaver, hillf.zj, Valdis Kletnieks, linux-kernel@vger.kernel.org On Thu, 23 Jul 2015 12:42:15 +0200 Peter Zijlstra <peterz@infradead.org> wrote: > static __always_inline bool arch_static_branch_jump(struct static_key *key, bool inv) > { > if (!inv) { > asm_volatile_goto("1:" > "jmp %l[l_yes]\n\t" And what happens when this gets converted to a two byte jump? -- Steve > ".pushsection __jump_table, \"aw\" \n\t" > _ASM_ALIGN "\n\t" > _ASM_PTR "1b, %l[l_yes], %c0 \n\t" > ".popsection \n\t" > : : "i" (key) : : l_yes); > } else { > asm_volatile_goto("1:" > "jmp %l[l_yes]\n\t" > ".pushsection __jump_table_inv, \"aw\" \n\t" > _ASM_ALIGN "\n\t" > _ASM_PTR "1b, %l[l_yes], %c0 \n\t" > ".popsection \n\t" > : : "i" (key) : : l_yes); > } > return false; > l_yes: > return true; > } > > ^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: Kernel broken on processors without performance counters 2015-07-23 15:34 ` Steven Rostedt @ 2015-07-23 17:08 ` Peter Zijlstra 2015-07-23 17:18 ` Steven Rostedt ` (3 more replies) 0 siblings, 4 replies; 54+ messages in thread From: Peter Zijlstra @ 2015-07-23 17:08 UTC (permalink / raw) To: Steven Rostedt Cc: Jason Baron, Borislav Petkov, Andy Lutomirski, Thomas Gleixner, Mikulas Patocka, Paul Mackerras, Arnaldo Carvalho de Melo, Kees Cook, Andrea Arcangeli, Vince Weaver, hillf.zj, Valdis Kletnieks, linux-kernel@vger.kernel.org On Thu, Jul 23, 2015 at 11:34:50AM -0400, Steven Rostedt wrote: > On Thu, 23 Jul 2015 12:42:15 +0200 > Peter Zijlstra <peterz@infradead.org> wrote: > > > static __always_inline bool arch_static_branch_jump(struct static_key *key, bool inv) > > { > > if (!inv) { > > asm_volatile_goto("1:" > > "jmp %l[l_yes]\n\t" > > And what happens when this gets converted to a two byte jump? > That would be bad, how can we force it to emit 5 bytes? ^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: Kernel broken on processors without performance counters 2015-07-23 17:08 ` Peter Zijlstra @ 2015-07-23 17:18 ` Steven Rostedt 2015-07-23 17:33 ` Jason Baron ` (2 subsequent siblings) 3 siblings, 0 replies; 54+ messages in thread From: Steven Rostedt @ 2015-07-23 17:18 UTC (permalink / raw) To: Peter Zijlstra Cc: Jason Baron, Borislav Petkov, Andy Lutomirski, Thomas Gleixner, Mikulas Patocka, Paul Mackerras, Arnaldo Carvalho de Melo, Kees Cook, Andrea Arcangeli, Vince Weaver, hillf.zj, Valdis Kletnieks, linux-kernel@vger.kernel.org On Thu, 23 Jul 2015 19:08:11 +0200 Peter Zijlstra <peterz@infradead.org> wrote: > On Thu, Jul 23, 2015 at 11:34:50AM -0400, Steven Rostedt wrote: > > On Thu, 23 Jul 2015 12:42:15 +0200 > > Peter Zijlstra <peterz@infradead.org> wrote: > > > > > static __always_inline bool arch_static_branch_jump(struct static_key *key, bool inv) > > > { > > > if (!inv) { > > > asm_volatile_goto("1:" > > > "jmp %l[l_yes]\n\t" > > > > And what happens when this gets converted to a two byte jump? > > > > That would be bad, how can we force it to emit 5 bytes? No idea, but I could pull out that old code that converted them :-) The complexity was in the elf parser that was run at kernel compile time. It was based on the same code that does the work with record-mcount.c to find all the mcount callers and made the sections for them. In fact, it wasn't much different, as record-mcount.c will convert the black listed sections into nops, so they do not bother calling mcount at all. But those sections were not recorded, as they were blacklisted anyway (not whitelisted really, as to be a blacklisted section, it just had to not be in the whitelisted list). If we got the jmp conversion in, I was going to clean up the code such that both record-mcount.c and the jmp conversions used the same code where applicable. I would probably still convert every jmp to a nop (2 or 5 byte), and then at boot up convert those back to jmps that are needed. -- Steve ^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: Kernel broken on processors without performance counters 2015-07-23 17:08 ` Peter Zijlstra 2015-07-23 17:18 ` Steven Rostedt @ 2015-07-23 17:33 ` Jason Baron 2015-07-23 18:12 ` Steven Rostedt 2015-07-23 19:02 ` Peter Zijlstra 2015-07-23 17:35 ` Andy Lutomirski 2015-07-23 17:54 ` Borislav Petkov 3 siblings, 2 replies; 54+ messages in thread From: Jason Baron @ 2015-07-23 17:33 UTC (permalink / raw) To: Peter Zijlstra, Steven Rostedt Cc: Borislav Petkov, Andy Lutomirski, Thomas Gleixner, Mikulas Patocka, Paul Mackerras, Arnaldo Carvalho de Melo, Kees Cook, Andrea Arcangeli, Vince Weaver, hillf.zj, Valdis Kletnieks, linux-kernel@vger.kernel.org On 07/23/2015 01:08 PM, Peter Zijlstra wrote: > On Thu, Jul 23, 2015 at 11:34:50AM -0400, Steven Rostedt wrote: >> On Thu, 23 Jul 2015 12:42:15 +0200 >> Peter Zijlstra <peterz@infradead.org> wrote: >> >>> static __always_inline bool arch_static_branch_jump(struct static_key *key, bool inv) >>> { >>> if (!inv) { >>> asm_volatile_goto("1:" >>> "jmp %l[l_yes]\n\t" >> And what happens when this gets converted to a two byte jump? >> > That would be bad, how can we force it to emit 5 bytes? hmm....I don't think that's an issue, the patching code can detect if its a 2-byte jump - 0xeb, or 5-byte: 0xe9, and do the correct no-op. Same going the other way. See the code I posted a few mails back. In fact, this gets us to the smaller 2-byte no-ops in cases where we are initialized to jump. Thanks, -Jason ^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: Kernel broken on processors without performance counters 2015-07-23 17:33 ` Jason Baron @ 2015-07-23 18:12 ` Steven Rostedt 2015-07-23 19:02 ` Peter Zijlstra 1 sibling, 0 replies; 54+ messages in thread From: Steven Rostedt @ 2015-07-23 18:12 UTC (permalink / raw) To: Jason Baron Cc: Peter Zijlstra, Borislav Petkov, Andy Lutomirski, Thomas Gleixner, Mikulas Patocka, Paul Mackerras, Arnaldo Carvalho de Melo, Kees Cook, Andrea Arcangeli, Vince Weaver, hillf.zj, Valdis Kletnieks, linux-kernel@vger.kernel.org On Thu, 23 Jul 2015 13:33:36 -0400 Jason Baron <jasonbaron0@gmail.com> wrote: > On 07/23/2015 01:08 PM, Peter Zijlstra wrote: > > On Thu, Jul 23, 2015 at 11:34:50AM -0400, Steven Rostedt wrote: > >> On Thu, 23 Jul 2015 12:42:15 +0200 > >> Peter Zijlstra <peterz@infradead.org> wrote: > >> > >>> static __always_inline bool arch_static_branch_jump(struct static_key *key, bool inv) > >>> { > >>> if (!inv) { > >>> asm_volatile_goto("1:" > >>> "jmp %l[l_yes]\n\t" > >> And what happens when this gets converted to a two byte jump? > >> > > That would be bad, how can we force it to emit 5 bytes? > hmm....I don't think that's an issue, the patching code can > detect if its a 2-byte jump - 0xeb, or 5-byte: 0xe9, and do > the correct no-op. Same going the other way. See the code > I posted a few mails back. In fact, this gets us to the > smaller 2-byte no-ops in cases where we are initialized > to jump. > Ah right, and I already have the code that checks that (from the original plan). -- Steve ^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: Kernel broken on processors without performance counters 2015-07-23 17:33 ` Jason Baron 2015-07-23 18:12 ` Steven Rostedt @ 2015-07-23 19:02 ` Peter Zijlstra 1 sibling, 0 replies; 54+ messages in thread From: Peter Zijlstra @ 2015-07-23 19:02 UTC (permalink / raw) To: Jason Baron Cc: Steven Rostedt, Borislav Petkov, Andy Lutomirski, Thomas Gleixner, Mikulas Patocka, Paul Mackerras, Arnaldo Carvalho de Melo, Kees Cook, Andrea Arcangeli, Vince Weaver, hillf.zj, Valdis Kletnieks, linux-kernel@vger.kernel.org On Thu, Jul 23, 2015 at 01:33:36PM -0400, Jason Baron wrote: > > That would be bad, how can we force it to emit 5 bytes? > hmm....I don't think that's an issue, the patching code can > detect if its a 2-byte jump - 0xeb, or 5-byte: 0xe9, and do > the correct no-op. Same going the other way. See the code > I posted a few mails back. In fact, this gets us to the > smaller 2-byte no-ops in cases where we are initialized > to jump. Ah yes, I looked right over that. I think that if we want to go do that, it should be a separate patch though. ^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: Kernel broken on processors without performance counters 2015-07-23 17:08 ` Peter Zijlstra 2015-07-23 17:18 ` Steven Rostedt 2015-07-23 17:33 ` Jason Baron @ 2015-07-23 17:35 ` Andy Lutomirski 2015-07-23 17:54 ` Borislav Petkov 3 siblings, 0 replies; 54+ messages in thread From: Andy Lutomirski @ 2015-07-23 17:35 UTC (permalink / raw) To: Peter Zijlstra Cc: hillf. zj, Andrea Arcangeli, Jason Baron, Paul Mackerras, Thomas Gleixner, Kees Cook, Vince Weaver, Borislav Petkov, Valdis Kletnieks, Steven Rostedt, linux-kernel@vger.kernel.org, Arnaldo Carvalho de Melo, Mikulas Patocka On Jul 23, 2015 10:08 AM, "Peter Zijlstra" <peterz@infradead.org> wrote: > > On Thu, Jul 23, 2015 at 11:34:50AM -0400, Steven Rostedt wrote: > > On Thu, 23 Jul 2015 12:42:15 +0200 > > Peter Zijlstra <peterz@infradead.org> wrote: > > > > > static __always_inline bool arch_static_branch_jump(struct static_key *key, bool inv) > > > { > > > if (!inv) { > > > asm_volatile_goto("1:" > > > "jmp %l[l_yes]\n\t" > > > > And what happens when this gets converted to a two byte jump? > > > > That would be bad, how can we force it to emit 5 bytes? jmp.d32 on newer toolchains IIRC. --Andy ^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: Kernel broken on processors without performance counters 2015-07-23 17:08 ` Peter Zijlstra ` (2 preceding siblings ...) 2015-07-23 17:35 ` Andy Lutomirski @ 2015-07-23 17:54 ` Borislav Petkov 2015-07-23 19:02 ` Peter Zijlstra 3 siblings, 1 reply; 54+ messages in thread From: Borislav Petkov @ 2015-07-23 17:54 UTC (permalink / raw) To: Peter Zijlstra Cc: Steven Rostedt, Jason Baron, Andy Lutomirski, Thomas Gleixner, Mikulas Patocka, Paul Mackerras, Arnaldo Carvalho de Melo, Kees Cook, Andrea Arcangeli, Vince Weaver, hillf.zj, Valdis Kletnieks, linux-kernel@vger.kernel.org On Thu, Jul 23, 2015 at 07:08:11PM +0200, Peter Zijlstra wrote: > That would be bad, how can we force it to emit 5 bytes? .byte 0xe9 like we used to do in static_cpu_has_safe(). Or you can copy the insn sizing from the alternatives macros which I added recently. -- Regards/Gruss, Boris. ECO tip #101: Trim your mails when you reply. -- ^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: Kernel broken on processors without performance counters 2015-07-23 17:54 ` Borislav Petkov @ 2015-07-23 19:02 ` Peter Zijlstra 2015-07-24 5:29 ` Borislav Petkov 0 siblings, 1 reply; 54+ messages in thread From: Peter Zijlstra @ 2015-07-23 19:02 UTC (permalink / raw) To: Borislav Petkov Cc: Steven Rostedt, Jason Baron, Andy Lutomirski, Thomas Gleixner, Mikulas Patocka, Paul Mackerras, Arnaldo Carvalho de Melo, Kees Cook, Andrea Arcangeli, Vince Weaver, hillf.zj, Valdis Kletnieks, linux-kernel@vger.kernel.org On Thu, Jul 23, 2015 at 07:54:36PM +0200, Borislav Petkov wrote: > On Thu, Jul 23, 2015 at 07:08:11PM +0200, Peter Zijlstra wrote: > > That would be bad, how can we force it to emit 5 bytes? > > .byte 0xe9 like we used to do in static_cpu_has_safe(). Like so then? static __always_inline bool arch_static_branch_jump(struct static_key *key, bool inv) { unsigned long kval = (unsigned long)key + inv; asm_volatile_goto("1:" ".byte 0xe9\n\t .long %l[l_yes]\n\t" ".pushsection __jump_table, \"aw\" \n\t" _ASM_ALIGN "\n\t" _ASM_PTR "1b, %l[l_yes], %c0 \n\t" ".popsection \n\t" : : "i" (kval) : : l_yes); return false; l_yes: return true; } ^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: Kernel broken on processors without performance counters 2015-07-23 19:02 ` Peter Zijlstra @ 2015-07-24 5:29 ` Borislav Petkov 2015-07-24 10:36 ` Peter Zijlstra 0 siblings, 1 reply; 54+ messages in thread From: Borislav Petkov @ 2015-07-24 5:29 UTC (permalink / raw) To: Peter Zijlstra Cc: Steven Rostedt, Jason Baron, Andy Lutomirski, Thomas Gleixner, Mikulas Patocka, Paul Mackerras, Arnaldo Carvalho de Melo, Kees Cook, Andrea Arcangeli, Vince Weaver, hillf.zj, Valdis Kletnieks, linux-kernel@vger.kernel.org On Thu, Jul 23, 2015 at 09:02:14PM +0200, Peter Zijlstra wrote: > On Thu, Jul 23, 2015 at 07:54:36PM +0200, Borislav Petkov wrote: > > On Thu, Jul 23, 2015 at 07:08:11PM +0200, Peter Zijlstra wrote: > > > That would be bad, how can we force it to emit 5 bytes? > > > > .byte 0xe9 like we used to do in static_cpu_has_safe(). > > Like so then? > > static __always_inline bool arch_static_branch_jump(struct static_key *key, bool inv) > { > unsigned long kval = (unsigned long)key + inv; > > asm_volatile_goto("1:" > ".byte 0xe9\n\t .long %l[l_yes]\n\t" > ".pushsection __jump_table, \"aw\" \n\t" > _ASM_ALIGN "\n\t" > _ASM_PTR "1b, %l[l_yes], %c0 \n\t" > ".popsection \n\t" > : : "i" (kval) : : l_yes); > > return false; > l_yes: > return true; > } Yap. But, we can do even better and note down what kind of JMP the compiler generated and teach __jump_label_transform() to generate the right one. Maybe this struct jump_entry would get a flags member or so. This way we're optimal. Methinks... -- Regards/Gruss, Boris. ECO tip #101: Trim your mails when you reply. -- ^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: Kernel broken on processors without performance counters 2015-07-24 5:29 ` Borislav Petkov @ 2015-07-24 10:36 ` Peter Zijlstra 0 siblings, 0 replies; 54+ messages in thread From: Peter Zijlstra @ 2015-07-24 10:36 UTC (permalink / raw) To: Borislav Petkov Cc: Steven Rostedt, Jason Baron, Andy Lutomirski, Thomas Gleixner, Mikulas Patocka, Paul Mackerras, Arnaldo Carvalho de Melo, Kees Cook, Andrea Arcangeli, Vince Weaver, hillf.zj, Valdis Kletnieks, linux-kernel@vger.kernel.org On Fri, Jul 24, 2015 at 07:29:05AM +0200, Borislav Petkov wrote: > On Thu, Jul 23, 2015 at 09:02:14PM +0200, Peter Zijlstra wrote: > > On Thu, Jul 23, 2015 at 07:54:36PM +0200, Borislav Petkov wrote: > > > On Thu, Jul 23, 2015 at 07:08:11PM +0200, Peter Zijlstra wrote: > > > > That would be bad, how can we force it to emit 5 bytes? > > > > > > .byte 0xe9 like we used to do in static_cpu_has_safe(). > > > > Like so then? > > > > static __always_inline bool arch_static_branch_jump(struct static_key *key, bool inv) > > { > > unsigned long kval = (unsigned long)key + inv; > > > > asm_volatile_goto("1:" > > ".byte 0xe9\n\t .long %l[l_yes]\n\t" > > ".pushsection __jump_table, \"aw\" \n\t" > > _ASM_ALIGN "\n\t" > > _ASM_PTR "1b, %l[l_yes], %c0 \n\t" > > ".popsection \n\t" > > : : "i" (kval) : : l_yes); > > > > return false; > > l_yes: > > return true; > > } > > Yap. > > But, we can do even better and note down what kind of JMP the compiler > generated and teach __jump_label_transform() to generate the right one. > Maybe this struct jump_entry would get a flags member or so. This way > we're optimal. > > Methinks... Yes, Jason and Steve already have patches to go do that, but I'd really like to keep that separate for now, this thing is big enough as is. ^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: Kernel broken on processors without performance counters 2015-07-22 4:24 ` Borislav Petkov 2015-07-22 17:06 ` Jason Baron @ 2015-07-22 20:43 ` Mikulas Patocka 1 sibling, 0 replies; 54+ messages in thread From: Mikulas Patocka @ 2015-07-22 20:43 UTC (permalink / raw) To: Borislav Petkov Cc: Jason Baron, Peter Zijlstra, Andy Lutomirski, Thomas Gleixner, Paul Mackerras, Arnaldo Carvalho de Melo, Kees Cook, Andrea Arcangeli, Vince Weaver, hillf.zj, Valdis Kletnieks, linux-kernel@vger.kernel.org, Steven Rostedt On Wed, 22 Jul 2015, Borislav Petkov wrote: > On Tue, Jul 21, 2015 at 02:50:25PM -0400, Jason Baron wrote: > > hmmm...so this is a case where need to the default the branch > > to the out-of-line branch at boot. That is, we can't just enable > > the out-of-line branch at boot time, b/c it might be too late at > > that point? IE native_sched_clock() gets called very early? > > Well, even the layout is wrong here. The optimal thing would be to have: > > NOP > rdtsc > > unlikely: > /* read jiffies */ > > at build time. And then at boot time, patch in the JMP over the NOP on > !use_tsc boxes. And RDTSC works always, no matter how early. RDTSC doesn't work on 486... Mikulas ^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: Kernel broken on processors without performance counters 2015-07-21 15:49 ` Peter Zijlstra 2015-07-21 15:51 ` Andy Lutomirski @ 2015-07-21 15:53 ` Thomas Gleixner 2015-07-21 15:54 ` Peter Zijlstra 2 siblings, 0 replies; 54+ messages in thread From: Thomas Gleixner @ 2015-07-21 15:53 UTC (permalink / raw) To: Peter Zijlstra Cc: Andy Lutomirski, Jason Baron, Mikulas Patocka, Paul Mackerras, Arnaldo Carvalho de Melo, Kees Cook, Andrea Arcangeli, Vince Weaver, hillf.zj, Valdis Kletnieks, linux-kernel@vger.kernel.org, Borislav Petkov, Steven Rostedt On Tue, 21 Jul 2015, Peter Zijlstra wrote: > On Tue, Jul 21, 2015 at 05:43:27PM +0200, Thomas Gleixner wrote: > > > > +#define static_branch_inc(_k) static_key_slow_inc(&(_k)->key) > > > > +#define static_branch_dec(_k) static_key_slow_dec(&(_k)->key) > > > > Inlines please > > > > > > +/* > > > > + * Normal usage; boolean enable/disable. > > > > + */ > > > > + > > > > +#define static_branch_enable(_k) static_key_enable(&(_k)->key) > > > > +#define static_branch_disable(_k) static_key_disable(&(_k)->key) > > > > Ditto > > > > All in all much more understandable than the existing horror. > > They cannot in fact be inlines because we play type tricks. Note how _k > can be either struct static_likely_key or struct static_unlikely_key. Indeed. Care to add a comment? Thanks, tglx ^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: Kernel broken on processors without performance counters 2015-07-21 15:49 ` Peter Zijlstra 2015-07-21 15:51 ` Andy Lutomirski 2015-07-21 15:53 ` Thomas Gleixner @ 2015-07-21 15:54 ` Peter Zijlstra 2 siblings, 0 replies; 54+ messages in thread From: Peter Zijlstra @ 2015-07-21 15:54 UTC (permalink / raw) To: Thomas Gleixner Cc: Andy Lutomirski, Jason Baron, Mikulas Patocka, Paul Mackerras, Arnaldo Carvalho de Melo, Kees Cook, Andrea Arcangeli, Vince Weaver, hillf.zj, Valdis Kletnieks, linux-kernel@vger.kernel.org, Borislav Petkov, Steven Rostedt On Tue, Jul 21, 2015 at 05:49:59PM +0200, Peter Zijlstra wrote: > On Tue, Jul 21, 2015 at 05:43:27PM +0200, Thomas Gleixner wrote: > > On Tue, 21 Jul 2015, Peter Zijlstra wrote: > > > > > > -#endif /* _LINUX_JUMP_LABEL_H */ > > > > +static inline void static_key_enable(struct static_key *key) > > > > +{ > > > > + int count = static_key_count(key); > > > > + > > > > + WARN_ON_ONCE(count < 0 || count > 1); > > > > + > > > > + if (!count) > > > > + static_key_slow_inc(key); > > > > +} > > > > + > > > > +static inline void static_key_disable(struct static_key *key) > > > > +{ > > > > + int count = static_key_count(key); > > > > + > > > > + WARN_ON_ONCE(count < 0 || count > 1); > > > > + > > > > + if (count) > > > > + static_key_slow_dec(key); > > > > +} > > > > The functions above are not part of the interface which should be used > > in code, right? > > They are in fact. They make it easier to deal with the refcount thing > when all you want is boolean states. That is, things like sched_feat_keys[] which is an array of static keys, the split types doesn't work -- an array can after all have only one type. In such cases you do have to be very careful to not wreck things. ^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: Kernel broken on processors without performance counters 2015-07-08 20:04 ` Jason Baron 2015-07-09 0:36 ` Andy Lutomirski @ 2015-07-09 17:11 ` Peter Zijlstra 2015-07-09 19:15 ` Jason Baron 1 sibling, 1 reply; 54+ messages in thread From: Peter Zijlstra @ 2015-07-09 17:11 UTC (permalink / raw) To: Jason Baron Cc: Andy Lutomirski, Mikulas Patocka, Paul Mackerras, Arnaldo Carvalho de Melo, Kees Cook, Andrea Arcangeli, Vince Weaver, hillf.zj, Valdis Kletnieks, linux-kernel@vger.kernel.org On Wed, Jul 08, 2015 at 04:04:32PM -0400, Jason Baron wrote: > So the 'static_key_false' is really branch is initially false. We had > a naming discussion before, but if ppl think its confusing, > 'static_key_init_false', or 'static_key_default_false' might be better, > or other ideas.... I agree its confusing. Jason, while I have you here on the subject; mind posting that true/fase thing again? That makes more sense than the inc/dec thing for some usage sites. ^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: Kernel broken on processors without performance counters 2015-07-09 17:11 ` Peter Zijlstra @ 2015-07-09 19:15 ` Jason Baron 0 siblings, 0 replies; 54+ messages in thread From: Jason Baron @ 2015-07-09 19:15 UTC (permalink / raw) To: Peter Zijlstra Cc: Andy Lutomirski, Mikulas Patocka, Paul Mackerras, Arnaldo Carvalho de Melo, Kees Cook, Andrea Arcangeli, Vince Weaver, hillf.zj, Valdis Kletnieks, linux-kernel@vger.kernel.org On 07/09/2015 01:11 PM, Peter Zijlstra wrote: > On Wed, Jul 08, 2015 at 04:04:32PM -0400, Jason Baron wrote: >> So the 'static_key_false' is really branch is initially false. We had >> a naming discussion before, but if ppl think its confusing, >> 'static_key_init_false', or 'static_key_default_false' might be better, >> or other ideas.... I agree its confusing. > Jason, while I have you here on the subject; mind posting that true/fase > thing again? That makes more sense than the inc/dec thing for some usage > sites. > > Ok, sounds good. I will try and get to this next week. Thanks, -Jason ^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: Kernel broken on processors without performance counters 2015-07-08 15:17 Kernel broken on processors without performance counters Mikulas Patocka 2015-07-08 16:07 ` Peter Zijlstra @ 2015-07-14 9:35 ` Borislav Petkov 2015-07-14 12:43 ` Mikulas Patocka 1 sibling, 1 reply; 54+ messages in thread From: Borislav Petkov @ 2015-07-14 9:35 UTC (permalink / raw) To: Mikulas Patocka Cc: Andy Lutomirski, Peter Zijlstra, Paul Mackerras, Arnaldo Carvalho de Melo, Kees Cook, Andrea Arcangeli, Vince Weaver, hillf.zj, Valdis Kletnieks, linux-kernel On Wed, Jul 08, 2015 at 11:17:38AM -0400, Mikulas Patocka wrote: > Hi > > I found out that the patch a66734297f78707ce39d756b656bfae861d53f62 breaks > the kernel on processors without performance counters, such as AMD K6-3. Out of curiosity: are you actually using this piece of computer history or you're only booting new kernels for regressions? :) Thanks. -- Regards/Gruss, Boris. ECO tip #101: Trim your mails when you reply. -- ^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: Kernel broken on processors without performance counters 2015-07-14 9:35 ` Borislav Petkov @ 2015-07-14 12:43 ` Mikulas Patocka 0 siblings, 0 replies; 54+ messages in thread From: Mikulas Patocka @ 2015-07-14 12:43 UTC (permalink / raw) To: Borislav Petkov Cc: Andy Lutomirski, Peter Zijlstra, Paul Mackerras, Arnaldo Carvalho de Melo, Kees Cook, Andrea Arcangeli, Vince Weaver, hillf.zj, Valdis Kletnieks, linux-kernel On Tue, 14 Jul 2015, Borislav Petkov wrote: > On Wed, Jul 08, 2015 at 11:17:38AM -0400, Mikulas Patocka wrote: > > Hi > > > > I found out that the patch a66734297f78707ce39d756b656bfae861d53f62 breaks > > the kernel on processors without performance counters, such as AMD K6-3. > > Out of curiosity: are you actually using this piece of computer history > or you're only booting new kernels for regressions? > > :) > > Thanks. I use it, but mostly for connecting to other machines. I wanted to test some other patch, I compiled a new kernel on K6-3 and found out this crash. Mikulas ^ permalink raw reply [flat|nested] 54+ messages in thread
end of thread, other threads:[~2015-07-24 14:15 UTC | newest] Thread overview: 54+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2015-07-08 15:17 Kernel broken on processors without performance counters Mikulas Patocka 2015-07-08 16:07 ` Peter Zijlstra 2015-07-08 16:54 ` Mikulas Patocka 2015-07-09 17:23 ` [PATCH] x86: Fix static_key in load_mm_cr4() Peter Zijlstra 2015-07-09 19:11 ` Mikulas Patocka 2015-07-10 8:27 ` [tip:perf/urgent] x86, perf: Fix static_key bug " tip-bot for Peter Zijlstra 2015-07-08 17:37 ` Kernel broken on processors without performance counters Andy Lutomirski 2015-07-08 20:04 ` Jason Baron 2015-07-09 0:36 ` Andy Lutomirski 2015-07-10 14:13 ` Peter Zijlstra 2015-07-10 15:29 ` Jason Baron 2015-07-21 8:21 ` Peter Zijlstra 2015-07-21 15:43 ` Thomas Gleixner 2015-07-21 15:49 ` Peter Zijlstra 2015-07-21 15:51 ` Andy Lutomirski 2015-07-21 16:12 ` Peter Zijlstra 2015-07-21 16:57 ` Jason Baron 2015-07-23 14:54 ` Steven Rostedt 2015-07-21 18:15 ` Borislav Petkov 2015-07-21 18:50 ` Jason Baron 2015-07-21 18:54 ` Andy Lutomirski 2015-07-21 19:00 ` Valdis.Kletnieks 2015-07-21 19:29 ` Andy Lutomirski 2015-07-21 23:49 ` Valdis.Kletnieks 2015-07-22 4:24 ` Borislav Petkov 2015-07-22 17:06 ` Jason Baron 2015-07-23 10:42 ` Peter Zijlstra 2015-07-23 10:53 ` Borislav Petkov 2015-07-23 14:19 ` Jason Baron 2015-07-23 14:33 ` Peter Zijlstra 2015-07-23 14:49 ` Peter Zijlstra 2015-07-23 19:14 ` Jason Baron 2015-07-24 10:56 ` Peter Zijlstra 2015-07-24 12:36 ` Peter Zijlstra 2015-07-24 14:15 ` Jason Baron 2015-07-23 14:58 ` Peter Zijlstra 2015-07-23 15:34 ` Steven Rostedt 2015-07-23 17:08 ` Peter Zijlstra 2015-07-23 17:18 ` Steven Rostedt 2015-07-23 17:33 ` Jason Baron 2015-07-23 18:12 ` Steven Rostedt 2015-07-23 19:02 ` Peter Zijlstra 2015-07-23 17:35 ` Andy Lutomirski 2015-07-23 17:54 ` Borislav Petkov 2015-07-23 19:02 ` Peter Zijlstra 2015-07-24 5:29 ` Borislav Petkov 2015-07-24 10:36 ` Peter Zijlstra 2015-07-22 20:43 ` Mikulas Patocka 2015-07-21 15:53 ` Thomas Gleixner 2015-07-21 15:54 ` Peter Zijlstra 2015-07-09 17:11 ` Peter Zijlstra 2015-07-09 19:15 ` Jason Baron 2015-07-14 9:35 ` Borislav Petkov 2015-07-14 12:43 ` Mikulas Patocka
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox; as well as URLs for NNTP newsgroup(s).