From patchwork Thu Mar 13 20:26:53 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Rafael J. Wysocki" X-Patchwork-Id: 873729 Received: from cloudserver094114.home.pl (cloudserver094114.home.pl [79.96.170.134]) (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 65B1E1F4629; Thu, 13 Mar 2025 20:35:30 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=79.96.170.134 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1741898134; cv=none; b=aDvQcspjsUkEWF1CLFS1MogqZkbOZmO3o9XoC7wZN1qTIlCFE9qsnibOWjBXsEP+2MvzT4TzG4xOXtxYygGjyqeKZhoHrzX13iLgCipwg8XhMb4rKDm/zAiHbamjGcV1XA91zMK3tzCZABdTQ8roH/zEb8SR4rjrIaRgorN3+Jk= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1741898134; c=relaxed/simple; bh=07RaQwLG1ZQ6iEnzoTxQiO05Gm8Rsfct4gDJ9ijKynk=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=lQgmT0mrgWsMY5vNy958c2i4xDd2Vvo4WqGReGia1w4HOsBJshaoPjZecsNK6ih5xAb3ldfjujA51iqUvx8oEE6uD4gHrQaDcj4a7e7oS3QAX19mH9CmAm/BPkN7pu3R+PsnKXy24Ct8OnGqtukmuZ8X5QApI+nEetb9OdwR1WU= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=rjwysocki.net; spf=pass smtp.mailfrom=rjwysocki.net; dkim=pass (2048-bit key) header.d=rjwysocki.net header.i=@rjwysocki.net header.b=dHcKMIjJ; arc=none smtp.client-ip=79.96.170.134 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=rjwysocki.net Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=rjwysocki.net Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=rjwysocki.net header.i=@rjwysocki.net header.b="dHcKMIjJ" Received: from localhost (127.0.0.1) (HELO v370.home.net.pl) by /usr/run/smtp (/usr/run/postfix/private/idea_relay_lmtp) via UNIX with SMTP (IdeaSmtpServer 6.3.1) id 4e3b9c4d6bbf6f51; Thu, 13 Mar 2025 21:35:29 +0100 Received: from kreacher.localnet (unknown [195.136.19.94]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by cloudserver094114.home.pl (Postfix) with ESMTPSA id CB0528E4517; Thu, 13 Mar 2025 21:35:28 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=rjwysocki.net; s=dkim; t=1741898129; bh=07RaQwLG1ZQ6iEnzoTxQiO05Gm8Rsfct4gDJ9ijKynk=; h=From:Subject:Date; b=dHcKMIjJH+m6f8Fvu0PmcfDLp8rEq2GL4wx0H0Z0jjY9CmxRLkNCJNeM9QH6mzZu1 4H8tVzyQsuzIZ0KPbimyRORIUtk97YiImnKP8i79uO/wLKfwtZ8tFSiCOa9zAoAYvU 0g2a1/dXHTBZkU+U6J7TlPYg3afB1Gr3H4gM3OEy00Kw2feo8QWKWDNV1kuehAUubb gzbtOU9vRUlNswzm+HZ3/mwaiq7Kw90JdXv7hAwXYV4vYF2K2DwoeB6AefPc729LH2 6cW6VJZvVgM7vc5l9g+MN0wc88DJYuy5fBHg79pJ2Ld4uL5LDK2WDrt1iAkvU0qBNL To8PLmLpB4xwA== From: "Rafael J. Wysocki" To: Linux PM Cc: LKML , Alan Stern , Ulf Hansson , Johan Hovold , Manivannan Sadhasivam , Jon Hunter , Saravana Kannan Subject: [PATCH v2 1/3] PM: sleep: Resume children after resuming the parent Date: Thu, 13 Mar 2025 21:26:53 +0100 Message-ID: <3346996.44csPzL39Z@rjwysocki.net> In-Reply-To: <1915694.tdWV9SEqCh@rjwysocki.net> References: <1915694.tdWV9SEqCh@rjwysocki.net> Precedence: bulk X-Mailing-List: linux-pm@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-CLIENT-IP: 195.136.19.94 X-CLIENT-HOSTNAME: 195.136.19.94 X-VADE-SPAMSTATE: clean X-VADE-SPAMCAUSE: gggruggvucftvghtrhhoucdtuddrgeefvddrtddtgdduvdekledvucetufdoteggodetrfdotffvucfrrhhofhhilhgvmecujffqoffgrffnpdggtffipffknecuuegrihhlohhuthemucduhedtnecusecvtfgvtghiphhivghnthhsucdlqddutddtmdenucfjughrpefhvfevufffkfgjfhgggfgtsehtufertddttdejnecuhfhrohhmpedftfgrfhgrvghlucflrdcuhgihshhotghkihdfuceorhhjfiesrhhjfiihshhotghkihdrnhgvtheqnecuggftrfgrthhtvghrnhepfeduudeutdeugfelffduieegiedtueefledvjeegffdttefhhffhtefhleejgfetnecuffhomhgrihhnpehkvghrnhgvlhdrohhrghenucfkphepudelhedrudefiedrudelrdelgeenucevlhhushhtvghrufhiiigvpedtnecurfgrrhgrmhepihhnvghtpeduleehrddufeeirdduledrleegpdhhvghlohepkhhrvggrtghhvghrrdhlohgtrghlnhgvthdpmhgrihhlfhhrohhmpehrjhifsehrjhifhihsohgtkhhirdhnvghtpdhnsggprhgtphhtthhopeekpdhrtghpthhtoheplhhinhhugidqphhmsehvghgvrhdrkhgvrhhnvghlrdhorhhgpdhrtghpthhtoheplhhinhhugidqkhgvrhhnvghlsehvghgvrhdrkhgvrhhnvghlrdhorhhgpdhrtghpthhtohepshhtvghrnhesrhhofihlrghnugdrhhgrrhhvrghrugdrvgguuhdprhgtphhtthhopehulhhfrdhhrghnshhsohhnsehlihhnrghrohdrohhrghdprhgtphhtthhopeh X-DCC--Metrics: v370.home.net.pl 1024; Body=8 Fuz1=8 Fuz2=8 From: Rafael J. Wysocki According to [1], the handling of device suspend and resume, and particularly the latter, involves unnecessary overhead related to starting new async work items for devices that cannot make progress right away because they have to wait for other devices. To reduce this problem in the resume path, use the observation that starting the async resume of the children of a device after resuming the parent is likely to produce less scheduling and memory management noise than starting it upfront while at the same time it should not increase the resume duration substantially. Accordingly, modify the code to start the async resume of the device's children when the processing of the parent has been completed in each stage of device resume and only start async resume upfront for devices without parents. Also make it check if a given device can be resumed asynchronously before starting the synchronous resume of it in case it will have to wait for another that is already resuming asynchronously. In addition to making the async resume of devices more friendly to systems with relatively less computing resources, this change is also preliminary for analogous changes in the suspend path. On the systems where it has been tested, this change by itself does not affect the overall system resume duration in a significant way. Link: https://lore.kernel.org/linux-pm/20241114220921.2529905-1-saravanak@google.com/ [1] Suggested-by: Saravana Kannan Signed-off-by: Rafael J. Wysocki --- v1 -> v2: Use a separate lock for power.work_in_progress protection which should reduce lock contention on dpm_list_mtx. --- drivers/base/power/main.c | 80 +++++++++++++++++++++++++++++++++++----------- 1 file changed, 61 insertions(+), 19 deletions(-) --- a/drivers/base/power/main.c +++ b/drivers/base/power/main.c @@ -63,6 +63,7 @@ static DEFINE_MUTEX(dpm_list_mtx); static pm_message_t pm_transition; +static DEFINE_MUTEX(async_wip_mtx); static int async_error; static const char *pm_verb(int event) @@ -597,8 +598,11 @@ && !pm_trace_is_enabled(); } -static bool dpm_async_fn(struct device *dev, async_func_t func) +static bool __dpm_async(struct device *dev, async_func_t func) { + if (dev->power.work_in_progress) + return true; + if (!is_async(dev)) return false; @@ -611,14 +615,37 @@ put_device(dev); + return false; +} + +static bool dpm_async_fn(struct device *dev, async_func_t func) +{ + guard(mutex)(&async_wip_mtx); + + return __dpm_async(dev, func); +} + +static int dpm_async_with_cleanup(struct device *dev, void *fn) +{ + guard(mutex)(&async_wip_mtx); + + if (!__dpm_async(dev, fn)) + dev->power.work_in_progress = false; + + return 0; +} + +static void dpm_async_resume_children(struct device *dev, async_func_t func) +{ /* - * async_schedule_dev_nocall() above has returned false, so func() is - * not running and it is safe to update power.work_in_progress without - * extra synchronization. + * Start processing "async" children of the device unless it's been + * started already for them. + * + * This could have been done for the device's "async" consumers too, but + * they either need to wait for their parents or the processing has + * already started for them after their parents were processed. */ - dev->power.work_in_progress = false; - - return false; + device_for_each_child(dev, func, dpm_async_with_cleanup); } static void dpm_clear_async_state(struct device *dev) @@ -627,6 +654,8 @@ dev->power.work_in_progress = false; } +static void async_resume_noirq(void *data, async_cookie_t cookie); + /** * device_resume_noirq - Execute a "noirq resume" callback for given device. * @dev: Device to handle. @@ -710,6 +739,8 @@ dpm_save_failed_dev(dev_name(dev)); pm_dev_err(dev, state, async ? " async noirq" : " noirq", error); } + + dpm_async_resume_children(dev, async_resume_noirq); } static void async_resume_noirq(void *data, async_cookie_t cookie) @@ -733,19 +764,20 @@ mutex_lock(&dpm_list_mtx); /* - * Trigger the resume of "async" devices upfront so they don't have to - * wait for the "non-async" ones they don't depend on. + * Start processing "async" devices without parents upfront so they + * don't wait for the "sync" devices they don't depend on. */ list_for_each_entry(dev, &dpm_noirq_list, power.entry) { dpm_clear_async_state(dev); - dpm_async_fn(dev, async_resume_noirq); + if (!dev->parent) + dpm_async_with_cleanup(dev, async_resume_noirq); } while (!list_empty(&dpm_noirq_list)) { dev = to_device(dpm_noirq_list.next); list_move_tail(&dev->power.entry, &dpm_late_early_list); - if (!dev->power.work_in_progress) { + if (!dpm_async_fn(dev, async_resume_noirq)) { get_device(dev); mutex_unlock(&dpm_list_mtx); @@ -781,6 +813,8 @@ device_wakeup_disarm_wake_irqs(); } +static void async_resume_early(void *data, async_cookie_t cookie); + /** * device_resume_early - Execute an "early resume" callback for given device. * @dev: Device to handle. @@ -848,6 +882,8 @@ dpm_save_failed_dev(dev_name(dev)); pm_dev_err(dev, state, async ? " async early" : " early", error); } + + dpm_async_resume_children(dev, async_resume_early); } static void async_resume_early(void *data, async_cookie_t cookie) @@ -875,19 +911,20 @@ mutex_lock(&dpm_list_mtx); /* - * Trigger the resume of "async" devices upfront so they don't have to - * wait for the "non-async" ones they don't depend on. + * Start processing "async" devices without parents upfront so they + * don't wait for the "sync" devices they don't depend on. */ list_for_each_entry(dev, &dpm_late_early_list, power.entry) { dpm_clear_async_state(dev); - dpm_async_fn(dev, async_resume_early); + if (!dev->parent) + dpm_async_with_cleanup(dev, async_resume_early); } while (!list_empty(&dpm_late_early_list)) { dev = to_device(dpm_late_early_list.next); list_move_tail(&dev->power.entry, &dpm_suspended_list); - if (!dev->power.work_in_progress) { + if (!dpm_async_fn(dev, async_resume_early)) { get_device(dev); mutex_unlock(&dpm_list_mtx); @@ -919,6 +956,8 @@ } EXPORT_SYMBOL_GPL(dpm_resume_start); +static void async_resume(void *data, async_cookie_t cookie); + /** * device_resume - Execute "resume" callbacks for given device. * @dev: Device to handle. @@ -1018,6 +1057,8 @@ dpm_save_failed_dev(dev_name(dev)); pm_dev_err(dev, state, async ? " async" : "", error); } + + dpm_async_resume_children(dev, async_resume); } static void async_resume(void *data, async_cookie_t cookie) @@ -1049,19 +1090,20 @@ mutex_lock(&dpm_list_mtx); /* - * Trigger the resume of "async" devices upfront so they don't have to - * wait for the "non-async" ones they don't depend on. + * Start processing "async" devices without parents upfront so they + * don't wait for the "sync" devices they don't depend on. */ list_for_each_entry(dev, &dpm_suspended_list, power.entry) { dpm_clear_async_state(dev); - dpm_async_fn(dev, async_resume); + if (!dev->parent) + dpm_async_with_cleanup(dev, async_resume); } while (!list_empty(&dpm_suspended_list)) { dev = to_device(dpm_suspended_list.next); list_move_tail(&dev->power.entry, &dpm_prepared_list); - if (!dev->power.work_in_progress) { + if (!dpm_async_fn(dev, async_resume)) { get_device(dev); mutex_unlock(&dpm_list_mtx); From patchwork Thu Mar 13 20:34:33 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Rafael J. Wysocki" X-Patchwork-Id: 873730 Received: from cloudserver094114.home.pl (cloudserver094114.home.pl [79.96.170.134]) (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 844571F37C5; Thu, 13 Mar 2025 20:35:30 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=79.96.170.134 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1741898132; cv=none; b=Ze1mjNjfHQ54Wv0ne79T+keAGFsrwOEjRggGt3R3s0X4/oMWwpoPfGFup/tf06qF/rCgI1DJxiIauto8VqmMbYZinPsiNTHEL9L4sINKRJzfwsIZnwaV4Ynai8r7OuxsCrQJtRsfoRxy1UpfPgPGM3bJ3ur4R3bo6tK4AnHPwRQ= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1741898132; c=relaxed/simple; bh=Bu0PktBMHBhQFAY01Z2FQXI2KJUpuNghVeOrGayJTC8=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=UZzJg4NrVNpc9uScg07eGcy5fEIThr3R1XJrYUVwDPJuY0A8Mex8Kvf7OKk4zYyOET6TAlxHt22txU797cpwQ1ASAHJWiXiG/2MnSjoo8VBoiZoXEv0iwqERmu4m1wJ610+RG/veBrj6tLIJfX4mC6cuOgFd8Puop6lq0CXX/Es= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=rjwysocki.net; spf=pass smtp.mailfrom=rjwysocki.net; dkim=pass (2048-bit key) header.d=rjwysocki.net header.i=@rjwysocki.net header.b=ORaCZlcw; arc=none smtp.client-ip=79.96.170.134 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=rjwysocki.net Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=rjwysocki.net Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=rjwysocki.net header.i=@rjwysocki.net header.b="ORaCZlcw" Received: from localhost (127.0.0.1) (HELO v370.home.net.pl) by /usr/run/smtp (/usr/run/postfix/private/idea_relay_lmtp) via UNIX with SMTP (IdeaSmtpServer 6.3.1) id 5c0ce5d0e197efee; Thu, 13 Mar 2025 21:35:28 +0100 Received: from kreacher.localnet (unknown [195.136.19.94]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by cloudserver094114.home.pl (Postfix) with ESMTPSA id D9D428E4517; Thu, 13 Mar 2025 21:35:27 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=rjwysocki.net; s=dkim; t=1741898128; bh=Bu0PktBMHBhQFAY01Z2FQXI2KJUpuNghVeOrGayJTC8=; h=From:Subject:Date; b=ORaCZlcwCKy3PNpT43l/Q7W5qIyX0jMtipCZz7nvJbKKsgcC+YqEvPV8fz/efLwg2 Jh/nn4EyaSnWQDmUPI5Ecyp2vk4omoE1ZdecemVT6J1FhvOjuZFPyOYEAYcSkMVrw+ YOAdPalGzpK7B2fbhpiJ1l/1ZRnT634BVpHQ6dFaQVxwuYV0VNKb926PVHa5fjR3Cs RrEDzp+OBoi7zEYxB4ulLJOfIWTRNXL44R9DDFr8vijxr+oddTXziJZ6XmlY2bBiM/ McyNUCcvVSwJ65PKj0CaRlTLWKC2efwWVCqvWVaD4O1dgTsirrxDU3brnveYMPumjr ZzHHUHSBM3pcQ== From: "Rafael J. Wysocki" To: Linux PM Cc: LKML , Alan Stern , Ulf Hansson , Johan Hovold , Manivannan Sadhasivam , Jon Hunter , Saravana Kannan Subject: [PATCH v2 2/3] PM: sleep: Suspend parents and suppliers after suspending subordinates Date: Thu, 13 Mar 2025 21:34:33 +0100 Message-ID: <3271724.5fSG56mABF@rjwysocki.net> In-Reply-To: <1915694.tdWV9SEqCh@rjwysocki.net> References: <1915694.tdWV9SEqCh@rjwysocki.net> Precedence: bulk X-Mailing-List: linux-pm@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-CLIENT-IP: 195.136.19.94 X-CLIENT-HOSTNAME: 195.136.19.94 X-VADE-SPAMSTATE: clean X-VADE-SPAMCAUSE: gggruggvucftvghtrhhoucdtuddrgeefvddrtddtgdduvdekleefucetufdoteggodetrfdotffvucfrrhhofhhilhgvmecujffqoffgrffnpdggtffipffknecuuegrihhlohhuthemucduhedtnecusecvtfgvtghiphhivghnthhsucdlqddutddtmdenucfjughrpefhvfevufffkfgjfhgggfgtsehtufertddttdejnecuhfhrohhmpedftfgrfhgrvghlucflrdcuhgihshhotghkihdfuceorhhjfiesrhhjfiihshhotghkihdrnhgvtheqnecuggftrfgrthhtvghrnhepvdffueeitdfgvddtudegueejtdffteetgeefkeffvdeftddttdeuhfegfedvjefhnecukfhppeduleehrddufeeirdduledrleegnecuvehluhhsthgvrhfuihiivgeptdenucfrrghrrghmpehinhgvthepudelhedrudefiedrudelrdelgedphhgvlhhopehkrhgvrggthhgvrhdrlhhotggrlhhnvghtpdhmrghilhhfrhhomheprhhjfiesrhhjfiihshhotghkihdrnhgvthdpnhgspghrtghpthhtohepkedprhgtphhtthhopehlihhnuhigqdhpmhesvhhgvghrrdhkvghrnhgvlhdrohhrghdprhgtphhtthhopehlihhnuhigqdhkvghrnhgvlhesvhhgvghrrdhkvghrnhgvlhdrohhrghdprhgtphhtthhopehsthgvrhhnsehrohiflhgrnhgurdhhrghrvhgrrhgurdgvughupdhrtghpthhtohepuhhlfhdrhhgrnhhsshhonheslhhinhgrrhhordhorhhgpdhrtghpthhtohepjhhohhgrnheskhgvrhhnvghlrdhorhhgpdhrtgh X-DCC--Metrics: v370.home.net.pl 1024; Body=8 Fuz1=8 Fuz2=8 From: Rafael J. Wysocki In analogy with the previous change affecting the resume path, make device_suspend() start the async suspend of the device's parent and suppliers after the device itself has been processed and make dpm_suspend() start processing "async" leaf devices (that is, devices without children or consumers) upfront because they don't need to wait for any other devices. On the Dell XPS13 9360 in my office, this change reduces the total duration of device suspend by approximately 100 ms (over 20%). Signed-off-by: Rafael J. Wysocki Suggested-by: Saravana Kannan --- v1 -> v2: * Adjust for the changes in patch [1/3]. * Fix walking suppliers in dpm_async_suspend_superior(). * Use device links read locking in dpm_async_suspend_superior() (Saravana). * Move all devices to the target list even if there are errors in dpm_suspend() so they are properly resumed during rollback (Saravana). --- drivers/base/power/main.c | 78 ++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 72 insertions(+), 6 deletions(-) --- a/drivers/base/power/main.c +++ b/drivers/base/power/main.c @@ -1231,6 +1231,50 @@ /*------------------------- Suspend routines -------------------------*/ +static bool dpm_leaf_device(struct device *dev) +{ + struct device *child; + + lockdep_assert_held(&dpm_list_mtx); + + child = device_find_any_child(dev); + if (child) { + put_device(child); + + return false; + } + + /* + * Since this function is required to run under dpm_list_mtx, the + * list_empty() below will only return true if the device's list of + * consumers is actually empty before calling it. + */ + return list_empty(&dev->links.consumers); +} + +static void dpm_async_suspend_superior(struct device *dev, async_func_t func) +{ + struct device_link *link; + int idx; + + mutex_lock(&dpm_list_mtx); + + /* Start processing the device's parent if it is "async". */ + if (dev->parent) + dpm_async_with_cleanup(dev->parent, func); + + mutex_unlock(&dpm_list_mtx); + + idx = device_links_read_lock(); + + /* Start processing the device's "async" suppliers. */ + list_for_each_entry_rcu(link, &dev->links.suppliers, c_node) + if (READ_ONCE(link->status) != DL_STATE_DORMANT) + dpm_async_with_cleanup(link->supplier, func); + + device_links_read_unlock(idx); +} + /** * resume_event - Return a "resume" message for given "suspend" sleep state. * @sleep_state: PM message representing a sleep state. @@ -1656,6 +1700,8 @@ device_links_read_unlock(idx); } +static void async_suspend(void *data, async_cookie_t cookie); + /** * device_suspend - Execute "suspend" callbacks for given device. * @dev: Device to handle. @@ -1785,7 +1831,13 @@ complete_all(&dev->power.completion); TRACE_SUSPEND(error); - return error; + + if (error || async_error) + return error; + + dpm_async_suspend_superior(dev, async_suspend); + + return 0; } static void async_suspend(void *data, async_cookie_t cookie) @@ -1803,6 +1855,7 @@ int dpm_suspend(pm_message_t state) { ktime_t starttime = ktime_get(); + struct device *dev; int error = 0; trace_suspend_resume(TPS("dpm_suspend"), state.event, true); @@ -1816,12 +1869,28 @@ mutex_lock(&dpm_list_mtx); + /* + * Start processing "async" leaf devices upfront because they don't need + * to wait. + */ + list_for_each_entry_reverse(dev, &dpm_prepared_list, power.entry) { + dpm_clear_async_state(dev); + if (dpm_leaf_device(dev)) + dpm_async_with_cleanup(dev, async_suspend); + } + while (!list_empty(&dpm_prepared_list)) { - struct device *dev = to_device(dpm_prepared_list.prev); + dev = to_device(dpm_prepared_list.prev); list_move(&dev->power.entry, &dpm_suspended_list); - dpm_clear_async_state(dev); + /* + * Move all devices to the target list to resume them properly + * on errors. + */ + if (error || async_error) + continue; + if (dpm_async_fn(dev, async_suspend)) continue; @@ -1834,9 +1903,6 @@ put_device(dev); mutex_lock(&dpm_list_mtx); - - if (error || async_error) - break; } mutex_unlock(&dpm_list_mtx); From patchwork Thu Mar 13 20:35:17 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Rafael J. Wysocki" X-Patchwork-Id: 873334 Received: from cloudserver094114.home.pl (cloudserver094114.home.pl [79.96.170.134]) (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 A595B1F1300; Thu, 13 Mar 2025 20:35:29 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=79.96.170.134 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1741898132; cv=none; b=NwKYsotXmJLzb3j4ugMI8ZoqwLRXGYj699M0rAZbKm9UM2eUQ3qxMB8lQ8e7nNiOgKwLbJSq0pl/vIdm5mkuf3ScVFR7wlTkA8FLg1MsJEjDf4TNPDZ3KbSzNqv9L7naS2geOFabNMrSEGdSPcAxSAGJ/gwpZVvZd1t/mpyqD10= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1741898132; c=relaxed/simple; bh=hcJz+Lim0wyn5nnm5SXK6SMc50j1KpHdWKRcvXbEl2w=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=UejxSMoFu2oVBR3VZIsl0bcYKTxUXKWev0dy1KyWadE/OHJpprujMVN6LLdP7o3lPk+sGDe5kYvNdMGQ8VE6LFH0TQMv5W5wRDhJPyKhtpeF0kVzjSCNv4AK4n/TwZAZOZjGjxqmGMQOPLfgWRRLFqE9Mv1MqXhT7ZG4QGgXQ3c= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=rjwysocki.net; spf=pass smtp.mailfrom=rjwysocki.net; dkim=pass (2048-bit key) header.d=rjwysocki.net header.i=@rjwysocki.net header.b=d/4BYgPb; arc=none smtp.client-ip=79.96.170.134 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=rjwysocki.net Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=rjwysocki.net Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=rjwysocki.net header.i=@rjwysocki.net header.b="d/4BYgPb" Received: from localhost (127.0.0.1) (HELO v370.home.net.pl) by /usr/run/smtp (/usr/run/postfix/private/idea_relay_lmtp) via UNIX with SMTP (IdeaSmtpServer 6.3.1) id e71e64c370882949; Thu, 13 Mar 2025 21:35:27 +0100 Received: from kreacher.localnet (unknown [195.136.19.94]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by cloudserver094114.home.pl (Postfix) with ESMTPSA id C93008E4517; Thu, 13 Mar 2025 21:35:26 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=rjwysocki.net; s=dkim; t=1741898127; bh=hcJz+Lim0wyn5nnm5SXK6SMc50j1KpHdWKRcvXbEl2w=; h=From:Subject:Date; b=d/4BYgPbn/98WUqif9KVcJM+Oopt0MHJEEZ4aDJ012CUwpDrHq44IZN8Ur7QkwvgY BwMqS7Z78Crtqw+0oylU9L8gWtfh11YlEUucMStUIlzG33SclHsHOdJrZfV3n/tImx sqbe9KRjU4CoorkhV8X+tusnqnfE4EO3fEjBg52geDVmVjTKtHcn3cPLZAvPwevfIS ijDVjhV6F6tkQGTuXfIGK4fFrFotFESxGDN4yDLL/MPDAOxQN9kTmsdPSIfv/HXVgb 8FrSWbwzyqrkvZZXCGM3YAddvWsVl0CvMRgWbhpc4BFfLF3jUY8KetOAc7mShcq8xD 3J4NkLPYFMSyQ== From: "Rafael J. Wysocki" To: Linux PM Cc: LKML , Alan Stern , Ulf Hansson , Johan Hovold , Manivannan Sadhasivam , Jon Hunter , Saravana Kannan Subject: [PATCH v2 3/3] PM: sleep: Make suspend of devices more asynchronous Date: Thu, 13 Mar 2025 21:35:17 +0100 Message-ID: <6030952.MhkbZ0Pkbq@rjwysocki.net> In-Reply-To: <1915694.tdWV9SEqCh@rjwysocki.net> References: <1915694.tdWV9SEqCh@rjwysocki.net> Precedence: bulk X-Mailing-List: linux-pm@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-CLIENT-IP: 195.136.19.94 X-CLIENT-HOSTNAME: 195.136.19.94 X-VADE-SPAMSTATE: clean X-VADE-SPAMCAUSE: gggruggvucftvghtrhhoucdtuddrgeefvddrtddtgdduvdekledvucetufdoteggodetrfdotffvucfrrhhofhhilhgvmecujffqoffgrffnpdggtffipffknecuuegrihhlohhuthemucduhedtnecusecvtfgvtghiphhivghnthhsucdlqddutddtmdenucfjughrpefhvfevufffkfgjfhgggfgtsehtufertddttdejnecuhfhrohhmpedftfgrfhgrvghlucflrdcuhgihshhotghkihdfuceorhhjfiesrhhjfiihshhotghkihdrnhgvtheqnecuggftrfgrthhtvghrnhepvdffueeitdfgvddtudegueejtdffteetgeefkeffvdeftddttdeuhfegfedvjefhnecukfhppeduleehrddufeeirdduledrleegnecuvehluhhsthgvrhfuihiivgeptdenucfrrghrrghmpehinhgvthepudelhedrudefiedrudelrdelgedphhgvlhhopehkrhgvrggthhgvrhdrlhhotggrlhhnvghtpdhmrghilhhfrhhomheprhhjfiesrhhjfiihshhotghkihdrnhgvthdpnhgspghrtghpthhtohepkedprhgtphhtthhopehlihhnuhigqdhpmhesvhhgvghrrdhkvghrnhgvlhdrohhrghdprhgtphhtthhopehlihhnuhigqdhkvghrnhgvlhesvhhgvghrrdhkvghrnhgvlhdrohhrghdprhgtphhtthhopehsthgvrhhnsehrohiflhgrnhgurdhhrghrvhgrrhgurdgvughupdhrtghpthhtohepuhhlfhdrhhgrnhhsshhonheslhhinhgrrhhordhorhhgpdhrtghpthhtohepjhhohhgrnheskhgvrhhnvghlrdhorhhgpdhrtgh X-DCC--Metrics: v370.home.net.pl 1024; Body=8 Fuz1=8 Fuz2=8 From: Rafael J. Wysocki In analogy with previous changes, make device_suspend_late() and device_suspend_noirq() start the async suspend of the device's parent and suppliers after the device itself has been processed and make dpm_suspend_late() and dpm_noirq_suspend_devices() start processing "async" leaf devices (that is, devices without children or consumers) upfront because they don't need to wait for any other devices. This change reduces the total duration of device suspend on some systems where it has been tested measurably, but not significantly. Signed-off-by: Rafael J. Wysocki Suggested-by: Saravana Kannan --- v1 -> v2: * Adjust for the changes in patches [1-2/3]. * Move all devices to the target lists even if there are errors in dpm_suspend_late() and dpm_noirq_suspend_devices() so they are properly resumed during rollback (Saravana). --- drivers/base/power/main.c | 68 +++++++++++++++++++++++++++++++++++++--------- 1 file changed, 56 insertions(+), 12 deletions(-) --- a/drivers/base/power/main.c +++ b/drivers/base/power/main.c @@ -1312,6 +1312,8 @@ device_links_read_unlock(idx); } +static void async_suspend_noirq(void *data, async_cookie_t cookie); + /** * device_suspend_noirq - Execute a "noirq suspend" callback for given device. * @dev: Device to handle. @@ -1390,7 +1392,13 @@ Complete: complete_all(&dev->power.completion); TRACE_SUSPEND(error); - return error; + + if (error || async_error) + return error; + + dpm_async_suspend_superior(dev, async_suspend_noirq); + + return 0; } static void async_suspend_noirq(void *data, async_cookie_t cookie) @@ -1404,6 +1412,7 @@ static int dpm_noirq_suspend_devices(pm_message_t state) { ktime_t starttime = ktime_get(); + struct device *dev; int error = 0; trace_suspend_resume(TPS("dpm_suspend_noirq"), state.event, true); @@ -1413,12 +1422,28 @@ mutex_lock(&dpm_list_mtx); + /* + * Start processing "async" leaf devices upfront because they don't need + * to wait. + */ + list_for_each_entry_reverse(dev, &dpm_late_early_list, power.entry) { + dpm_clear_async_state(dev); + if (dpm_leaf_device(dev)) + dpm_async_with_cleanup(dev, async_suspend_noirq); + } + while (!list_empty(&dpm_late_early_list)) { - struct device *dev = to_device(dpm_late_early_list.prev); + dev = to_device(dpm_late_early_list.prev); list_move(&dev->power.entry, &dpm_noirq_list); - dpm_clear_async_state(dev); + /* + * Move all devices to the target list to resume them properly + * on errors. + */ + if (error || async_error) + break; + if (dpm_async_fn(dev, async_suspend_noirq)) continue; @@ -1431,9 +1456,6 @@ put_device(dev); mutex_lock(&dpm_list_mtx); - - if (error || async_error) - break; } mutex_unlock(&dpm_list_mtx); @@ -1486,6 +1508,8 @@ spin_unlock_irq(&parent->power.lock); } +static void async_suspend_late(void *data, async_cookie_t cookie); + /** * device_suspend_late - Execute a "late suspend" callback for given device. * @dev: Device to handle. @@ -1562,7 +1586,13 @@ Complete: TRACE_SUSPEND(error); complete_all(&dev->power.completion); - return error; + + if (error || async_error) + return error; + + dpm_async_suspend_superior(dev, async_suspend_late); + + return 0; } static void async_suspend_late(void *data, async_cookie_t cookie) @@ -1580,6 +1610,7 @@ int dpm_suspend_late(pm_message_t state) { ktime_t starttime = ktime_get(); + struct device *dev; int error = 0; trace_suspend_resume(TPS("dpm_suspend_late"), state.event, true); @@ -1591,12 +1622,28 @@ mutex_lock(&dpm_list_mtx); + /* + * Start processing "async" leaf devices upfront because they don't need + * to wait. + */ + list_for_each_entry_reverse(dev, &dpm_suspended_list, power.entry) { + dpm_clear_async_state(dev); + if (dpm_leaf_device(dev)) + dpm_async_with_cleanup(dev, async_suspend_late); + } + while (!list_empty(&dpm_suspended_list)) { - struct device *dev = to_device(dpm_suspended_list.prev); + dev = to_device(dpm_suspended_list.prev); list_move(&dev->power.entry, &dpm_late_early_list); - dpm_clear_async_state(dev); + /* + * Move all devices to the target list to resume them properly + * on errors. + */ + if (error || async_error) + continue; + if (dpm_async_fn(dev, async_suspend_late)) continue; @@ -1609,9 +1656,6 @@ put_device(dev); mutex_lock(&dpm_list_mtx); - - if (error || async_error) - break; } mutex_unlock(&dpm_list_mtx);