2015-07-16 22:09:26

by Laura Abbott

[permalink] [raw]
Subject: [RFC][PATCH] x86, CPU: Restore MSR_IA32_ENERGY_PERF_BIAS after resume

MSR_IA32_ENERGY_PERF_BIAS is lost after suspend/resume:

x86_energy_perf_policy -r before

cpu0: 0x0000000000000006
cpu1: 0x0000000000000006
cpu2: 0x0000000000000006
cpu3: 0x0000000000000006
cpu4: 0x0000000000000006
cpu5: 0x0000000000000006
cpu6: 0x0000000000000006
cpu7: 0x0000000000000006

after

cpu0: 0x0000000000000000
cpu1: 0x0000000000000006
cpu2: 0x0000000000000006
cpu3: 0x0000000000000006
cpu4: 0x0000000000000006
cpu5: 0x0000000000000006
cpu6: 0x0000000000000006
cpu7: 0x0000000000000006

This register is set via init_intel at bootup. During resume, the
secondary CPUs are brought online again and init_intel is callled which
re-initializes the register. The boot cpu however never reinitializes
the register. Add a syscore callback to reinitialize the register for
the boot CPU.

Signed-off-by: Laura Abbott <[email protected]>
---
RFC because I'm not that familiar with x86 suspend/resume inner workings.
---
arch/x86/kernel/cpu/common.c | 18 ++++++++++++++++++
arch/x86/kernel/cpu/cpu.h | 1 +
arch/x86/kernel/cpu/intel.c | 36 +++++++++++++++++++++---------------
3 files changed, 40 insertions(+), 15 deletions(-)

diff --git a/arch/x86/kernel/cpu/common.c b/arch/x86/kernel/cpu/common.c
index 922c5e0..dc9f1e5 100644
--- a/arch/x86/kernel/cpu/common.c
+++ b/arch/x86/kernel/cpu/common.c
@@ -13,6 +13,7 @@
#include <linux/kgdb.h>
#include <linux/smp.h>
#include <linux/io.h>
+#include <linux/syscore_ops.h>

#include <asm/stackprotector.h>
#include <asm/perf_event.h>
@@ -1488,3 +1489,20 @@ inline bool __static_cpu_has_safe(u16 bit)
return boot_cpu_has(bit);
}
EXPORT_SYMBOL_GPL(__static_cpu_has_safe);
+
+static void cpu_custom_resume(void)
+{
+ if (this_cpu->c_resume)
+ this_cpu->c_resume(&boot_cpu_data);
+}
+
+static struct syscore_ops cpu_syscore_ops = {
+ .resume = cpu_custom_resume,
+};
+
+static int __init init_cpu_syscore(void)
+{
+ register_syscore_ops(&cpu_syscore_ops);
+ return 0;
+}
+core_initcall(init_cpu_syscore);
diff --git a/arch/x86/kernel/cpu/cpu.h b/arch/x86/kernel/cpu/cpu.h
index c37dc37..5143d0f 100644
--- a/arch/x86/kernel/cpu/cpu.h
+++ b/arch/x86/kernel/cpu/cpu.h
@@ -13,6 +13,7 @@ struct cpu_dev {
void (*c_init)(struct cpuinfo_x86 *);
void (*c_identify)(struct cpuinfo_x86 *);
void (*c_detect_tlb)(struct cpuinfo_x86 *);
+ void (*c_resume)(struct cpuinfo_x86 *);
int c_x86_vendor;
#ifdef CONFIG_X86_32
/* Optional vendor specific routine to obtain the cache size. */
diff --git a/arch/x86/kernel/cpu/intel.c b/arch/x86/kernel/cpu/intel.c
index 50163fa..9a22474 100644
--- a/arch/x86/kernel/cpu/intel.c
+++ b/arch/x86/kernel/cpu/intel.c
@@ -371,6 +371,25 @@ static void detect_vmx_virtcap(struct cpuinfo_x86 *c)
}
}

+static void init_intel_energy_perf(struct cpuinfo_x86 *c)
+{
+ /*
+ * Initialize MSR_IA32_ENERGY_PERF_BIAS if BIOS did not.
+ * x86_energy_perf_policy(8) is available to change it at run-time
+ */
+ if (cpu_has(c, X86_FEATURE_EPB)) {
+ u64 epb;
+
+ rdmsrl(MSR_IA32_ENERGY_PERF_BIAS, epb);
+ if ((epb & 0xF) == ENERGY_PERF_BIAS_PERFORMANCE) {
+ pr_warn_once("ENERGY_PERF_BIAS: Set to 'normal', was 'performance'\n");
+ pr_warn_once("ENERGY_PERF_BIAS: View and update with x86_energy_perf_policy(8)\n");
+ epb = (epb & ~0xF) | ENERGY_PERF_BIAS_NORMAL;
+ wrmsrl(MSR_IA32_ENERGY_PERF_BIAS, epb);
+ }
+ }
+}
+
static void init_intel(struct cpuinfo_x86 *c)
{
unsigned int l2 = 0;
@@ -478,21 +497,7 @@ static void init_intel(struct cpuinfo_x86 *c)
if (cpu_has(c, X86_FEATURE_VMX))
detect_vmx_virtcap(c);

- /*
- * Initialize MSR_IA32_ENERGY_PERF_BIAS if BIOS did not.
- * x86_energy_perf_policy(8) is available to change it at run-time
- */
- if (cpu_has(c, X86_FEATURE_EPB)) {
- u64 epb;
-
- rdmsrl(MSR_IA32_ENERGY_PERF_BIAS, epb);
- if ((epb & 0xF) == ENERGY_PERF_BIAS_PERFORMANCE) {
- pr_warn_once("ENERGY_PERF_BIAS: Set to 'normal', was 'performance'\n");
- pr_warn_once("ENERGY_PERF_BIAS: View and update with x86_energy_perf_policy(8)\n");
- epb = (epb & ~0xF) | ENERGY_PERF_BIAS_NORMAL;
- wrmsrl(MSR_IA32_ENERGY_PERF_BIAS, epb);
- }
- }
+ init_intel_energy_perf(c);
}

