From nobody Thu Sep 19 19:31:52 2024 Received: from mailgw01.mediatek.com (unknown [60.244.123.138]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id C0F3D1C32FC; Mon, 2 Sep 2024 09:21:49 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=60.244.123.138 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1725268912; cv=none; b=moT0I7Col48IlGm4c7UcufDjys0ApASBeceV4ZVcJGRoH4sJXlnXnCzqKB89Ph88ElE1l7Lil4R7LH8rXtNf3ssFT1HlF5lVDDfyIQ2Ol9GlUWz/q5ytI2Y+7bonmiO7AiHvto6mfbRKhslSl0BCXkLe7rMBB3hiIIoROJfKyis= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1725268912; c=relaxed/simple; bh=9y4KPKveOubZLlZJiVMWJ82HEm4mQDc/4QnFr/Zi8dY=; h=From:To:CC:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=Y1q7UlTTiHxFPlvgReUm25piLqmBF+9t5y3sjlB9Huub8uq1wYtTUN0QfVNUqrQDgOtmWozKholA0JCSRXdMZJdJM5ztY8pYICOnzV3JamlQ0Z9dhgpn7jX/9w2SYHCvXakAkT/st6T6X+XxRBAUBsdfG2mq0H7X79Ze8SFCAeo= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=mediatek.com; spf=pass smtp.mailfrom=mediatek.com; dkim=pass (1024-bit key) header.d=mediatek.com header.i=@mediatek.com header.b=nLTTS1xH; arc=none smtp.client-ip=60.244.123.138 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=mediatek.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=mediatek.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=mediatek.com header.i=@mediatek.com header.b="nLTTS1xH" X-UUID: c3717d82690c11ef8593d301e5c8a9c0-20240902 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=mediatek.com; s=dk; h=Content-Type:MIME-Version:References:In-Reply-To:Message-ID:Date:Subject:CC:To:From; bh=0TyVDYav3Nzbmm84YEt4RiUopjs6QxdUyqIWaillQHM=; b=nLTTS1xHlJldfud33WnQxJLYYWnjVR+xszccl8WLp4psuV+PUxlKzo1IA/ftAe8ZGciXK4NZkz8Se473m9nOEwvG04BqC390UR0I4BiOCQDfL4fyK9WPvdQxEpqOkSyS85jphJhkKESQ6nfTct4qsQswBY+HOn4RPaYL8hfZ1fI=; X-CID-P-RULE: Release_Ham X-CID-O-INFO: VERSION:1.1.41,REQID:aa3d2d99-3f3c-4b18-86be-0eea9dc28c9e,IP:0,U RL:0,TC:0,Content:0,EDM:0,RT:0,SF:0,FILE:0,BULK:0,RULE:Release_Ham,ACTION: release,TS:0 X-CID-META: VersionHash:6dc6a47,CLOUDID:a1368ecf-7921-4900-88a1-3aef019a55ce,B ulkID:nil,BulkQuantity:0,Recheck:0,SF:102,TC:nil,Content:0,EDM:-3,IP:nil,U RL:11|1,File:nil,RT:nil,Bulk:nil,QS:nil,BEC:nil,COL:0,OSI:0,OSA:0,AV:0,LES :1,SPR:NO,DKR:0,DKP:0,BRR:0,BRE:0,ARC:0 X-CID-BVR: 0,NGT X-CID-BAS: 0,NGT,0,_ X-CID-FACTOR: TF_CID_SPAM_SNR,TF_CID_SPAM_ULN X-UUID: c3717d82690c11ef8593d301e5c8a9c0-20240902 Received: from mtkmbs09n2.mediatek.inc [(172.21.101.94)] by mailgw01.mediatek.com (envelope-from ) (Generic MTA with TLSv1.2 ECDHE-RSA-AES256-GCM-SHA384 256/256) with ESMTP id 832729975; Mon, 02 Sep 2024 17:21:41 +0800 Received: from mtkmbs11n1.mediatek.inc (172.21.101.185) by MTKMBS14N1.mediatek.inc (172.21.101.75) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.2.1118.26; Mon, 2 Sep 2024 17:21:42 +0800 Received: from mtksdccf07.mediatek.inc (172.21.84.99) by mtkmbs11n1.mediatek.inc (172.21.101.73) with Microsoft SMTP Server id 15.2.1118.26 via Frontend Transport; Mon, 2 Sep 2024 17:21:42 +0800 From: Yenchia Chen To: CC: "Rafael J. Wysocki" , Yenchia Chen , "Rafael J. Wysocki" , "Len Brown" , Pavel Machek , Greg Kroah-Hartman , Matthias Brugger , , , , Subject: [PATCH 6.1 1/1] PM: sleep: Restore asynchronous device resume optimization Date: Mon, 2 Sep 2024 17:21:20 +0800 Message-ID: <20240902092121.16369-2-yenchia.chen@mediatek.com> X-Mailer: git-send-email 2.18.0 In-Reply-To: <20240902092121.16369-1-yenchia.chen@mediatek.com> References: <20240902092121.16369-1-yenchia.chen@mediatek.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-TM-AS-Product-Ver: SMEX-14.0.0.3152-9.1.1006-23728.005 X-TM-AS-Result: No-10--5.609000-8.000000 X-TMASE-MatchedRID: BBt3isQeX+AhSBTdgDTdzWivjLE8DPtZFAr+wPWe7jF3vUA6/Pi03D6w DkfdfiqcXr7NXg/pSgQefN6Epq6WAQtrOhDKumbSFYJUGv4DL3y1k3bRIdXVNGojx25puFlrKT3 PsaLyojR2Drq+F6jWouKOmN63egZIZW+a5JK8E6htzb3s8Aa1ZoEcpMn6x9cZCqIJhrrDy2/j6f SiVX5Avye7w8ymgFM4n88pz6ki3TziteLUiHQ2KjCIlN/eSPB9pQH4ogtVQP0ifM7JMNHW67Ed0 qNn8ZtHviELeOq3H9dv+GfOK6fpuGf/awmfCAYuqb4ybQC/JXM3GofkGVB3jpkd+ko3Vgxli18y 270V9usnMrhHFzIbpcTKrSvo7uRnHxPMjOKY7A8LbigRnpKlKTpcQTtiHDgW8uEWPj8FUvT1QcP yn7Jx71FwKxCoIs0l76R+Ne/F29gGYeMEsOGG3UMMprcbiest X-TM-AS-User-Approved-Sender: No X-TM-AS-User-Blocked-Sender: No X-TMASE-Result: 10--5.609000-8.000000 X-TMASE-Version: SMEX-14.0.0.3152-9.1.1006-23728.005 X-TM-SNTS-SMTP: EB139BE01F5C4CA15EB7AB3C3F70AE66EEA8A0798DB991896ED73109B1010E052000:8 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" From: "Rafael J. Wysocki" commit 3e999770ac1c7c31a70685dd5b88e89473509e9c upstream. Before commit 7839d0078e0d ("PM: sleep: Fix possible deadlocks in core system-wide PM code"), the resume of devices that were allowed to resume asynchronously was scheduled before starting the resume of the other devices, so the former did not have to wait for the latter unless functional dependencies were present. Commit 7839d0078e0d removed that optimization in order to address a correctness issue, but it can be restored with the help of a new device power management flag, so do that now. Signed-off-by: Rafael J. Wysocki Reviewed-by: Stanislaw Gruszka Signed-off-by: Yenchia Chen --- drivers/base/power/main.c | 117 +++++++++++++++++++++----------------- include/linux/pm.h | 1 + 2 files changed, 65 insertions(+), 53 deletions(-) diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c index 9c5a5f4dba5a..fadcd0379dc2 100644 --- a/drivers/base/power/main.c +++ b/drivers/base/power/main.c @@ -579,7 +579,7 @@ bool dev_pm_skip_resume(struct device *dev) } =20 /** - * __device_resume_noirq - Execute a "noirq resume" callback for given dev= ice. + * device_resume_noirq - Execute a "noirq resume" callback for given devic= e. * @dev: Device to handle. * @state: PM transition of the system being carried out. * @async: If true, the device is being resumed asynchronously. @@ -587,7 +587,7 @@ bool dev_pm_skip_resume(struct device *dev) * The driver of @dev will not receive interrupts while this function is b= eing * executed. */ -static void __device_resume_noirq(struct device *dev, pm_message_t state, = bool async) +static void device_resume_noirq(struct device *dev, pm_message_t state, bo= ol async) { pm_callback_t callback =3D NULL; const char *info =3D NULL; @@ -674,16 +674,22 @@ static bool dpm_async_fn(struct device *dev, async_fu= nc_t func) { reinit_completion(&dev->power.completion); =20 - if (!is_async(dev)) - return false; - - get_device(dev); + if (is_async(dev)) { + dev->power.async_in_progress =3D true; =20 - if (async_schedule_dev_nocall(func, dev)) - return true; + get_device(dev); =20 - put_device(dev); + if (async_schedule_dev_nocall(func, dev)) + return true; =20 + put_device(dev); + } + /* + * Because async_schedule_dev_nocall() above has returned false or it + * has not been called at all, func() is not running and it is safe to + * update the async_in_progress flag without extra synchronization. + */ + dev->power.async_in_progress =3D false; return false; } =20 @@ -691,18 +697,10 @@ static void async_resume_noirq(void *data, async_cook= ie_t cookie) { struct device *dev =3D data; =20 - __device_resume_noirq(dev, pm_transition, true); + device_resume_noirq(dev, pm_transition, true); put_device(dev); } =20 -static void device_resume_noirq(struct device *dev) -{ - if (dpm_async_fn(dev, async_resume_noirq)) - return; - - __device_resume_noirq(dev, pm_transition, false); -} - static void dpm_noirq_resume_devices(pm_message_t state) { struct device *dev; @@ -712,18 +710,28 @@ static void dpm_noirq_resume_devices(pm_message_t sta= te) mutex_lock(&dpm_list_mtx); pm_transition =3D state; =20 + /* + * Trigger the resume of "async" devices upfront so they don't have to + * wait for the "non-async" ones they don't depend on. + */ + list_for_each_entry(dev, &dpm_noirq_list, power.entry) + dpm_async_fn(dev, async_resume_noirq); + while (!list_empty(&dpm_noirq_list)) { dev =3D to_device(dpm_noirq_list.next); - get_device(dev); list_move_tail(&dev->power.entry, &dpm_late_early_list); =20 - mutex_unlock(&dpm_list_mtx); + if (!dev->power.async_in_progress) { + get_device(dev); =20 - device_resume_noirq(dev); + mutex_unlock(&dpm_list_mtx); =20 - put_device(dev); + device_resume_noirq(dev, state, false); =20 - mutex_lock(&dpm_list_mtx); + put_device(dev); + + mutex_lock(&dpm_list_mtx); + } } mutex_unlock(&dpm_list_mtx); async_synchronize_full(); @@ -747,14 +755,14 @@ void dpm_resume_noirq(pm_message_t state) } =20 /** - * __device_resume_early - Execute an "early resume" callback for given de= vice. + * device_resume_early - Execute an "early resume" callback for given devi= ce. * @dev: Device to handle. * @state: PM transition of the system being carried out. * @async: If true, the device is being resumed asynchronously. * * Runtime PM is disabled for @dev while this function is being executed. */ -static void __device_resume_early(struct device *dev, pm_message_t state, = bool async) +static void device_resume_early(struct device *dev, pm_message_t state, bo= ol async) { pm_callback_t callback =3D NULL; const char *info =3D NULL; @@ -820,18 +828,10 @@ static void async_resume_early(void *data, async_cook= ie_t cookie) { struct device *dev =3D data; =20 - __device_resume_early(dev, pm_transition, true); + device_resume_early(dev, pm_transition, true); put_device(dev); } =20 -static void device_resume_early(struct device *dev) -{ - if (dpm_async_fn(dev, async_resume_early)) - return; - - __device_resume_early(dev, pm_transition, false); -} - /** * dpm_resume_early - Execute "early resume" callbacks for all devices. * @state: PM transition of the system being carried out. @@ -845,18 +845,28 @@ void dpm_resume_early(pm_message_t state) mutex_lock(&dpm_list_mtx); pm_transition =3D state; =20 + /* + * Trigger the resume of "async" devices upfront so they don't have to + * wait for the "non-async" ones they don't depend on. + */ + list_for_each_entry(dev, &dpm_late_early_list, power.entry) + dpm_async_fn(dev, async_resume_early); + while (!list_empty(&dpm_late_early_list)) { dev =3D to_device(dpm_late_early_list.next); - get_device(dev); list_move_tail(&dev->power.entry, &dpm_suspended_list); =20 - mutex_unlock(&dpm_list_mtx); + if (!dev->power.async_in_progress) { + get_device(dev); =20 - device_resume_early(dev); + mutex_unlock(&dpm_list_mtx); =20 - put_device(dev); + device_resume_early(dev, state, false); =20 - mutex_lock(&dpm_list_mtx); + put_device(dev); + + mutex_lock(&dpm_list_mtx); + } } mutex_unlock(&dpm_list_mtx); async_synchronize_full(); @@ -876,12 +886,12 @@ void dpm_resume_start(pm_message_t state) EXPORT_SYMBOL_GPL(dpm_resume_start); =20 /** - * __device_resume - Execute "resume" callbacks for given device. + * device_resume - Execute "resume" callbacks for given device. * @dev: Device to handle. * @state: PM transition of the system being carried out. * @async: If true, the device is being resumed asynchronously. */ -static void __device_resume(struct device *dev, pm_message_t state, bool a= sync) +static void device_resume(struct device *dev, pm_message_t state, bool asy= nc) { pm_callback_t callback =3D NULL; const char *info =3D NULL; @@ -975,18 +985,10 @@ static void async_resume(void *data, async_cookie_t c= ookie) { struct device *dev =3D data; =20 - __device_resume(dev, pm_transition, true); + device_resume(dev, pm_transition, true); put_device(dev); } =20 -static void device_resume(struct device *dev) -{ - if (dpm_async_fn(dev, async_resume)) - return; - - __device_resume(dev, pm_transition, false); -} - /** * dpm_resume - Execute "resume" callbacks for non-sysdev devices. * @state: PM transition of the system being carried out. @@ -1006,16 +1008,25 @@ void dpm_resume(pm_message_t state) pm_transition =3D state; async_error =3D 0; =20 + /* + * Trigger the resume of "async" devices upfront so they don't have to + * wait for the "non-async" ones they don't depend on. + */ + list_for_each_entry(dev, &dpm_suspended_list, power.entry) + dpm_async_fn(dev, async_resume); + while (!list_empty(&dpm_suspended_list)) { dev =3D to_device(dpm_suspended_list.next); =20 get_device(dev); =20 - mutex_unlock(&dpm_list_mtx); + if (!dev->power.async_in_progress) { + mutex_unlock(&dpm_list_mtx); =20 - device_resume(dev); + device_resume(dev, state, false); =20 - mutex_lock(&dpm_list_mtx); + mutex_lock(&dpm_list_mtx); + } =20 if (!list_empty(&dev->power.entry)) list_move_tail(&dev->power.entry, &dpm_prepared_list); diff --git a/include/linux/pm.h b/include/linux/pm.h index 93cd34f00822..bfc441099d8e 100644 --- a/include/linux/pm.h +++ b/include/linux/pm.h @@ -653,6 +653,7 @@ struct dev_pm_info { bool wakeup_path:1; bool syscore:1; bool no_pm_callbacks:1; /* Owned by the PM core */ + bool async_in_progress:1; /* Owned by the PM core */ unsigned int must_resume:1; /* Owned by the PM core */ unsigned int may_skip_resume:1; /* Set by subsystems */ #else --=20 2.18.0