Skip to content

Commit

Permalink
Merge branches 'pm-cpuidle', 'pm-core' and 'pm-sleep'
Browse files Browse the repository at this point in the history
Merge cpuidle updates, PM core updates and one hiberation-related
update for 5.17-rc1:

 - Make cpuidle use default_groups in kobj_type (Greg Kroah-Hartman).

 - Fix two comments in cpuidle code (Jason Wang, Yang Li).

 - Simplify locking in pm_runtime_put_suppliers() (Rafael Wysocki).

 - Add safety net to supplier device release in the runtime PM core
   code (Rafael Wysocki).

 - Capture device status before disabling runtime PM for it (Rafael
   Wysocki).

 - Add new macros for declaring PM operations to allow drivers to
   avoid guarding them with CONFIG_PM #ifdefs or __maybe_unused and
   update some drivers to use these macros (Paul Cercueil).

 - Allow ACPI hardware signature to be honoured during restore from
   hibernation (David Woodhouse).

* pm-cpuidle:
  cpuidle: use default_groups in kobj_type
  cpuidle: Fix cpuidle_remove_state_sysfs() kerneldoc comment
  cpuidle: menu: Fix typo in a comment

* pm-core:
  PM: runtime: Simplify locking in pm_runtime_put_suppliers()
  mmc: mxc: Use the new PM macros
  mmc: jz4740: Use the new PM macros
  PM: runtime: Add safety net to supplier device release
  PM: runtime: Capture device status before disabling runtime PM
  PM: core: Add new *_PM_OPS macros, deprecate old ones
  PM: core: Redefine pm_ptr() macro
  r8169: Avoid misuse of pm_ptr() macro

* pm-sleep:
  PM: hibernate: Allow ACPI hardware signature to be honoured
  • Loading branch information
Rafael J. Wysocki committed Jan 10, 2022
4 parents 5561f25 + 7dfc5b6 + 50a4606 + 74d9555 commit c001a52
Show file tree
Hide file tree
Showing 17 changed files with 198 additions and 95 deletions.
15 changes: 12 additions & 3 deletions Documentation/admin-guide/kernel-parameters.txt
Original file line number Diff line number Diff line change
Expand Up @@ -225,14 +225,23 @@
For broken nForce2 BIOS resulting in XT-PIC timer.

acpi_sleep= [HW,ACPI] Sleep options
Format: { s3_bios, s3_mode, s3_beep, s4_nohwsig,
old_ordering, nonvs, sci_force_enable, nobl }
Format: { s3_bios, s3_mode, s3_beep, s4_hwsig,
s4_nohwsig, old_ordering, nonvs,
sci_force_enable, nobl }
See Documentation/power/video.rst for information on
s3_bios and s3_mode.
s3_beep is for debugging; it makes the PC's speaker beep
as soon as the kernel's real-mode entry point is called.
s4_hwsig causes the kernel to check the ACPI hardware
signature during resume from hibernation, and gracefully
refuse to resume if it has changed. This complies with
the ACPI specification but not with reality, since
Windows does not do this and many laptops do change it
on docking. So the default behaviour is to allow resume
and simply warn when the signature changes, unless the
s4_hwsig option is enabled.
s4_nohwsig prevents ACPI hardware signature from being
used during resume from hibernation.
used (or even warned about) during resume.
old_ordering causes the ACPI 1.0 ordering of the _PTS
control method, with respect to putting devices into
low power states, to be enforced (the ACPI 2.0 ordering
Expand Down
14 changes: 10 additions & 4 deletions Documentation/power/runtime_pm.rst
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,10 @@ defined in include/linux/pm.h:
RPM_SUSPENDED, which means that each device is initially regarded by the
PM core as 'suspended', regardless of its real hardware status

`enum rpm_status last_status;`
- the last runtime PM status of the device captured before disabling runtime
PM for it (invalid initially and when disable_depth is 0)

`unsigned int runtime_auto;`
- if set, indicates that the user space has allowed the device driver to
power manage the device at run time via the /sys/devices/.../power/control
Expand Down Expand Up @@ -333,10 +337,12 @@ drivers/base/power/runtime.c and include/linux/pm_runtime.h:

`int pm_runtime_resume(struct device *dev);`
- execute the subsystem-level resume callback for the device; returns 0 on
success, 1 if the device's runtime PM status was already 'active' or
error code on failure, where -EAGAIN means it may be safe to attempt to
resume the device again in future, but 'power.runtime_error' should be
checked additionally, and -EACCES means that 'power.disable_depth' is
success, 1 if the device's runtime PM status is already 'active' (also if
'power.disable_depth' is nonzero, but the status was 'active' when it was
changing from 0 to 1) or error code on failure, where -EAGAIN means it may
be safe to attempt to resume the device again in future, but
'power.runtime_error' should be checked additionally, and -EACCES means
that the callback could not be run, because 'power.disable_depth' was
different from 0