#ifdef CONFIG_X86_32
@@ -747,6 +752,7 @@ static const struct cpu_dev intel_cpu_dev = {
.c_detect_tlb = intel_detect_tlb,
.c_early_init = early_init_intel,
.c_init = init_intel,
+ .c_resume = init_intel_energy_perf,
.c_x86_vendor = X86_VENDOR_INTEL,
};

--
2.4.3


2015-07-17 08:40:08

by Borislav Petkov

[permalink] [raw]
Subject: Re: [RFC][PATCH] x86, CPU: Restore MSR_IA32_ENERGY_PERF_BIAS after resume

On Thu, Jul 16, 2015 at 03:09:21PM -0700, Laura Abbott wrote:
> MSR_IA32_ENERGY_PERF_BIAS is lost after suspend/resume:
>
> x86_energy_perf_policy -r before
>
> cpu0: 0x0000000000000006
> cpu1: 0x0000000000000006
> cpu2: 0x0000000000000006
> cpu3: 0x0000000000000006
> cpu4: 0x0000000000000006
> cpu5: 0x0000000000000006
> cpu6: 0x0000000000000006
> cpu7: 0x0000000000000006
>
> after
>
> cpu0: 0x0000000000000000

Yap, I see it here too on an IVB laptop when doing s2r.

> cpu1: 0x0000000000000006
> cpu2: 0x0000000000000006
> cpu3: 0x0000000000000006
> cpu4: 0x0000000000000006
> cpu5: 0x0000000000000006
> cpu6: 0x0000000000000006
> cpu7: 0x0000000000000006
>
> This register is set via init_intel at bootup. During resume, the
> secondary CPUs are brought online again and init_intel is callled which
> re-initializes the register. The boot cpu however never reinitializes
> the register. Add a syscore callback to reinitialize the register for
> the boot CPU.
>
> Signed-off-by: Laura Abbott <[email protected]>
> ---
> RFC because I'm not that familiar with x86 suspend/resume inner workings.
> ---
> arch/x86/kernel/cpu/common.c | 18 ++++++++++++++++++
> arch/x86/kernel/cpu/cpu.h | 1 +
> arch/x86/kernel/cpu/intel.c | 36 +++++++++++++++++++++---------------
> 3 files changed, 40 insertions(+), 15 deletions(-)
>
> diff --git a/arch/x86/kernel/cpu/common.c b/arch/x86/kernel/cpu/common.c
> index 922c5e0..dc9f1e5 100644
> --- a/arch/x86/kernel/cpu/common.c
> +++ b/arch/x86/kernel/cpu/common.c
> @@ -13,6 +13,7 @@
> #include <linux/kgdb.h>
> #include <linux/smp.h>
> #include <linux/io.h>
> +#include <linux/syscore_ops.h>
>
> #include <asm/stackprotector.h>
> #include <asm/perf_event.h>
> @@ -1488,3 +1489,20 @@ inline bool __static_cpu_has_safe(u16 bit)
> return boot_cpu_has(bit);
> }
> EXPORT_SYMBOL_GPL(__static_cpu_has_safe);
> +
> +static void cpu_custom_resume(void)

Let's call this bsp_resume() as it is run only on the boot CPU.

> +{
> + if (this_cpu->c_resume)
> + this_cpu->c_resume(&boot_cpu_data);
> +}

So giving boot_cpu_data means, this is the boot CPU but you're assigning
it to c_resume() which means, every CPU. What you could do is call the
function ptr ->c_bsp_resume like the ->c_bsp_init which we already have
so that it is clear that it is run only on the BSP.

Rest looks ok to me, unless tip guys have a better idea...

--
Regards/Gruss,
Boris.

ECO tip #101: Trim your mails when you reply.
--

2015-07-17 12:14:52

by Ingo Molnar

[permalink] [raw]
Subject: Re: [RFC][PATCH] x86, CPU: Restore MSR_IA32_ENERGY_PERF_BIAS after resume


* Borislav Petkov <[email protected]> wrote:

> > EXPORT_SYMBOL_GPL(__static_cpu_has_safe);
> > +
> > +static void cpu_custom_resume(void)
>
> Let's call this bsp_resume() as it is run only on the boot CPU.
>
> > +{
> > + if (this_cpu->c_resume)
> > + this_cpu->c_resume(&boot_cpu_data);
> > +}
>
> So giving boot_cpu_data means, this is the boot CPU but you're assigning it to
> c_resume() which means, every CPU. What you could do is call the function ptr
> ->c_bsp_resume like the ->c_bsp_init which we already have so that it is clear
> that it is run only on the BSP.
>
> Rest looks ok to me, unless tip guys have a better idea...

Looks good to me!

Ingo