[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <8fa4023f-50f2-4e25-9f9b-4e5236015e27@vivo.com>
Date: Mon, 1 Dec 2025 20:56:06 +0800
From: YangYang <yang.yang@...o.com>
To: "Rafael J. Wysocki" <rafael@...nel.org>,
Bart Van Assche <bvanassche@....org>
Cc: Jens Axboe <axboe@...nel.dk>, Pavel Machek <pavel@...nel.org>,
Len Brown <lenb@...nel.org>, Greg Kroah-Hartman
<gregkh@...uxfoundation.org>, Danilo Krummrich <dakr@...nel.org>,
linux-block@...r.kernel.org, linux-kernel@...r.kernel.org,
linux-pm@...r.kernel.org
Subject: Re: [PATCH 1/2] PM: runtime: Fix I/O hang due to race between resume
and runtime disable
On 2025/12/1 17:46, YangYang wrote:
> On 2025/11/27 20:34, Rafael J. Wysocki wrote:
>> On Wed, Nov 26, 2025 at 11:47 PM Bart Van Assche <bvanassche@....org> wrote:
>>>
>>> On 11/26/25 1:30 PM, Rafael J. Wysocki wrote:
>>>> On Wed, Nov 26, 2025 at 10:11 PM Bart Van Assche <bvanassche@....org> wrote:
>>>>>
>>>>> On 11/26/25 12:17 PM, Rafael J. Wysocki wrote:
>>>>>> --- a/block/blk-core.c
>>>>>> +++ b/block/blk-core.c
>>>>>> @@ -309,6 +309,8 @@ int blk_queue_enter(struct request_queue
>>>>>> if (flags & BLK_MQ_REQ_NOWAIT)
>>>>>> return -EAGAIN;
>>>>>>
>>>>>> + /* if necessary, resume .dev (assume success). */
>>>>>> + blk_pm_resume_queue(pm, q);
>>>>>> /*
>>>>>> * read pair of barrier in blk_freeze_queue_start(), we need to
>>>>>> * order reading __PERCPU_REF_DEAD flag of .q_usage_counter and
>>>>>
>>>>> blk_queue_enter() may be called from the suspend path so I don't think
>>>>> that the above change will work.
>>>>
>>>> Why would the existing code work then?
>>>
>>> The existing code works reliably on a very large number of devices.
>>
>> Well, except that it doesn't work during system suspend and
>> hibernation when the PM workqueue is frozen. I think that we agree
>> here.
>>
>> This needs to be addressed because it may very well cause system
>> suspend to deadlock.
>>
>> There are two possible ways to address it I can think of:
>>
>> 1. Changing blk_pm_resume_queue() and its users to carry out a
>> synchronous resume of q->dev instead of calling pm_request_resume()
>> and (effectively) waiting for the queued-up runtime resume of q->dev
>> to take effect.
>>
>> This would be my preferred option, but at this point I'm not sure if
>> it's viable.
>>
>
> After __pm_runtime_disable() is called from device_suspend_late(), dev->power.disable_depth is set, preventing
> rpm_resume() from making progress until the system resume completes, regardless of whether rpm_resume() is invoked
> synchronously or asynchronously.
> Performing a synchronous resume of q->dev seems to have a similar effect to removing the following code block from
> __pm_runtime_barrier(), which is invoked by __pm_runtime_disable():
>
> 1428 if (dev->power.request_pending) {
> 1429 dev->power.request = RPM_REQ_NONE;
> 1430 spin_unlock_irq(&dev->power.lock);
> 1431
> 1432 cancel_work_sync(&dev->power.work);
> 1433
> 1434 spin_lock_irq(&dev->power.lock);
> 1435 dev->power.request_pending = false;
> 1436 }
>
Since both synchronous and asynchronous resumes face similar issues,
it may be sufficient to keep using the asynchronous resume path as long as
pending work items are not canceled while the PM workqueue is frozen.
This allows the pending work to proceed normally once the PM workqueue
is unfrozen.
---
drivers/base/power/main.c | 2 +-
drivers/base/power/runtime.c | 17 +++++++++++------
include/linux/pm_runtime.h | 6 +++---
3 files changed, 15 insertions(+), 10 deletions(-)
diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c
index 1de1cd72b616..d5c3d7a6777e 100644
--- a/drivers/base/power/main.c
+++ b/drivers/base/power/main.c
@@ -1635,7 +1635,7 @@ static void device_suspend_late(struct device *dev, pm_message_t state, bool asy
* Disable runtime PM for the device without checking if there is a
* pending resume request for it.
*/
- __pm_runtime_disable(dev, false);
+ __pm_runtime_disable(dev, false, true);
if (dev->power.syscore)
goto Skip;
diff --git a/drivers/base/power/runtime.c b/drivers/base/power/runtime.c
index 1b11a3cd4acc..ff3fdfba2dc8 100644
--- a/drivers/base/power/runtime.c
+++ b/drivers/base/power/runtime.c
@@ -1421,11 +1421,16 @@ EXPORT_SYMBOL_GPL(__pm_runtime_set_status);
*
* Should be called under dev->power.lock with interrupts disabled.
*/
-static void __pm_runtime_barrier(struct device *dev)
+static void __pm_runtime_barrier(struct device *dev, bool frozen)
{
pm_runtime_deactivate_timer(dev);
- if (dev->power.request_pending) {
+ /*
+ * If the PM workqueue has already been frozen, the following
+ * operations are unnecessary. This allows any pending work to
+ * continue execution once the PM workqueue is unfrozen.
+ */
+ if (!frozen && dev->power.request_pending) {
dev->power.request = RPM_REQ_NONE;
spin_unlock_irq(&dev->power.lock);
@@ -1485,7 +1490,7 @@ int pm_runtime_barrier(struct device *dev)
retval = 1;
}
- __pm_runtime_barrier(dev);
+ __pm_runtime_barrier(dev, false);
spin_unlock_irq(&dev->power.lock);
pm_runtime_put_noidle(dev);
@@ -1519,7 +1524,7 @@ void pm_runtime_unblock(struct device *dev)
spin_unlock_irq(&dev->power.lock);
}
-void __pm_runtime_disable(struct device *dev, bool check_resume)
+void __pm_runtime_disable(struct device *dev, bool check_resume, bool frozen)
{
spin_lock_irq(&dev->power.lock);
@@ -1550,7 +1555,7 @@ void __pm_runtime_disable(struct device *dev, bool check_resume)
update_pm_runtime_accounting(dev);
if (!dev->power.disable_depth++) {
- __pm_runtime_barrier(dev);
+ __pm_runtime_barrier(dev, frozen);
dev->power.last_status = dev->power.runtime_status;
}
@@ -1893,7 +1898,7 @@ void pm_runtime_reinit(struct device *dev)
*/
void pm_runtime_remove(struct device *dev)
{
- __pm_runtime_disable(dev, false);
+ __pm_runtime_disable(dev, false, false);
pm_runtime_reinit(dev);
}
diff --git a/include/linux/pm_runtime.h b/include/linux/pm_runtime.h
index 0b436e15f4cd..102060a9ebc7 100644
--- a/include/linux/pm_runtime.h
+++ b/include/linux/pm_runtime.h
@@ -80,7 +80,7 @@ extern int pm_runtime_barrier(struct device *dev);
extern bool pm_runtime_block_if_disabled(struct device *dev);
extern void pm_runtime_unblock(struct device *dev);
extern void pm_runtime_enable(struct device *dev);
-extern void __pm_runtime_disable(struct device *dev, bool check_resume);
+extern void __pm_runtime_disable(struct device *dev, bool check_resume, bool frozen);
extern void pm_runtime_allow(struct device *dev);
extern void pm_runtime_forbid(struct device *dev);
extern void pm_runtime_no_callbacks(struct device *dev);
@@ -288,7 +288,7 @@ static inline int pm_runtime_barrier(struct device *dev) { return 0; }
static inline bool pm_runtime_block_if_disabled(struct device *dev) { return true; }
static inline void pm_runtime_unblock(struct device *dev) {}
static inline void pm_runtime_enable(struct device *dev) {}
-static inline void __pm_runtime_disable(struct device *dev, bool c) {}
+static inline void __pm_runtime_disable(struct device *dev, bool c, bool f) {}
static inline bool pm_runtime_blocked(struct device *dev) { return true; }
static inline void pm_runtime_allow(struct device *dev) {}
static inline void pm_runtime_forbid(struct device *dev) {}
@@ -775,7 +775,7 @@ static inline int pm_runtime_set_suspended(struct device *dev)
*/
static inline void pm_runtime_disable(struct device *dev)
{
- __pm_runtime_disable(dev, true);
+ __pm_runtime_disable(dev, true, false);
}
/**
--
Powered by blists - more mailing lists