Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]
Groups > linux.kernel > #1417989 > unrolled thread
| Started by | dbasehore@chromium.org |
|---|---|
| First post | 2016-06-09 02:50 +0200 |
| Last post | 2016-06-09 02:50 +0200 |
| Articles | 4 — 1 participant |
Back to article view | Back to linux.kernel
[PATCH v2 0/5] Add suspend-to-idle validation for Intel SoCs dbasehore@chromium.org - 2016-06-09 02:50 +0200
[PATCH v2 3/5] x86, apic: Add timed freeze support dbasehore@chromium.org - 2016-06-09 02:50 +0200
[PATCH v2 1/5] x86: stub out pmc function dbasehore@chromium.org - 2016-06-09 02:50 +0200
[PATCH v2 4/5] freeze: Add error reporting dbasehore@chromium.org - 2016-06-09 02:50 +0200
| From | dbasehore@chromium.org |
|---|---|
| Date | 2016-06-09 02:50 +0200 |
| Subject | [PATCH v2 0/5] Add suspend-to-idle validation for Intel SoCs |
| Message-ID | <rHWkx-7lM-7@gated-at.bofh.it> |
From: Derek Basehore <dbasehore@chromium.org> This patch set adds support for catching errors when entering freeze on Intel Skylake SoCs. Support for this can be added to newer SoCs in later patches. Verification is done by waking up the CPU up to 1000 seconds later based on base 10 exponential backoff from 1 second to check the residency of S0ix. This can't be verified before attempting to enter S0ix through mwait, so we have to repeatedly verify entry into that state. Successfully entering S0ix is no guarantee that it will be entered on the next attempt, so we have to schedule another check. This has a minimal average power impact of ~3uW on Skylake systems. This leaves plenty of room for additional overhead based on changes to the system. This relies on the recently added patch "platform/x86: Add PMC Driver for Intel Core SoC" Changes for v2: - Moved to exponential backoff for the freeze duration with a max of 1000 seconds - Changed to make the feature default off - Add module parameter for enabling/disabling the feature instead of a debugfs entry Derek Basehore (5): x86: stub out pmc function clockevents: Add timed freeze x86, apic: Add timed freeze support freeze: Add error reporting intel_idle: Add S0ix validation arch/x86/include/asm/pmc_core.h | 6 +- arch/x86/kernel/apic/apic.c | 25 ++++++- drivers/acpi/processor_idle.c | 10 ++- drivers/cpuidle/cpuidle.c | 31 ++++++-- drivers/idle/intel_idle.c | 161 +++++++++++++++++++++++++++++++++++++--- include/linux/clockchips.h | 10 +++ include/linux/cpuidle.h | 10 ++- include/linux/suspend.h | 10 +++ kernel/power/suspend.c | 11 ++- kernel/time/clockevents.c | 117 +++++++++++++++++++++++++++++ 10 files changed, 361 insertions(+), 30 deletions(-) -- 2.8.0.rc3.226.g39d4020
[toc] | [next] | [standalone]
| From | dbasehore@chromium.org |
|---|---|
| Date | 2016-06-09 02:50 +0200 |
| Subject | [PATCH v2 3/5] x86, apic: Add timed freeze support |
| Message-ID | <rHWkx-7lM-19@gated-at.bofh.it> |
| In reply to | #1417989 |
From: Derek Basehore <dbasehore@chromium.org>
This adds support to the clock event devices created by apic to use
timed freeze. The apic is able to run a timer during freeze with near
izero impact on modern CPUs such as skylake. This will allow S0ix,
suspend-to-idle, to be validated on Intel CPUs that support it.
This is needed because bugs with power settings on the SoC can prevent
S0ix entry. There is also no way to check this before idling all of
the CPUs.
Signed-off-by: Derek Basehore <dbasehore@chromium.org>
---
arch/x86/kernel/apic/apic.c | 25 ++++++++++++++++++++++++-
1 file changed, 24 insertions(+), 1 deletion(-)
diff --git a/arch/x86/kernel/apic/apic.c b/arch/x86/kernel/apic/apic.c
index 60078a6..f0c5f92 100644
--- a/arch/x86/kernel/apic/apic.c
+++ b/arch/x86/kernel/apic/apic.c
@@ -475,6 +475,26 @@ static int lapic_next_deadline(unsigned long delta,
return 0;
}
+static bool lapic_event_expired(struct clock_event_device *evt)
+{
+ u32 cct;
+
+ cct = apic_read(APIC_TMCCT);
+ return cct == 0;
+}
+
+static bool lapic_deadline_expired(struct clock_event_device *evt)
+{
+ u64 msr;
+
+ /*
+ * When the timer interrupt is triggered, the register is cleared, so a
+ * non-zero value indicates a pending timer event.
+ */
+ rdmsrl(MSR_IA32_TSC_DEADLINE, msr);
+ return msr == 0;
+}
+
static int lapic_timer_shutdown(struct clock_event_device *evt)
{
unsigned int v;
@@ -529,12 +549,14 @@ static struct clock_event_device lapic_clockevent = {
.name = "lapic",
.features = CLOCK_EVT_FEAT_PERIODIC |
CLOCK_EVT_FEAT_ONESHOT | CLOCK_EVT_FEAT_C3STOP
- | CLOCK_EVT_FEAT_DUMMY,
+ | CLOCK_EVT_FEAT_DUMMY |
+ CLOCK_EVT_FEAT_FREEZE,
.shift = 32,
.set_state_shutdown = lapic_timer_shutdown,
.set_state_periodic = lapic_timer_set_periodic,
.set_state_oneshot = lapic_timer_set_oneshot,
.set_next_event = lapic_next_event,
+ .event_expired = lapic_event_expired,
.broadcast = lapic_timer_broadcast,
.rating = 100,
.irq = -1,
@@ -562,6 +584,7 @@ static void setup_APIC_timer(void)
levt->features &= ~(CLOCK_EVT_FEAT_PERIODIC |
CLOCK_EVT_FEAT_DUMMY);
levt->set_next_event = lapic_next_deadline;
+ levt->event_expired = lapic_deadline_expired;
clockevents_config_and_register(levt,
(tsc_khz / TSC_DIVISOR) * 1000,
0xF, ~0UL);
--
2.8.0.rc3.226.g39d4020
[toc] | [prev] | [next] | [standalone]
| From | dbasehore@chromium.org |
|---|---|
| Date | 2016-06-09 02:50 +0200 |
| Subject | [PATCH v2 1/5] x86: stub out pmc function |
| Message-ID | <rHWkx-7lM-21@gated-at.bofh.it> |
| In reply to | #1417989 |
From: Derek Basehore <dbasehore@chromium.org>
This creates an inline function of intel_pmc_slp_s0_counter_read for
!CONFIG_INTEL_PMC_CORE.
Signed-off-by: Derek Basehore <dbasehore@chromium.org>
---
arch/x86/include/asm/pmc_core.h | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/arch/x86/include/asm/pmc_core.h b/arch/x86/include/asm/pmc_core.h
index d4855f1..786e526 100644
--- a/arch/x86/include/asm/pmc_core.h
+++ b/arch/x86/include/asm/pmc_core.h
@@ -22,6 +22,10 @@
#define _ASM_PMC_CORE_H
/* API to read SLP_S0_RESIDENCY counter */
-int intel_pmc_slp_s0_counter_read(u32 *data);
+#ifdef CONFIG_INTEL_PMC_CORE
+extern int intel_pmc_slp_s0_counter_read(u32 *data);
+#else
+static inline int intel_pmc_slp_s0_counter_read(u32 *data) { return -ENOSYS; }
+#endif
#endif /* _ASM_PMC_CORE_H */
--
2.8.0.rc3.226.g39d4020
[toc] | [prev] | [next] | [standalone]
| From | dbasehore@chromium.org |
|---|---|
| Date | 2016-06-09 02:50 +0200 |
| Subject | [PATCH v2 4/5] freeze: Add error reporting |
| Message-ID | <rHWky-7lM-29@gated-at.bofh.it> |
| In reply to | #1417989 |
From: Derek Basehore <dbasehore@chromium.org>
This adds error reporting for cpuidle to freeze so suspend-to-idle can
report errors when the CPU/SoC is unable to idle properly. Freeze will
abort when an error is encounted.
Signed-off-by: Derek Basehore <dbasehore@chromium.org>
---
drivers/acpi/processor_idle.c | 10 ++++++----
drivers/cpuidle/cpuidle.c | 31 ++++++++++++++++++++++++++-----
drivers/idle/intel_idle.c | 8 +++++---
include/linux/cpuidle.h | 10 ++++++----
kernel/power/suspend.c | 11 +++++++----
5 files changed, 50 insertions(+), 20 deletions(-)
diff --git a/drivers/acpi/processor_idle.c b/drivers/acpi/processor_idle.c
index 444e374..a959b32 100644
--- a/drivers/acpi/processor_idle.c
+++ b/drivers/acpi/processor_idle.c
@@ -783,8 +783,8 @@ static int acpi_idle_enter(struct cpuidle_device *dev,
return index;
}
-static void acpi_idle_enter_freeze(struct cpuidle_device *dev,
- struct cpuidle_driver *drv, int index)
+static int acpi_idle_enter_freeze(struct cpuidle_device *dev,
+ struct cpuidle_driver *drv, int index)
{
struct acpi_processor_cx *cx = per_cpu(acpi_cstate[index], dev->cpu);
@@ -792,16 +792,18 @@ static void acpi_idle_enter_freeze(struct cpuidle_device *dev,
struct acpi_processor *pr = __this_cpu_read(processors);
if (unlikely(!pr))
- return;
+ return 0;
if (pr->flags.bm_check) {
acpi_idle_enter_bm(pr, cx, false);
- return;
+ return 0;
} else {
ACPI_FLUSH_CPU_CACHE();
}
}
acpi_idle_do_entry(cx);
+
+ return 0;
}
struct cpuidle_driver acpi_idle_driver = {
diff --git a/drivers/cpuidle/cpuidle.c b/drivers/cpuidle/cpuidle.c
index a4d0059..2664a6c 100644
--- a/drivers/cpuidle/cpuidle.c
+++ b/drivers/cpuidle/cpuidle.c
@@ -34,6 +34,7 @@ LIST_HEAD(cpuidle_detected_devices);
static int enabled_devices;
static int off __read_mostly;
static int initialized __read_mostly;
+static int cpuidle_freeze_error;
int cpuidle_disabled(void)
{
@@ -109,9 +110,11 @@ int cpuidle_find_deepest_state(struct cpuidle_driver *drv,
return find_deepest_state(drv, dev, UINT_MAX, 0, false);
}
-static void enter_freeze_proper(struct cpuidle_driver *drv,
+static int enter_freeze_proper(struct cpuidle_driver *drv,
struct cpuidle_device *dev, int index)
{
+ int ret;
+
/*
* trace_suspend_resume() called by tick_freeze() for the last CPU
* executing it contains RCU usage regarded as invalid in the idle
@@ -124,7 +127,7 @@ static void enter_freeze_proper(struct cpuidle_driver *drv,
* suspended is generally unsafe.
*/
stop_critical_timings();
- drv->states[index].enter_freeze(dev, drv, index);
+ ret = drv->states[index].enter_freeze(dev, drv, index);
WARN_ON(!irqs_disabled());
/*
* timekeeping_resume() that will be called by tick_unfreeze() for the
@@ -133,6 +136,7 @@ static void enter_freeze_proper(struct cpuidle_driver *drv,
*/
RCU_NONIDLE(tick_unfreeze());
start_critical_timings();
+ return ret;
}
/**
@@ -145,7 +149,7 @@ static void enter_freeze_proper(struct cpuidle_driver *drv,
*/
int cpuidle_enter_freeze(struct cpuidle_driver *drv, struct cpuidle_device *dev)
{
- int index;
+ int index, ret = 0;
/*
* Find the deepest state with ->enter_freeze present, which guarantees
@@ -153,8 +157,13 @@ int cpuidle_enter_freeze(struct cpuidle_driver *drv, struct cpuidle_device *dev)
* be frozen safely.
*/
index = find_deepest_state(drv, dev, UINT_MAX, 0, true);
- if (index > 0)
- enter_freeze_proper(drv, dev, index);
+ if (index >= 0)
+ ret = enter_freeze_proper(drv, dev, index);
+
+ if (ret < 0) {
+ cpuidle_freeze_error = ret;
+ freeze_wake();
+ }
return index;
}
@@ -353,6 +362,18 @@ void cpuidle_resume(void)
mutex_unlock(&cpuidle_lock);
}
+void cpuidle_prepare_freeze(void)
+{
+ cpuidle_freeze_error = 0;
+ cpuidle_resume();
+}
+
+int cpuidle_complete_freeze(void)
+{
+ cpuidle_pause();
+ return cpuidle_freeze_error;
+}
+
/**
* cpuidle_enable_device - enables idle PM for a CPU
* @dev: the CPU
diff --git a/drivers/idle/intel_idle.c b/drivers/idle/intel_idle.c
index c966492..98565de 100644
--- a/drivers/idle/intel_idle.c
+++ b/drivers/idle/intel_idle.c
@@ -97,8 +97,8 @@ static const struct idle_cpu *icpu;
static struct cpuidle_device __percpu *intel_idle_cpuidle_devices;
static int intel_idle(struct cpuidle_device *dev,
struct cpuidle_driver *drv, int index);
-static void intel_idle_freeze(struct cpuidle_device *dev,
- struct cpuidle_driver *drv, int index);
+static int intel_idle_freeze(struct cpuidle_device *dev,
+ struct cpuidle_driver *drv, int index);
static int intel_idle_cpu_init(int cpu);
static struct cpuidle_state *cpuidle_state_table;
@@ -870,13 +870,15 @@ static int intel_idle(struct cpuidle_device *dev,
* @drv: cpuidle driver
* @index: state index
*/
-static void intel_idle_freeze(struct cpuidle_device *dev,
+static int intel_idle_freeze(struct cpuidle_device *dev,
struct cpuidle_driver *drv, int index)
{
unsigned long ecx = 1; /* break on interrupt flag */
unsigned long eax = flg2MWAIT(drv->states[index].flags);
mwait_idle_with_hints(eax, ecx);
+
+ return 0;
}
static void __setup_broadcast_timer(void *arg)
diff --git a/include/linux/cpuidle.h b/include/linux/cpuidle.h
index 786ad32..27f6b11 100644
--- a/include/linux/cpuidle.h
+++ b/include/linux/cpuidle.h
@@ -54,11 +54,11 @@ struct cpuidle_state {
/*
* CPUs execute ->enter_freeze with the local tick or entire timekeeping
* suspended, so it must not re-enable interrupts at any point (even
- * temporarily) or attempt to change states of clock event devices.
+ * temporarily). Returns 0 on success and non-zero if an error occurred.
*/
- void (*enter_freeze) (struct cpuidle_device *dev,
- struct cpuidle_driver *drv,
- int index);
+ int (*enter_freeze) (struct cpuidle_device *dev,
+ struct cpuidle_driver *drv,
+ int index);
};
/* Idle State Flags */
@@ -147,6 +147,8 @@ extern void cpuidle_pause_and_lock(void);
extern void cpuidle_resume_and_unlock(void);
extern void cpuidle_pause(void);
extern void cpuidle_resume(void);
+extern void cpuidle_prepare_freeze(void);
+extern int cpuidle_complete_freeze(void);
extern int cpuidle_enable_device(struct cpuidle_device *dev);
extern void cpuidle_disable_device(struct cpuidle_device *dev);
extern int cpuidle_play_dead(void);
diff --git a/kernel/power/suspend.c b/kernel/power/suspend.c
index 5b70d64..419154b 100644
--- a/kernel/power/suspend.c
+++ b/kernel/power/suspend.c
@@ -57,8 +57,10 @@ static void freeze_begin(void)
suspend_freeze_state = FREEZE_STATE_NONE;
}
-static void freeze_enter(void)
+static int freeze_enter(void)
{
+ int error = 0;
+
spin_lock_irq(&suspend_freeze_lock);
if (pm_wakeup_pending())
goto out;
@@ -67,7 +69,7 @@ static void freeze_enter(void)
spin_unlock_irq(&suspend_freeze_lock);
get_online_cpus();
- cpuidle_resume();
+ cpuidle_prepare_freeze();
/* Push all the CPUs into the idle loop. */
wake_up_all_idle_cpus();
@@ -77,7 +79,7 @@ static void freeze_enter(void)
suspend_freeze_state == FREEZE_STATE_WAKE);
pr_debug("PM: resume from suspend-to-idle\n");
- cpuidle_pause();
+ error = cpuidle_complete_freeze();
put_online_cpus();
spin_lock_irq(&suspend_freeze_lock);
@@ -85,6 +87,7 @@ static void freeze_enter(void)
out:
suspend_freeze_state = FREEZE_STATE_NONE;
spin_unlock_irq(&suspend_freeze_lock);
+ return error;
}
void freeze_wake(void)
@@ -347,7 +350,7 @@ static int suspend_enter(suspend_state_t state, bool *wakeup)
*/
if (state == PM_SUSPEND_FREEZE) {
trace_suspend_resume(TPS("machine_suspend"), state, true);
- freeze_enter();
+ error = freeze_enter();
trace_suspend_resume(TPS("machine_suspend"), state, false);
goto Platform_wake;
}
--
2.8.0.rc3.226.g39d4020
[toc] | [prev] | [standalone]
Back to top | Article view | linux.kernel
csiph-web