`int pm_runtime_resume_and_get(struct device *dev);`
Expand Down
4 changes: 3 additions & 1 deletion arch/x86/kernel/acpi/sleep.c
Original file line number Diff line number Diff line change
Expand Up @@ -139,8 +139,10 @@ static int __init acpi_sleep_setup(char *str)
if (strncmp(str, "s3_beep", 7) == 0)
acpi_realmode_flags |= 4;
#ifdef CONFIG_HIBERNATION
if (strncmp(str, "s4_hwsig", 8) == 0)
acpi_check_s4_hw_signature(1);
if (strncmp(str, "s4_nohwsig", 10) == 0)
acpi_no_s4_hw_signature();
acpi_check_s4_hw_signature(0);
#endif
if (strncmp(str, "nonvs", 5) == 0)
acpi_nvs_nosave();
Expand Down
26 changes: 21 additions & 5 deletions drivers/acpi/sleep.c
Original file line number Diff line number Diff line change
Expand Up @@ -877,11 +877,11 @@ static inline void acpi_sleep_syscore_init(void) {}
#ifdef CONFIG_HIBERNATION
static unsigned long s4_hardware_signature;
static struct acpi_table_facs *facs;
static bool nosigcheck;
static int sigcheck = -1; /* Default behaviour is just to warn */

void __init acpi_no_s4_hw_signature(void)
void __init acpi_check_s4_hw_signature(int check)
{
nosigcheck = true;
sigcheck = check;
}

static int acpi_hibernation_begin(pm_message_t stage)
Expand Down Expand Up @@ -1009,12 +1009,28 @@ static void acpi_sleep_hibernate_setup(void)
hibernation_set_ops(old_suspend_ordering ?
&acpi_hibernation_ops_old : &acpi_hibernation_ops);
sleep_states[ACPI_STATE_S4] = 1;
if (nosigcheck)
if (!sigcheck)
return;

acpi_get_table(ACPI_SIG_FACS, 1, (struct acpi_table_header **)&facs);
if (facs)
if (facs) {
/*
* s4_hardware_signature is the local variable which is just
* used to warn about mismatch after we're attempting to
* resume (in violation of the ACPI specification.)
*/
s4_hardware_signature = facs->hardware_signature;

if (sigcheck > 0) {
/*
* If we're actually obeying the ACPI specification
* then the signature is written out as part of the
* swsusp header, in order to allow the boot kernel
* to gracefully decline to resume.
*/
swsusp_hardware_signature = facs->hardware_signature;
}
}
}
#else /* !CONFIG_HIBERNATION */
static inline void acpi_sleep_hibernate_setup(void) {}
Expand Down
3 changes: 1 addition & 2 deletions drivers/base/core.c
Original file line number Diff line number Diff line change
Expand Up @@ -485,8 +485,7 @@ static void device_link_release_fn(struct work_struct *work)
/* Ensure that all references to the link object have been dropped. */
device_link_synchronize_removal();

while (refcount_dec_not_one(&link->rpm_active))
pm_runtime_put(link->supplier);
pm_runtime_release_supplier(link, true);

put_device(link->consumer);
put_device(link->supplier);
Expand Down
98 changes: 63 additions & 35 deletions drivers/base/power/runtime.c
Original file line number Diff line number Diff line change
Expand Up @@ -305,19 +305,40 @@ static int rpm_get_suppliers(struct device *dev)
return 0;
}

/**
* pm_runtime_release_supplier - Drop references to device link's supplier.
* @link: Target device link.
* @check_idle: Whether or not to check if the supplier device is idle.
*
* Drop all runtime PM references associated with @link to its supplier device
* and if @check_idle is set, check if that device is idle (and so it can be
* suspended).
*/
void pm_runtime_release_supplier(struct device_link *link, bool check_idle)
{
struct device *supplier = link->supplier;

/*
* The additional power.usage_count check is a safety net in case
* the rpm_active refcount becomes saturated, in which case
* refcount_dec_not_one() would return true forever, but it is not
* strictly necessary.
*/
while (refcount_dec_not_one(&link->rpm_active) &&
atomic_read(&supplier->power.usage_count) > 0)
pm_runtime_put_noidle(supplier);

if (check_idle)
pm_request_idle(supplier);
}

static void __rpm_put_suppliers(struct device *dev, bool try_to_suspend)
{
struct device_link *link;

list_for_each_entry_rcu(link, &dev->links.suppliers, c_node,
device_links_read_lock_held()) {

while (refcount_dec_not_one(&link->rpm_active))
pm_runtime_put_noidle(link->supplier);

if (try_to_suspend)
pm_request_idle(link->supplier);
}
device_links_read_lock_held())
pm_runtime_release_supplier(link, try_to_suspend);
}

