@@ -174,6 +174,8 @@ static ssize_t mem_sleep_show(struct kobject *kobj, struct kobj_attribute *attr,
for (i = PM_SUSPEND_MIN; i < PM_SUSPEND_MAX; i++) {
if (i >= PM_SUSPEND_MEM && cxl_mem_active())
continue;
+ if (i == PM_SUSPEND_TO_IDLE && cpuidle_not_available())
+ continue;
if (mem_sleep_states[i]) {
const char *label = mem_sleep_states[i];
@@ -226,11 +228,15 @@ static ssize_t mem_sleep_store(struct kobject *kobj, struct kobj_attribute *attr
}
state = decode_suspend_state(buf, n);
- if (state < PM_SUSPEND_MAX && state > PM_SUSPEND_ON)
+ if (state == PM_SUSPEND_TO_IDLE && cpuidle_not_available())
+ goto err;
+ if (state < PM_SUSPEND_MAX && state > PM_SUSPEND_ON) {
mem_sleep_current = state;
- else
- error = -EINVAL;
+ goto out;
+ }
+ err:
+ error = -EINVAL;
out:
pm_autosleep_unlock();
return error ? error : n;
@@ -556,6 +556,11 @@ static int enter_state(suspend_state_t state)
trace_suspend_resume(TPS("suspend_enter"), state, true);
if (state == PM_SUSPEND_TO_IDLE) {
+ if (cpuidle_not_available()) {
+ pr_warn("s2idle is unsupported when cpuidle is unavailable");
+ return -EINVAL;
+ }
+
#ifdef CONFIG_PM_DEBUG
if (pm_test_level != TEST_NONE && pm_test_level <= TEST_CPUS) {
pr_warn("Unsupported test mode for suspend to idle, please choose none/freezer/devices/platform.\n");
In order for systems to properly enter s2idle, we need functions both in the idle subsystem (such as call_cpuidle_s2idle()) and the suspend subsystem to be executed. s2idle got blocked in the idle subsystem on platforms without cpuidle after commit ef2b22ac540c ("cpuidle / sleep: Use broadcast timer for states that stop local timer"). However, the suspend subsystem doesn't have this, which can cause the suspend subsystem to begin entering s2idle behind the idle subsystem's back, which in turn can cause the system to enter s2idle even though all the functions necessary for s2idle hasn't been executed, breaking the system (e.g. ClOCK_MONOTONIC keeps ticking during suspend even though it's not supposed to). Prevent the system from entering s2idle when cpuidle isn't supported in the suspend subsystem as well. Fixes: ef2b22ac540c ("cpuidle / sleep: Use broadcast timer for states that stop local timer") Signed-off-by: Kazuki Hashimoto <kazukih0205@gmail.com> --- kernel/power/main.c | 12 +++++++++--- kernel/power/suspend.c | 5 +++++ 2 files changed, 14 insertions(+), 3 deletions(-)