diff mbox series

[v6,13/20] x86/split_lock: Enable split lock detection by default

Message ID 1554326526-172295-14-git-send-email-fenghua.yu@intel.com (mailing list archive)
State Not Applicable
Delegated to: Johannes Berg
Headers show
Series x86/split_lock: Enable split locked accesses detection | expand

Commit Message

Fenghua Yu April 3, 2019, 9:21 p.m. UTC
A split locked access locks bus and degrades overall memory access
performance. When split lock detection feature is enumerated, enable
the feature by default to find any split lock issue and then fix
the issue.

Signed-off-by: Fenghua Yu <fenghua.yu@intel.com>
---
 arch/x86/kernel/cpu/intel.c | 42 +++++++++++++++++++++++++++++++++++++
 1 file changed, 42 insertions(+)

Comments

Thomas Gleixner April 4, 2019, 6:07 p.m. UTC | #1
On Wed, 3 Apr 2019, Fenghua Yu wrote:

> A split locked access locks bus and degrades overall memory access
> performance. When split lock detection feature is enumerated, enable
> the feature by default to find any split lock issue and then fix
> the issue.

Enabling the feature allows to find the issues, but does not automagically
fix them. Come on.

> +#define DISABLE_SPLIT_LOCK_DETECT 0
> +#define ENABLE_SPLIT_LOCK_DETECT  1

If those defines have a value at all, please start with the facility not
with functionality, i.e. AC_SPLIT_LOCK_ENABLE....

> +
> +static DEFINE_MUTEX(split_lock_detect_mutex);
> +static int split_lock_detect_val;

detect_val? What value is that? Its supposed to hold those magic defines
above. So something like

static unsigned int ac_split_lock_enable;

>  /*
>   * Just in case our CPU detection goes bad, or you have a weird system,
>   * allow a way to override the automatic disabling of MPX.
> @@ -161,10 +167,45 @@ static bool bad_spectre_microcode(struct cpuinfo_x86 *c)
>  	return false;
>  }
>  
> +static u32 new_sp_test_ctl_val(u32 test_ctl_val)
> +{
> +	/* Change the split lock setting. */
> +	if (READ_ONCE(split_lock_detect_val) == DISABLE_SPLIT_LOCK_DETECT)

That READ_ONCE() is required because?

> +		test_ctl_val &= ~TEST_CTL_ENABLE_SPLIT_LOCK_DETECT;
> +	else
> +		test_ctl_val |= TEST_CTL_ENABLE_SPLIT_LOCK_DETECT;
> +
> +	return test_ctl_val;
> +}

Aside of that do we really need a misnomed function which replaces the
simple inline code at the call site:

	rdmsr(l, h)
	l &= ~TEST_CTL_ENABLE_SPLIT_LOCK_DETECT;
	l |= ac_split_lock_enable << TEST_CTL_ENABLE_SPLIT_LOCK_DETECT_SHIFT;
	wrmrs(...)

or the even more simple

	if (ac_split_lock_enable)
		msr_set_bit(...)
	else
		msr_clear_nit(...)

Hmm?