static void rpm_put_suppliers(struct device *dev)
Expand Down Expand Up @@ -742,13 +763,15 @@ static int rpm_resume(struct device *dev, int rpmflags)
trace_rpm_resume_rcuidle(dev, rpmflags);

repeat:
if (dev->power.runtime_error)
if (dev->power.runtime_error) {
retval = -EINVAL;
else if (dev->power.disable_depth == 1 && dev->power.is_suspended
&& dev->power.runtime_status == RPM_ACTIVE)
retval = 1;
else if (dev->power.disable_depth > 0)
retval = -EACCES;
} else if (dev->power.disable_depth > 0) {
if (dev->power.runtime_status == RPM_ACTIVE &&
dev->power.last_status == RPM_ACTIVE)
retval = 1;
else
retval = -EACCES;
}
if (retval)
goto out;

Expand Down Expand Up @@ -1410,8 +1433,10 @@ void __pm_runtime_disable(struct device *dev, bool check_resume)
/* Update time accounting before disabling PM-runtime. */
update_pm_runtime_accounting(dev);

if (!dev->power.disable_depth++)
if (!dev->power.disable_depth++) {
__pm_runtime_barrier(dev);
dev->power.last_status = dev->power.runtime_status;
}

out:
spin_unlock_irq(&dev->power.lock);
Expand All @@ -1428,23 +1453,23 @@ void pm_runtime_enable(struct device *dev)

spin_lock_irqsave(&dev->power.lock, flags);

if (dev->power.disable_depth > 0) {
dev->power.disable_depth--;

/* About to enable runtime pm, set accounting_timestamp to now */
if (!dev->power.disable_depth)
dev->power.accounting_timestamp = ktime_get_mono_fast_ns();
} else {
if (!dev->power.disable_depth) {
dev_warn(dev, "Unbalanced %s!\n", __func__);
goto out;
}

WARN(!dev->power.disable_depth &&
dev->power.runtime_status == RPM_SUSPENDED &&
!dev->power.ignore_children &&
atomic_read(&dev->power.child_count) > 0,
"Enabling runtime PM for inactive device (%s) with active children\n",
dev_name(dev));
if (--dev->power.disable_depth > 0)
goto out;

dev->power.last_status = RPM_INVALID;
dev->power.accounting_timestamp = ktime_get_mono_fast_ns();

if (dev->power.runtime_status == RPM_SUSPENDED &&
!dev->power.ignore_children &&
atomic_read(&dev->power.child_count) > 0)
dev_warn(dev, "Enabling runtime PM for inactive device with active children\n");

out:
spin_unlock_irqrestore(&dev->power.lock, flags);
}
EXPORT_SYMBOL_GPL(pm_runtime_enable);
Expand Down Expand Up @@ -1640,6 +1665,7 @@ EXPORT_SYMBOL_GPL(__pm_runtime_use_autosuspend);
void pm_runtime_init(struct device *dev)
{
dev->power.runtime_status = RPM_SUSPENDED;
dev->power.last_status = RPM_INVALID;
dev->power.idle_notification = false;

dev->power.disable_depth = 1;
Expand Down Expand Up @@ -1722,20 +1748,24 @@ void pm_runtime_get_suppliers(struct device *dev)
void pm_runtime_put_suppliers(struct device *dev)
{
struct device_link *link;
unsigned long flags;
bool put;
int idx;

idx = device_links_read_lock();

list_for_each_entry_rcu(link, &dev->links.suppliers, c_node,
device_links_read_lock_held())
if (link->supplier_preactivated) {
bool put;

link->supplier_preactivated = false;
spin_lock_irqsave(&dev->power.lock, flags);

spin_lock_irq(&dev->power.lock);

put = pm_runtime_status_suspended(dev) &&
refcount_dec_not_one(&link->rpm_active);
spin_unlock_irqrestore(&dev->power.lock, flags);

spin_unlock_irq(&dev->power.lock);

if (put)
pm_runtime_put(link->supplier);
}
Expand Down Expand Up @@ -1772,9 +1802,7 @@ void pm_runtime_drop_link(struct device_link *link)
return;

pm_runtime_drop_link_count(link->consumer);

while (refcount_dec_not_one(&link->rpm_active))
pm_runtime_put(link->supplier);
pm_runtime_release_supplier(link, true);
}

static bool pm_runtime_need_not_resume(struct device *dev)
Expand Down
2 changes: 1 addition & 1 deletion drivers/cpuidle/governors/menu.c
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
* 1) Energy break even point
* 2) Performance impact
* 3) Latency tolerance (from pmqos infrastructure)
* These these three factors are treated independently.
* These three factors are treated independently.
*
* Energy break even point
* -----------------------
Expand Down
8 changes: 5 additions & 3 deletions drivers/cpuidle/sysfs.c
Original file line number Diff line number Diff line change
Expand Up @@ -335,6 +335,7 @@ static struct attribute *cpuidle_state_default_attrs[] = {
&attr_default_status.attr,
NULL
};
ATTRIBUTE_GROUPS(cpuidle_state_default);

