Commit a373ec23 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'pm-5.2-rc4' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm

Pull power management fixes from Rafael Wysocki:
 "These fix a crash during resume from hibernation introduced during the
  4.19 cycle, cause the new Performance and Energy Bias Hint (EPB) code
  to be built only if CONFIG_PM is set and add a few missing kerneldoc
  comments.

  Specifics:

   - Fix a crash that occurs when a kernel with 'nosmt' in the command
     line is used to resume the system from hibernation (as the
     "restore" kernel), because memory mapping differences between the
     restore and image kernels cause SMT siblings to be woken up from
     idle states and subsequently they try to fetch instructions from
     incorrect memory locations (Jiri Kosina).

   - Cause the new Performance and Energy Bias Hint (EPB) code to be
     built only if CONFIG_PM is set, because that code is not really
     necessary otherwise (Rafael Wysocki).

   - Add kerneldoc comments to documents some helper functions related
     to system-wide suspend to avoid possible confusion regarding their
     purpose (Rafael Wysocki)"

* tag 'pm-5.2-rc4' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm:
  x86/power: Fix 'nosmt' vs hibernation triple fault during resume
  PM: sleep: Add kerneldoc comments to some functions
  x86: intel_epb: Do not build when CONFIG_PM is unset
parents de9f8696 a964d23c
......@@ -28,7 +28,10 @@ obj-y += cpuid-deps.o
obj-$(CONFIG_PROC_FS) += proc.o
obj-$(CONFIG_X86_FEATURE_NAMES) += capflags.o powerflags.o
obj-$(CONFIG_CPU_SUP_INTEL) += intel.o intel_pconfig.o intel_epb.o
ifdef CONFIG_CPU_SUP_INTEL
obj-y += intel.o intel_pconfig.o
obj-$(CONFIG_PM) += intel_epb.o
endif
obj-$(CONFIG_CPU_SUP_AMD) += amd.o
obj-$(CONFIG_CPU_SUP_HYGON) += hygon.o
obj-$(CONFIG_CPU_SUP_CYRIX_32) += cyrix.o
......
......@@ -97,7 +97,6 @@ static void intel_epb_restore(void)
wrmsrl(MSR_IA32_ENERGY_PERF_BIAS, (epb & ~EPB_MASK) | val);
}
#ifdef CONFIG_PM
static struct syscore_ops intel_epb_syscore_ops = {
.suspend = intel_epb_save,
.resume = intel_epb_restore,
......@@ -194,25 +193,6 @@ static int intel_epb_offline(unsigned int cpu)
return 0;
}
static inline void register_intel_ebp_syscore_ops(void)
{
register_syscore_ops(&intel_epb_syscore_ops);
}
#else /* !CONFIG_PM */
static int intel_epb_online(unsigned int cpu)
{
intel_epb_restore();
return 0;
}
static int intel_epb_offline(unsigned int cpu)
{
return intel_epb_save();
}
static inline void register_intel_ebp_syscore_ops(void) {}
#endif
static __init int intel_epb_init(void)
{
int ret;
......@@ -226,7 +206,7 @@ static __init int intel_epb_init(void)
if (ret < 0)
goto err_out_online;
register_intel_ebp_syscore_ops();
register_syscore_ops(&intel_epb_syscore_ops);
return 0;
err_out_online:
......
......@@ -299,7 +299,17 @@ int hibernate_resume_nonboot_cpu_disable(void)
* address in its instruction pointer may not be possible to resolve
* any more at that point (the page tables used by it previously may
* have been overwritten by hibernate image data).
*
* First, make sure that we wake up all the potentially disabled SMT
* threads which have been initially brought up and then put into
* mwait/cpuidle sleep.
* Those will be put to proper (not interfering with hibernation
* resume) sleep afterwards, and the resumed kernel will decide itself
* what to do with them.
*/
ret = cpuhp_smt_enable();
if (ret)
return ret;
smp_ops.play_dead = resume_play_dead;
ret = disable_nonboot_cpus();
smp_ops.play_dead = play_dead;
......
......@@ -11,6 +11,7 @@
#include <linux/suspend.h>
#include <linux/scatterlist.h>
#include <linux/kdebug.h>
#include <linux/cpu.h>
#include <crypto/hash.h>
......@@ -245,3 +246,35 @@ int relocate_restore_code(void)
__flush_tlb_all();
return 0;
}
int arch_resume_nosmt(void)
{
int ret = 0;
/*
* We reached this while coming out of hibernation. This means
* that SMT siblings are sleeping in hlt, as mwait is not safe
* against control transition during resume (see comment in
* hibernate_resume_nonboot_cpu_disable()).
*
* If the resumed kernel has SMT disabled, we have to take all the
* SMT siblings out of hlt, and offline them again so that they
* end up in mwait proper.
*
* Called with hotplug disabled.
*/
cpu_hotplug_enable();
if (cpu_smt_control == CPU_SMT_DISABLED ||
cpu_smt_control == CPU_SMT_FORCE_DISABLED) {
enum cpuhp_smt_control old = cpu_smt_control;
ret = cpuhp_smt_enable();
if (ret)
goto out;
ret = cpuhp_smt_disable(old);
if (ret)
goto out;
}
out:
cpu_hotplug_disable();
return ret;
}
......@@ -201,10 +201,14 @@ enum cpuhp_smt_control {
extern enum cpuhp_smt_control cpu_smt_control;
extern void cpu_smt_disable(bool force);
extern void cpu_smt_check_topology(void);
extern int cpuhp_smt_enable(void);
extern int cpuhp_smt_disable(enum cpuhp_smt_control ctrlval);
#else
# define cpu_smt_control (CPU_SMT_NOT_IMPLEMENTED)
static inline void cpu_smt_disable(bool force) { }
static inline void cpu_smt_check_topology(void) { }
static inline int cpuhp_smt_enable(void) { return 0; }
static inline int cpuhp_smt_disable(enum cpuhp_smt_control ctrlval) { return 0; }
#endif
/*
......
......@@ -227,11 +227,42 @@ static inline void pm_set_resume_via_firmware(void)
pm_suspend_global_flags |= PM_SUSPEND_FLAG_FW_RESUME;
}
/**
* pm_suspend_via_firmware - Check if platform firmware will suspend the system.
*
* To be called during system-wide power management transitions to sleep states
* or during the subsequent system-wide transitions back to the working state.
*
* Return 'true' if the platform firmware is going to be invoked at the end of
* the system-wide power management transition (to a sleep state) in progress in
* order to complete it, or if the platform firmware has been invoked in order
* to complete the last (or preceding) transition of the system to a sleep
* state.
*
* This matters if the caller needs or wants to carry out some special actions
* depending on whether or not control will be passed to the platform firmware
* subsequently (for example, the device may need to be reset before letting the
* platform firmware manipulate it, which is not necessary when the platform
* firmware is not going to be invoked) or when such special actions may have
* been carried out during the preceding transition of the system to a sleep
* state (as they may need to be taken into account).
*/
static inline bool pm_suspend_via_firmware(void)
{
return !!(pm_suspend_global_flags & PM_SUSPEND_FLAG_FW_SUSPEND);
}
/**
* pm_resume_via_firmware - Check if platform firmware has woken up the system.
*
* To be called during system-wide power management transitions from sleep
* states.
*
* Return 'true' if the platform firmware has passed control to the kernel at
* the beginning of the system-wide power management transition in progress, so
* the event that woke up the system from sleep has been handled by the platform
* firmware.
*/
static inline bool pm_resume_via_firmware(void)
{
return !!(pm_suspend_global_flags & PM_SUSPEND_FLAG_FW_RESUME);
......
......@@ -2061,7 +2061,7 @@ static void cpuhp_online_cpu_device(unsigned int cpu)
kobject_uevent(&dev->kobj, KOBJ_ONLINE);
}
static int cpuhp_smt_disable(enum cpuhp_smt_control ctrlval)
int cpuhp_smt_disable(enum cpuhp_smt_control ctrlval)
{
int cpu, ret = 0;
......@@ -2093,7 +2093,7 @@ static int cpuhp_smt_disable(enum cpuhp_smt_control ctrlval)
return ret;
}
static int cpuhp_smt_enable(void)
int cpuhp_smt_enable(void)
{
int cpu, ret = 0;
......
......@@ -257,6 +257,11 @@ void swsusp_show_speed(ktime_t start, ktime_t stop,
(kps % 1000) / 10);
}
__weak int arch_resume_nosmt(void)
{
return 0;
}
/**
* create_image - Create a hibernation image.
* @platform_mode: Whether or not to use the platform driver.
......@@ -324,6 +329,10 @@ static int create_image(int platform_mode)
Enable_cpus:
suspend_enable_secondary_cpus();
/* Allow architectures to do nosmt-specific post-resume dances */
if (!in_suspend)
error = arch_resume_nosmt();
Platform_finish:
platform_finish(platform_mode);
......
......@@ -62,6 +62,12 @@ static DECLARE_SWAIT_QUEUE_HEAD(s2idle_wait_head);
enum s2idle_states __read_mostly s2idle_state;
static DEFINE_RAW_SPINLOCK(s2idle_lock);
/**
* pm_suspend_via_s2idle - Check if suspend-to-idle is the default suspend.
*
* Return 'true' if suspend-to-idle has been selected as the default system
* suspend method.
*/
bool pm_suspend_via_s2idle(void)
{
return mem_sleep_current == PM_SUSPEND_TO_IDLE;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment