mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
From: Paul Menzel <pmenzel@molgen.mpg.de>
To: "Rafael J. Wysocki" <rafael@kernel.org>, Len Brown <lenb@kernel.org>
Cc: Paul Menzel <pmenzel@molgen.mpg.de>,
	linux-acpi@vger.kernel.org, linux-kernel@vger.kernel.org
Subject: [PATCH] ACPI: battery: Refresh the battery asynchronously after system resume
Date: Sat, 14 Nov 2026 01:26:12 +0100	[thread overview]
Message-ID: <20261114002612.219894-2-pmenzel@molgen.mpg.de> (raw)

The PM_POST_SUSPEND notifier evaluates _BIX, _BTP and _BST synchronously,
and the notifier chain is the last step of the system resume: the write to
/sys/power/state, and so systemd-sleep and everything waiting for it, only
returns after it.

On a Dell XPS 13 9310 nearly all of the PM_POST_SUSPEND notifier chain is
battery_notify() (143 ms with the function_graph tracer):

    @ 143043.5 us |        } /* battery_notify [battery] */
    @ 143513.1 us |      } /* notifier_call_chain */
    @ 143536.0 us |  } /* pm_notifier_call_chain */

Queue the refresh on system_freezable_wq instead.

Time from "Restarting tasks: Done" (end of thaw_processes) to "PM: suspend
exit" for suspend-to-idle cycles of 8 s with sleepgraph, in ms, on Linux
7.4-rc1 with other resume changes, which do not run in this phase:

                  panel lit at suspend       panel off at suspend
    before        24.5  26.7  27.3  29.4     58.8  60.4  85.2
    after         10.7  10.8  15.9  17.7      5.7   8.3   9.3
    median        27.0 -> 13.4               60.4 -> 8.3

After the change, pm_notifier_call_chain(PM_POST_SUSPEND) takes
0.7-0.9 ms (function_graph, three cycles, hwrng_pm_notifier() being the
largest), so the rest of the phase is outside the notifier chain.  The
refresh still runs after each resume (kprobes, three cycles):
battery_notify() returns after 4-8 us, acpi_battery_pm_worker() starts
0.02-0.2 ms later and takes 17.8-18.6 ms, and it ends after rtcwake has
already returned.

Signed-off-by: Paul Menzel <pmenzel@molgen.mpg.de>
---

Notes:
    Assisted-by: claude-opus-5.5 (Claude Code), analysis and patch
    draft; I reviewed it.

 drivers/acpi/battery.c | 63 +++++++++++++++++++++++++-----------------
 1 file changed, 37 insertions(+), 26 deletions(-)

diff --git a/drivers/acpi/battery.c b/drivers/acpi/battery.c
index 6edea559a5bce..4e2a79d356ef9 100644
--- a/drivers/acpi/battery.c
+++ b/drivers/acpi/battery.c
@@ -105,6 +105,7 @@ struct acpi_battery {
 	struct kfifo acpi_notif_fifo;
 	struct delayed_work acpi_notif_dwork;
 	struct notifier_block pm_nb;
+	struct work_struct pm_work;
 	struct list_head list;
 	unsigned long flags;
 
@@ -1293,40 +1294,48 @@ static void acpi_battery_notify(acpi_handle handle, u32 event, void *data)
 	pr_err_ratelimited("too many battery notifications within %dms\n", NOTIF_MERGING_MS);
 }
 
-static int battery_notify(struct notifier_block *nb,
-			  unsigned long mode, void *_unused)
+static void acpi_battery_pm_worker(struct work_struct *work)
 {
-	struct acpi_battery *battery = container_of(nb, struct acpi_battery,
-						    pm_nb);
-
-	if (mode == PM_POST_SUSPEND || mode == PM_POST_HIBERNATION) {
-		guard(mutex)(&battery->update_lock);
-
-		if (!acpi_battery_present(battery))
-			return 0;
-
-		if (battery->bat) {
-			acpi_battery_refresh(battery);
-		} else {
-			int result;
+	struct acpi_battery *battery = container_of(work, struct acpi_battery,
+						    pm_work);
 
-			scoped_guard(mutex, &battery->property_lock) {
-				result = acpi_battery_get_info(battery);
-				if (result)
-					return result;
-			}
+	guard(mutex)(&battery->update_lock);
 
-			result = sysfs_add_battery(battery);
-			if (result)
-				return result;
-		}
+	if (!acpi_battery_present(battery))
+		return;
 
+	if (battery->bat) {
+		acpi_battery_refresh(battery);
+	} else {
 		scoped_guard(mutex, &battery->property_lock) {
-			acpi_battery_init_alarm(battery);
-			acpi_battery_get_state(battery);
+			if (acpi_battery_get_info(battery))
+				return;
 		}
+
+		if (sysfs_add_battery(battery))
+			return;
 	}
 
+	scoped_guard(mutex, &battery->property_lock) {
+		acpi_battery_init_alarm(battery);
+		acpi_battery_get_state(battery);
+	}
+}
+
+static int battery_notify(struct notifier_block *nb,
+			  unsigned long mode, void *_unused)
+{
+	struct acpi_battery *battery = container_of(nb, struct acpi_battery,
+						    pm_nb);
+
+	/*
+	 * Evaluating _BIX, _BTP and _BST takes tens of milliseconds on some
+	 * systems, and the PM notifier chain delays the end of the system
+	 * resume, so do it asynchronously.
+	 */
+	if (mode == PM_POST_SUSPEND || mode == PM_POST_HIBERNATION)
+		queue_work(system_freezable_wq, &battery->pm_work);
+
 	return 0;
 }
 
@@ -1516,6 +1525,7 @@ static int acpi_battery_probe(struct platform_device *pdev)
 	if (result)
 		return result;
 
+	INIT_WORK(&battery->pm_work, acpi_battery_pm_worker);
 	battery->pm_nb.notifier_call = battery_notify;
 	return register_pm_notifier(&battery->pm_nb);
 }
@@ -1525,6 +1535,7 @@ static void acpi_battery_remove(struct platform_device *pdev)
 	struct acpi_battery *battery = platform_get_drvdata(pdev);
 
 	unregister_pm_notifier(&battery->pm_nb);
+	cancel_work_sync(&battery->pm_work);
 }
 
 /* this is needed to learn about changes made in suspended state */
-- 
2.54.0


                 reply	other threads:[~2026-11-14  0:26 UTC|newest]

Thread overview: [no followups] expand[flat|nested]  mbox.gz  Atom feed

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20261114002612.219894-2-pmenzel@molgen.mpg.de \
    --to=pmenzel@molgen.mpg.de \
    --cc=lenb@kernel.org \
    --cc=linux-acpi@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=rafael@kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®