summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRafael J. Wysocki <rafael.j.wysocki@intel.com>2026-02-04 22:52:09 +0300
committerRafael J. Wysocki <rafael.j.wysocki@intel.com>2026-02-04 22:52:09 +0300
commitc233403593f55c5211c0806d9869508490f218c7 (patch)
tree850f59e3d3d2359821904347585bf38fe186c800
parent6d367141eb68ced4d1e1fc97e97c59be1daa3085 (diff)
parent0491f3f9f664e7e0131eb4d2a8b19c49562e5c64 (diff)
downloadlinux-c233403593f55c5211c0806d9869508490f218c7.tar.xz
Merge branch 'pm-sleep'
Merge updates related to system suspend and hibernation for 6.20-rc1/7.0-rc1: - Stop flagging the PM runtime workqueue as freezable to avoid system suspend and resume deadlocks in subsystems that assume asynchronous runtime PM to work during system-wide PM transitions (Rafael Wysocki) - Drop redundant NULL pointer checks before acomp_request_free() from the hibernation code handling image saving (Rafael Wysocki) - Update wakeup_sources_walk_start() to handle empty lists of wakeup sources as appropriate (Samuel Wu) - Make dev_pm_clear_wake_irq() check the power.wakeirq value under power.lock to avoid race conditions (Gui-Dong Han) - Avoid bit field races related to power.work_in_progress in the core device suspend code (Xuewen Yan) * pm-sleep: PM: sleep: core: Avoid bit field races related to work_in_progress PM: sleep: wakeirq: harden dev_pm_clear_wake_irq() against races PM: wakeup: Handle empty list in wakeup_sources_walk_start() PM: hibernate: Drop NULL pointer checks before acomp_request_free() PM: sleep: Do not flag runtime PM workqueue as freezable
-rw-r--r--Documentation/power/runtime_pm.rst7
-rw-r--r--drivers/base/power/main.c7
-rw-r--r--drivers/base/power/wakeirq.c9
-rw-r--r--drivers/base/power/wakeup.c4
-rw-r--r--include/linux/pm.h2
-rw-r--r--kernel/power/main.c2
-rw-r--r--kernel/power/swap.c8
7 files changed, 20 insertions, 19 deletions
diff --git a/Documentation/power/runtime_pm.rst b/Documentation/power/runtime_pm.rst
index 455b9d135d85..a53ab09c37d5 100644
--- a/Documentation/power/runtime_pm.rst
+++ b/Documentation/power/runtime_pm.rst
@@ -712,10 +712,9 @@ out the following operations:
* During system suspend pm_runtime_get_noresume() is called for every device
right before executing the subsystem-level .prepare() callback for it and
pm_runtime_barrier() is called for every device right before executing the
- subsystem-level .suspend() callback for it. In addition to that the PM core
- calls __pm_runtime_disable() with 'false' as the second argument for every
- device right before executing the subsystem-level .suspend_late() callback
- for it.
+ subsystem-level .suspend() callback for it. In addition to that, the PM
+ core disables runtime PM for every device right before executing the
+ subsystem-level .suspend_late() callback for it.
* During system resume pm_runtime_enable() and pm_runtime_put() are called for
every device right after executing the subsystem-level .resume_early()
diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c
index 97a8b4fcf471..189de5250f25 100644
--- a/drivers/base/power/main.c
+++ b/drivers/base/power/main.c
@@ -1647,10 +1647,11 @@ static void device_suspend_late(struct device *dev, pm_message_t state, bool asy
goto Complete;
/*
- * Disable runtime PM for the device without checking if there is a
- * pending resume request for it.
+ * After this point, any runtime PM operations targeting the device
+ * will fail until the corresponding pm_runtime_enable() call in
+ * device_resume_early().
*/
- __pm_runtime_disable(dev, false);
+ pm_runtime_disable(dev);
if (dev->power.syscore)
goto Skip;
diff --git a/drivers/base/power/wakeirq.c b/drivers/base/power/wakeirq.c
index 8aa28c08b289..c0809d18fc54 100644
--- a/drivers/base/power/wakeirq.c
+++ b/drivers/base/power/wakeirq.c
@@ -83,13 +83,16 @@ EXPORT_SYMBOL_GPL(dev_pm_set_wake_irq);
*/
void dev_pm_clear_wake_irq(struct device *dev)
{
- struct wake_irq *wirq = dev->power.wakeirq;
+ struct wake_irq *wirq;
unsigned long flags;
- if (!wirq)
+ spin_lock_irqsave(&dev->power.lock, flags);
+ wirq = dev->power.wakeirq;
+ if (!wirq) {
+ spin_unlock_irqrestore(&dev->power.lock, flags);
return;
+ }
- spin_lock_irqsave(&dev->power.lock, flags);
device_wakeup_detach_irq(dev);
dev->power.wakeirq = NULL;
spin_unlock_irqrestore(&dev->power.lock, flags);
diff --git a/drivers/base/power/wakeup.c b/drivers/base/power/wakeup.c
index 1e1a0e7eeac5..e69033d16fba 100644
--- a/drivers/base/power/wakeup.c
+++ b/drivers/base/power/wakeup.c
@@ -275,9 +275,7 @@ EXPORT_SYMBOL_GPL(wakeup_sources_read_unlock);
*/
struct wakeup_source *wakeup_sources_walk_start(void)
{
- struct list_head *ws_head = &wakeup_sources;
-
- return list_entry_rcu(ws_head->next, struct wakeup_source, entry);
+ return list_first_or_null_rcu(&wakeup_sources, struct wakeup_source, entry);
}
EXPORT_SYMBOL_GPL(wakeup_sources_walk_start);
diff --git a/include/linux/pm.h b/include/linux/pm.h
index 98a899858ece..afcaaa37a812 100644
--- a/include/linux/pm.h
+++ b/include/linux/pm.h
@@ -681,10 +681,10 @@ struct dev_pm_info {
struct list_head entry;
struct completion completion;
struct wakeup_source *wakeup;
+ bool work_in_progress; /* Owned by the PM core */
bool wakeup_path:1;
bool syscore:1;
bool no_pm_callbacks:1; /* Owned by the PM core */
- bool work_in_progress:1; /* Owned by the PM core */
bool smart_suspend:1; /* Owned by the PM core */
bool must_resume:1; /* Owned by the PM core */
bool may_skip_resume:1; /* Set by subsystems */
diff --git a/kernel/power/main.c b/kernel/power/main.c
index 03b2c5495c77..5f8c9e12eaec 100644
--- a/kernel/power/main.c
+++ b/kernel/power/main.c
@@ -1125,7 +1125,7 @@ EXPORT_SYMBOL_GPL(pm_wq);
static int __init pm_start_workqueues(void)
{
- pm_wq = alloc_workqueue("pm", WQ_FREEZABLE | WQ_UNBOUND, 0);
+ pm_wq = alloc_workqueue("pm", WQ_UNBOUND, 0);
if (!pm_wq)
return -ENOMEM;
diff --git a/kernel/power/swap.c b/kernel/power/swap.c
index 8050e5182835..7e462957c9bf 100644
--- a/kernel/power/swap.c
+++ b/kernel/power/swap.c
@@ -902,8 +902,8 @@ out_clean:
for (thr = 0; thr < nr_threads; thr++) {
if (data[thr].thr)
kthread_stop(data[thr].thr);
- if (data[thr].cr)
- acomp_request_free(data[thr].cr);
+
+ acomp_request_free(data[thr].cr);
if (!IS_ERR_OR_NULL(data[thr].cc))
crypto_free_acomp(data[thr].cc);
@@ -1502,8 +1502,8 @@ out_clean:
for (thr = 0; thr < nr_threads; thr++) {
if (data[thr].thr)
kthread_stop(data[thr].thr);
- if (data[thr].cr)
- acomp_request_free(data[thr].cr);
+
+ acomp_request_free(data[thr].cr);
if (!IS_ERR_OR_NULL(data[thr].cc))
crypto_free_acomp(data[thr].cc);