struct cpuidle_state_kobj {
struct cpuidle_state *state;
Expand Down Expand Up @@ -448,7 +449,7 @@ static void cpuidle_state_sysfs_release(struct kobject *kobj)

static struct kobj_type ktype_state_cpuidle = {
.sysfs_ops = &cpuidle_state_sysfs_ops,
.default_attrs = cpuidle_state_default_attrs,
.default_groups = cpuidle_state_default_groups,
.release = cpuidle_state_sysfs_release,
};

Expand Down Expand Up @@ -505,7 +506,7 @@ static int cpuidle_add_state_sysfs(struct cpuidle_device *device)
}

/**
* cpuidle_remove_driver_sysfs - removes the cpuidle states sysfs attributes
* cpuidle_remove_state_sysfs - removes the cpuidle states sysfs attributes
* @device: the target device
*/
static void cpuidle_remove_state_sysfs(struct cpuidle_device *device)
Expand Down Expand Up @@ -591,10 +592,11 @@ static struct attribute *cpuidle_driver_default_attrs[] = {
&attr_driver_name.attr,
NULL
};
ATTRIBUTE_GROUPS(cpuidle_driver_default);

static struct kobj_type ktype_driver_cpuidle = {
.sysfs_ops = &cpuidle_driver_sysfs_ops,
.default_attrs = cpuidle_driver_default_attrs,
.default_groups = cpuidle_driver_default_groups,
.release = cpuidle_driver_sysfs_release,
};

Expand Down
8 changes: 4 additions & 4 deletions drivers/mmc/host/jz4740_mmc.c
Original file line number Diff line number Diff line change
Expand Up @@ -1103,17 +1103,17 @@ static int jz4740_mmc_remove(struct platform_device *pdev)
return 0;
}

static int __maybe_unused jz4740_mmc_suspend(struct device *dev)
static int jz4740_mmc_suspend(struct device *dev)
{
return pinctrl_pm_select_sleep_state(dev);
}

static int __maybe_unused jz4740_mmc_resume(struct device *dev)
static int jz4740_mmc_resume(struct device *dev)
{
return pinctrl_select_default_state(dev);
}

static SIMPLE_DEV_PM_OPS(jz4740_mmc_pm_ops, jz4740_mmc_suspend,
DEFINE_SIMPLE_DEV_PM_OPS(jz4740_mmc_pm_ops, jz4740_mmc_suspend,
jz4740_mmc_resume);

static struct platform_driver jz4740_mmc_driver = {
Expand All @@ -1123,7 +1123,7 @@ static struct platform_driver jz4740_mmc_driver = {
.name = "jz4740-mmc",
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
.of_match_table = of_match_ptr(jz4740_mmc_of_match),
.pm = pm_ptr(&jz4740_mmc_pm_ops),
.pm = pm_sleep_ptr(&jz4740_mmc_pm_ops),
},
};

Expand Down
6 changes: 2 additions & 4 deletions drivers/mmc/host/mxcmmc.c
Original file line number Diff line number Diff line change
Expand Up @@ -1183,7 +1183,6 @@ static int mxcmci_remove(struct platform_device *pdev)
return 0;
}

#ifdef CONFIG_PM_SLEEP
static int mxcmci_suspend(struct device *dev)
{
struct mmc_host *mmc = dev_get_drvdata(dev);
Expand All @@ -1210,17 +1209,16 @@ static int mxcmci_resume(struct device *dev)

return ret;
}
#endif

static SIMPLE_DEV_PM_OPS(mxcmci_pm_ops, mxcmci_suspend, mxcmci_resume);
DEFINE_SIMPLE_DEV_PM_OPS(mxcmci_pm_ops, mxcmci_suspend, mxcmci_resume);

static struct platform_driver mxcmci_driver = {
.probe = mxcmci_probe,
.remove = mxcmci_remove,
.driver = {
.name = DRIVER_NAME,
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
.pm = &mxcmci_pm_ops,
.pm = pm_sleep_ptr(&mxcmci_pm_ops),
.of_match_table = mxcmci_of_match,
}
};
Expand Down
Loading

0 comments on commit c001a52

Please sign in to comment.