> +
> +static inline void show_split_lock_detection_info(void)
> +{
> +	if (READ_ONCE(split_lock_detect_val))

That READ_ONCE() is required because?

> +		pr_info_once("x86/split_lock: split lock detection enabled\n");
> +	else
> +		pr_info_once("x86/split_lock: split lock detection disabled\n");

pr_fmt exists for a reason and having 'split lock' repeated several times
in the same line is not making it more readable.

> +}
> +
> +static void init_split_lock_detect(struct cpuinfo_x86 *c)
> +{
> +	if (cpu_has(c, X86_FEATURE_SPLIT_LOCK_DETECT)) {
> +		u32 l, h;
> +
> +		mutex_lock(&split_lock_detect_mutex);
> +		rdmsr(MSR_TEST_CTL, l, h);
> +		l = new_sp_test_ctl_val(l);
> +		wrmsr(MSR_TEST_CTL, l, h);
> +		show_split_lock_detection_info();
> +		mutex_unlock(&split_lock_detect_mutex);
> +	}
> +}
> +
>  static void early_init_intel(struct cpuinfo_x86 *c)
>  {
>  	u64 misc_enable;
>  
> +	init_split_lock_detect(c);

so we have in early boot:

	early_cpu_init()
	  early_identify_cpu()
	    this_cpu->c_early_init(c)
	      early_init_intel() {
	        init_split_lock_detect();
	      }	
            ....
            cpu_set_core_cap_bits(c)
	       set(FEATURE_SPLIT_LOCK)

I don't have to understand how init_split_lock_detect() will magically see
the feature bit which gets set afterwards, right? 


> +
>  	/* Unmask CPUID levels if masked: */
>  	if (c->x86 > 6 || (c->x86 == 6 && c->x86_model >= 0xd)) {
>  		if (msr_clear_bit(MSR_IA32_MISC_ENABLE,
> @@ -1032,6 +1073,7 @@ cpu_dev_register(intel_cpu_dev);
>  static void __init set_split_lock_detect(void)
>  {
>  	setup_force_cpu_cap(X86_FEATURE_SPLIT_LOCK_DETECT);
> +	split_lock_detect_val = 1;

Oh well. You add defines on top of the file and then you don't use them.

Thanks,

	tglx
Thomas Gleixner April 4, 2019, 6:14 p.m. UTC | #2
On Thu, 4 Apr 2019, Thomas Gleixner wrote:
> On Wed, 3 Apr 2019, Fenghua Yu wrote:
> > +static void init_split_lock_detect(struct cpuinfo_x86 *c)
> > +{
> > +	if (cpu_has(c, X86_FEATURE_SPLIT_LOCK_DETECT)) {
> > +		u32 l, h;
> > +
> > +		mutex_lock(&split_lock_detect_mutex);

The mutex protects what exactly in the cpu bringup code?

Thanks,

	tglx
Fenghua Yu April 4, 2019, 7:23 p.m. UTC | #3
On Thu, Apr 04, 2019 at 08:07:57PM +0200, Thomas Gleixner wrote:
> On Wed, 3 Apr 2019, Fenghua Yu wrote:
> 
> > A split locked access locks bus and degrades overall memory access
> > performance. When split lock detection feature is enumerated, enable
> > the feature by default to find any split lock issue and then fix
> > the issue.
> 
> Enabling the feature allows to find the issues, but does not automagically
> fix them. Come on.

Ok. I will remove the "and then fix the issue".

> 
> > +#define DISABLE_SPLIT_LOCK_DETECT 0
> > +#define ENABLE_SPLIT_LOCK_DETECT  1
> 
> If those defines have a value at all, please start with the facility not
> with functionality, i.e. AC_SPLIT_LOCK_ENABLE....

OK.

> 
> > +
> > +static DEFINE_MUTEX(split_lock_detect_mutex);
> > +static int split_lock_detect_val;
> 
> detect_val? What value is that?

According to previous discussions, I was told to call this split lock feature
as "split lock detection" instead of "#AC for split lock". So I use
"split_lock_detect..." in variable names or function names, call feature flag
as "split_lock_detect", and call the feature as "split lock detection" in
descriptions.

If you don't agree to name feature as "split lock detection", I can change
variable names/function names/feature flag/descriptions etc back to previous
names "ac_split_lock...", "#AC for split lock", etc.

The variable split_lock_detect_val is either 0 or 1. It stores current
enable/disable status of split lock detection feature. By default it's
one after the feature is enumerated. Then sysadmin can change it to 0 or 1
to enable or disable the feature during run time.

> Its supposed to hold those magic defines
> above. So something like
> 
> static unsigned int ac_split_lock_enable;

If you agree to name the split lock feature as "split lock detection" feature,
can I change this variable to static unsigned int split_lock_detect_enable?

> >  /*
> >   * Just in case our CPU detection goes bad, or you have a weird system,
> >   * allow a way to override the automatic disabling of MPX.
> > @@ -161,10 +167,45 @@ static bool bad_spectre_microcode(struct cpuinfo_x86 *c)
> >  	return false;
> >  }
> >  
> > +static u32 new_sp_test_ctl_val(u32 test_ctl_val)
> > +{
> > +	/* Change the split lock setting. */
> > +	if (READ_ONCE(split_lock_detect_val) == DISABLE_SPLIT_LOCK_DETECT)
> 
> That READ_ONCE() is required because?

Ok. Will remove READ_ONCE().

> 
> > +		test_ctl_val &= ~TEST_CTL_ENABLE_SPLIT_LOCK_DETECT;
> > +	else
> > +		test_ctl_val |= TEST_CTL_ENABLE_SPLIT_LOCK_DETECT;
> > +
> > +	return test_ctl_val;
> > +}
> 
> Aside of that do we really need a misnomed function which replaces the
> simple inline code at the call site:
> 
> 	rdmsr(l, h)
> 	l &= ~TEST_CTL_ENABLE_SPLIT_LOCK_DETECT;
> 	l |= ac_split_lock_enable << TEST_CTL_ENABLE_SPLIT_LOCK_DETECT_SHIFT;
> 	wrmrs(...)
> 
> or the even more simple
> 
> 	if (ac_split_lock_enable)
> 		msr_set_bit(...)
> 	else
> 		msr_clear_nit(...)
> 
> Hmm?

The function new_sp_test_ctrl_val() will be called twice: here when
initializing split lock detection and in split_lock_detect_store()
when enabling/disabling the feature through the sysfs interface in
patch 0014.

So can I still keep this function and name it as get_new_test_ctrl_val()?

> 
> > +
> > +static inline void show_split_lock_detection_info(void)
> > +{
> > +	if (READ_ONCE(split_lock_detect_val))
> 
> That READ_ONCE() is required because?

Ok. Will remove READ_ONCE().

> 
> > +		pr_info_once("x86/split_lock: split lock detection enabled\n");
> > +	else
> > +		pr_info_once("x86/split_lock: split lock detection disabled\n");
> 
> pr_fmt exists for a reason and having 'split lock' repeated several times
> in the same line is not making it more readable.

Ok. I will change the string to "x86/split_lock_detection: enabled\n",
is it ok?

> 
> 
> > +
> >  	/* Unmask CPUID levels if masked: */
> >  	if (c->x86 > 6 || (c->x86 == 6 && c->x86_model >= 0xd)) {
> >  		if (msr_clear_bit(MSR_IA32_MISC_ENABLE,
> > @@ -1032,6 +1073,7 @@ cpu_dev_register(intel_cpu_dev);
> >  static void __init set_split_lock_detect(void)
> >  {
> >  	setup_force_cpu_cap(X86_FEATURE_SPLIT_LOCK_DETECT);
> > +	split_lock_detect_val = 1;
> 
> Oh well. You add defines on top of the file and then you don't use them.

Will fix this.

Thanks.

-Fenghua
Thomas Gleixner April 4, 2019, 7:44 p.m. UTC | #4
On Thu, 4 Apr 2019, Fenghua Yu wrote:
> On Thu, Apr 04, 2019 at 08:07:57PM +0200, Thomas Gleixner wrote:
> > On Wed, 3 Apr 2019, Fenghua Yu wrote:
> > > +static DEFINE_MUTEX(split_lock_detect_mutex);
> > > +static int split_lock_detect_val;
> > 
> > detect_val? What value is that?
> 
> According to previous discussions, I was told to call this split lock feature
> as "split lock detection" instead of "#AC for split lock". So I use
> "split_lock_detect..." in variable names or function names, call feature flag
> as "split_lock_detect", and call the feature as "split lock detection" in
> descriptions.
> 
> If you don't agree to name feature as "split lock detection", I can change
> variable names/function names/feature flag/descriptions etc back to previous
> names "ac_split_lock...", "#AC for split lock", etc.
> 
> The variable split_lock_detect_val is either 0 or 1. It stores current
> enable/disable status of split lock detection feature. By default it's
> one after the feature is enumerated. Then sysadmin can change it to 0 or 1
> to enable or disable the feature during run time.

> > static unsigned int ac_split_lock_enable;
> 
> If you agree to name the split lock feature as "split lock detection" feature,
> can I change this variable to static unsigned int split_lock_detect_enable?

I don't care much whether it's ac_split_lock or split_lock_detect, but _val
is a completely bogus and unintuitive name. The variable tells whether the
functionality is enabled or not. Then do not name it $prefix_val, which can
mean anything. Name it $prefix_enable, which makes it entirely clear what
this is about.

And please make it type bool so you don't need any of these defines either.

> > > +static u32 new_sp_test_ctl_val(u32 test_ctl_val)
> > > +{
> > > +	/* Change the split lock setting. */
> > > +	if (READ_ONCE(split_lock_detect_val) == DISABLE_SPLIT_LOCK_DETECT)
> > 
> > That READ_ONCE() is required because?
> 
> Ok. Will remove READ_ONCE().
> 
> > 
> > > +		test_ctl_val &= ~TEST_CTL_ENABLE_SPLIT_LOCK_DETECT;
> > > +	else
> > > +		test_ctl_val |= TEST_CTL_ENABLE_SPLIT_LOCK_DETECT;
> > > +
> > > +	return test_ctl_val;
> > > +}
> > 
> > Aside of that do we really need a misnomed function which replaces the
> > simple inline code at the call site:
> > 
> > 	rdmsr(l, h)
> > 	l &= ~TEST_CTL_ENABLE_SPLIT_LOCK_DETECT;
> > 	l |= ac_split_lock_enable << TEST_CTL_ENABLE_SPLIT_LOCK_DETECT_SHIFT;
> > 	wrmrs(...)
> > 
> > or the even more simple
> > 
> > 	if (ac_split_lock_enable)
> > 		msr_set_bit(...)
> > 	else
> > 		msr_clear_nit(...)
> > 
> > Hmm?
> 
> The function new_sp_test_ctrl_val() will be called twice: here when
> initializing split lock detection and in split_lock_detect_store()
> when enabling/disabling the feature through the sysfs interface in
> patch 0014.

It's still pointless.

> So can I still keep this function and name it as get_new_test_ctrl_val()?

No. The function you want to share between init code and sysfs is

    split_lock_update_msr()
    {
 	if (split_lock_enable)
 		msr_set_bit(...)
 	else
 		msr_clear_nit(...)
    }

That's all. No duplicated code. No convoluted helper function,
nothing. Simple straight forward readable code.

> > > +static inline void show_split_lock_detection_info(void)
> > > +{
> > > +	if (READ_ONCE(split_lock_detect_val))
> > 
> > That READ_ONCE() is required because?
> 
> Ok. Will remove READ_ONCE().
> 
> > 
> > > +		pr_info_once("x86/split_lock: split lock detection enabled\n");
> > > +	else
> > > +		pr_info_once("x86/split_lock: split lock detection disabled\n");
> > 
> > pr_fmt exists for a reason and having 'split lock' repeated several times
> > in the same line is not making it more readable.
> 
> Ok. I will change the string to "x86/split_lock_detection: enabled\n",
> is it ok?

Care to read carefully what I wrote? Hint: pr_fmt
 
> > Oh well. You add defines on top of the file and then you don't use them.
> 
> Will fix this.

What about the init / feature detection sequence which you snipped from the
reply?

Thanks,

	tglx
Fenghua Yu April 10, 2019, 12:02 a.m. UTC | #5
On Thu, Apr 04, 2019 at 08:07:57PM +0200, Thomas Gleixner wrote:
> On Wed, 3 Apr 2019, Fenghua Yu wrote:
> 
> > +static void init_split_lock_detect(struct cpuinfo_x86 *c)
> > +{
> > +	if (cpu_has(c, X86_FEATURE_SPLIT_LOCK_DETECT)) {
> > +		u32 l, h;
> > +
> > +		mutex_lock(&split_lock_detect_mutex);
> > +		rdmsr(MSR_TEST_CTL, l, h);
> > +		l = new_sp_test_ctl_val(l);
> > +		wrmsr(MSR_TEST_CTL, l, h);
> > +		show_split_lock_detection_info();
> > +		mutex_unlock(&split_lock_detect_mutex);
> > +	}
> > +}
> > +
> >  static void early_init_intel(struct cpuinfo_x86 *c)
> >  {
> >  	u64 misc_enable;
> >  
> > +	init_split_lock_detect(c);
> 
> so we have in early boot:
> 
> 	early_cpu_init()
> 	  early_identify_cpu()
> 	    this_cpu->c_early_init(c)
> 	      early_init_intel() {
> 	        init_split_lock_detect();
> 	      }	
>             ....
>             cpu_set_core_cap_bits(c)
> 	       set(FEATURE_SPLIT_LOCK)
> 
> I don't have to understand how init_split_lock_detect() will magically see
> the feature bit which gets set afterwards, right? 

early_init_intel() is called twice on the boot CPU. Besides it's called
in earl_cpu_init(), it's also called in:
	identify_boot_cpu()
		identify_cpu()
			init_intel()
				early_init_intel()
					init_split_lock_detect();

It's true that init_split_lock_detect() doesn't see the feature bit when
it's called for the first time in early_cpu_init(). But it sees the feature
bit when it's called for the second time in identify_boot_cpu().

So is init_split_lock_detect() in the right place?

Thanks.

-Fenghua
Thomas Gleixner April 10, 2019, 6:31 a.m. UTC | #6
On Tue, 9 Apr 2019, Fenghua Yu wrote:
> On Thu, Apr 04, 2019 at 08:07:57PM +0200, Thomas Gleixner wrote:
> > On Wed, 3 Apr 2019, Fenghua Yu wrote:
> > >  static void early_init_intel(struct cpuinfo_x86 *c)
> > >  {
> > >  	u64 misc_enable;
> > >  
> > > +	init_split_lock_detect(c);
> > 
> > so we have in early boot:
> > 
> > 	early_cpu_init()
> > 	  early_identify_cpu()
> > 	    this_cpu->c_early_init(c)
> > 	      early_init_intel() {
> > 	        init_split_lock_detect();
> > 	      }	
> >             ....
> >             cpu_set_core_cap_bits(c)
> > 	       set(FEATURE_SPLIT_LOCK)
> > 
> > I don't have to understand how init_split_lock_detect() will magically see
> > the feature bit which gets set afterwards, right? 
> 
> early_init_intel() is called twice on the boot CPU. Besides it's called
> in earl_cpu_init(), it's also called in:
> 	identify_boot_cpu()
> 		identify_cpu()
> 			init_intel()
> 				early_init_intel()
> 					init_split_lock_detect();
> 
> It's true that init_split_lock_detect() doesn't see the feature bit when
> it's called for the first time in early_cpu_init(). But it sees the feature
> bit when it's called for the second time in identify_boot_cpu().

That's hideous, really. 

> So is init_split_lock_detect() in the right place?

You're not seriously asking that?

It's obviously not the right place. We are not placing calls at random
points just because they happen to work by chance.

Thanks,

	tglx
David Laight April 10, 2019, 8:50 a.m. UTC | #7
FWIW it took me a while to work out what a 'split lock' was.

I suspect because I was thinking of kernel locks, not the
instruction lock prefix.
It also isn't really obvious that 'split' refers to crossing
cache lines.

Referring to it as a 'misaligned lock' might be more
easily understood.

	David

-
Registered Address Lakeside, Bramley Road, Mount Farm, Milton Keynes, MK1 1PT, UK
Registration No: 1397386 (Wales)
Fenghua Yu April 10, 2019, 12:35 p.m. UTC | #8
On Wed, Apr 10, 2019 at 08:31:31AM +0200, Thomas Gleixner wrote:
> On Tue, 9 Apr 2019, Fenghua Yu wrote:
> > On Thu, Apr 04, 2019 at 08:07:57PM +0200, Thomas Gleixner wrote:
> > > On Wed, 3 Apr 2019, Fenghua Yu wrote:
> > > >  static void early_init_intel(struct cpuinfo_x86 *c)
> > > >  {
> > > >  	u64 misc_enable;
> > > >  
> > > > +	init_split_lock_detect(c);
> > > 
> > > so we have in early boot:
> > > 
> > > 	early_cpu_init()
> > > 	  early_identify_cpu()
> > > 	    this_cpu->c_early_init(c)
> > > 	      early_init_intel() {
> > > 	        init_split_lock_detect();
> > > 	      }	
> > >             ....
> > >             cpu_set_core_cap_bits(c)
> > > 	       set(FEATURE_SPLIT_LOCK)
> > > 
> > > I don't have to understand how init_split_lock_detect() will magically see
> > > the feature bit which gets set afterwards, right? 
> > 
> > early_init_intel() is called twice on the boot CPU. Besides it's called
> > in earl_cpu_init(), it's also called in:
> > 	identify_boot_cpu()
> > 		identify_cpu()
> > 			init_intel()
> > 				early_init_intel()
> > 					init_split_lock_detect();
> > 
> > It's true that init_split_lock_detect() doesn't see the feature bit when
> > it's called for the first time in early_cpu_init(). But it sees the feature
> > bit when it's called for the second time in identify_boot_cpu().
> 
> That's hideous, really. 
> 
> > So is init_split_lock_detect() in the right place?
> 
> You're not seriously asking that?
> 
> It's obviously not the right place. We are not placing calls at random
> points just because they happen to work by chance.

Is it OK to put init_split_lock_detect(c) after early_init_intel() in
init_intel()? X86_FEATURE_SPLIT_LOCK_DETECT is available now and
init_split_lock_detec() is called only once on each CPU.

@@ -746,6 +749,8 @@ static void init_intel(struct cpuinfo_x86 *c)
 {
        early_init_intel(c);
 
+       init_split_lock_detect(c);
+
        intel_workarounds(c);

Thanks.

-Fenghua
diff mbox series

Patch

diff --git a/arch/x86/kernel/cpu/intel.c b/arch/x86/kernel/cpu/intel.c
index 7f6943af35dc..ae3e327d5e35 100644
--- a/arch/x86/kernel/cpu/intel.c
+++ b/arch/x86/kernel/cpu/intel.c
@@ -31,6 +31,12 @@ 
 #include <asm/apic.h>
 #endif
 
+#define DISABLE_SPLIT_LOCK_DETECT 0
+#define ENABLE_SPLIT_LOCK_DETECT  1
+
+static DEFINE_MUTEX(split_lock_detect_mutex);
+static int split_lock_detect_val;
+
 /*
  * Just in case our CPU detection goes bad, or you have a weird system,
  * allow a way to override the automatic disabling of MPX.
@@ -161,10 +167,45 @@  static bool bad_spectre_microcode(struct cpuinfo_x86 *c)
 	return false;
 }
 
+static u32 new_sp_test_ctl_val(u32 test_ctl_val)
+{
+	/* Change the split lock setting. */
+	if (READ_ONCE(split_lock_detect_val) == DISABLE_SPLIT_LOCK_DETECT)
+		test_ctl_val &= ~TEST_CTL_ENABLE_SPLIT_LOCK_DETECT;
+	else
+		test_ctl_val |= TEST_CTL_ENABLE_SPLIT_LOCK_DETECT;
+
+	return test_ctl_val;
+}
+
+static inline void show_split_lock_detection_info(void)
+{
+	if (READ_ONCE(split_lock_detect_val))
+		pr_info_once("x86/split_lock: split lock detection enabled\n");
+	else
+		pr_info_once("x86/split_lock: split lock detection disabled\n");
+}
+
+static void init_split_lock_detect(struct cpuinfo_x86 *c)
+{
+	if (cpu_has(c, X86_FEATURE_SPLIT_LOCK_DETECT)) {
+		u32 l, h;
+
+		mutex_lock(&split_lock_detect_mutex);
+		rdmsr(MSR_TEST_CTL, l, h);
+		l = new_sp_test_ctl_val(l);
+		wrmsr(MSR_TEST_CTL, l, h);
+		show_split_lock_detection_info();
+		mutex_unlock(&split_lock_detect_mutex);
+	}
+}
+
 static void early_init_intel(struct cpuinfo_x86 *c)
 {
 	u64 misc_enable;
 
+	init_split_lock_detect(c);
+
 	/* Unmask CPUID levels if masked: */
 	if (c->x86 > 6 || (c->x86 == 6 && c->x86_model >= 0xd)) {
 		if (msr_clear_bit(MSR_IA32_MISC_ENABLE,
@@ -1032,6 +1073,7 @@  cpu_dev_register(intel_cpu_dev);
 static void __init set_split_lock_detect(void)
 {
 	setup_force_cpu_cap(X86_FEATURE_SPLIT_LOCK_DETECT);
+	split_lock_detect_val = 1;
 }
 
 void __init cpu_set_core_cap_bits(struct cpuinfo_x86 *c)