From nobody Sat Oct 4 21:05:27 2025 Received: from mail-pl1-f202.google.com (mail-pl1-f202.google.com [209.85.214.202]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id DF8031A9FBE for ; Tue, 12 Aug 2025 23:21:28 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.214.202 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755040890; cv=none; b=U/qPpowMbA9I5cWdyr9Rg3X/tpMW0Sg5pxYi0YqLXD21ktAap9CDW09TfTll8dVa0mK0V2t8wvykS1YGK1vNJEGmcpXqWY7ZIfS0BZthngxC49NhGrDlRNmfwDlGcOJ93ueVV1PHI8mGVObcA9Y1S8U6a4yTXs6rgSbDnYFp5+0= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755040890; c=relaxed/simple; bh=LOk7dl93AGvnlrN+hnrHWS3R9F8H5tutntHoaNuqDdQ=; h=Date:Mime-Version:Message-ID:Subject:From:To:Cc:Content-Type; b=gcE02f7ozUbAO1osaomesjEVJSownZ5VA3ody2gStQx0/PtMNict6PXcGGRjR1vGsbqhrzviVWvNYLlmsXBjPhG1ihDXJ6fnx3vNgW8K0u0OsoWfD24sZ3UOfXIoXk9mIQ0hBlU8tCg/k1s6QzPwpjn1K/bafjJLoc+GW8ZjN74= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--wusamuel.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=Gwg9jyiK; arc=none smtp.client-ip=209.85.214.202 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--wusamuel.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="Gwg9jyiK" Received: by mail-pl1-f202.google.com with SMTP id d9443c01a7336-24004ac2ecdso94359505ad.0 for ; Tue, 12 Aug 2025 16:21:28 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20230601; t=1755040888; x=1755645688; darn=vger.kernel.org; h=cc:to:from:subject:message-id:mime-version:date:from:to:cc:subject :date:message-id:reply-to; bh=bZpiz79XGOfcAFrU83e9u5Jtr5pnmDIUJC8xFkGk0EY=; b=Gwg9jyiKV2ZAnontM272UA9FC3LrvDPewTXQpEEfeNTBUuPeNwjCVYN+r7hsUdazFm NBr2pjVwhAz31sWyQP4Yx/362rL4D6G3HZEJafClmLDWtmHWuBJfKtzF18TPLt6CaWFH Wj6337eQYmkuRoddj9dKvqxlj2RU/TBVIFlPHgN1X05NP7u56MBRLXJ7xr3Nc2DMnWDv vcni77ETXRzQSedfVWiNrq3CnSj9g9/R39AkF0n3AQ9Wat7AflnelHpmdlfEuOT6+fwR KqgaQHkWga7bawzY//S9pFThGeQqjdoZ4uVvmV1dx7utAVQprao8T5agt1OgrAgZOgrq KRjQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1755040888; x=1755645688; h=cc:to:from:subject:message-id:mime-version:date:x-gm-message-state :from:to:cc:subject:date:message-id:reply-to; bh=bZpiz79XGOfcAFrU83e9u5Jtr5pnmDIUJC8xFkGk0EY=; b=ABrWZII01xOZ7hqtgeb25nI+IAM/XdcK4tc9AWYIvLnrWSmPRMEXpXqWDBsV5iFrN7 d7tkrR6zPKSYzTNzoZrKcurbbHfOdpQMPhfh+fh2tl0hBuAWheqMeRANX/0dVFO84n+7 ZHo5ZwwdLIBFzNyFZbxohVT+x9t1c9kpmKJg5Rie41OrEUmt7auebY4OMPVAH2HAAyO9 O2MwGCklmPHNzQVoC6/if9sqH200VGH8Gk0xE28MT7yQgTq5CNQP+iqBF54y5iY7NRKA CXtMUcuh1z0G8AOmqUdg+LImy9ZZofGjHjoUYNwM+CFa6dcUpofLDXhuNyXUFwD8U0uX xJAA== X-Forwarded-Encrypted: i=1; AJvYcCV6qJuaQ+/15wACXhWTbXSjhBQdh1bdFVQbMfY5O/tGJ4URrzikQovMiRnv/rS0sxKkKS3+iMaqqY3Gr/Q=@vger.kernel.org X-Gm-Message-State: AOJu0Yy9F0uYeDhOOqSrRVJ9ohWArnUpew/Li8+JnE5gJHt/5ljmnaEa UGBh8Bxsvrd/p0E9/F5OcDyrqrqvQtKnCfhyerLGuxhl4XhBEf1+dmmUGcEKHKsyPQ+2YFNTUPQ x2OflCDTzpz4uCg== X-Google-Smtp-Source: AGHT+IEpN2+KlAnjkUEee+7tyFmAYnD+YYk0SolpIJ6qM5vTpNsALavalXORFevn/twSGWrD+KCnKhgjS0cCdQ== X-Received: from plks17.prod.google.com ([2002:a17:903:2d1:b0:231:de34:f9f6]) (user=wusamuel job=prod-delivery.src-stubby-dispatcher) by 2002:a17:903:1a84:b0:240:1953:f9a with SMTP id d9443c01a7336-2430d0b3239mr16123015ad.2.1755040888203; Tue, 12 Aug 2025 16:21:28 -0700 (PDT) Date: Tue, 12 Aug 2025 16:21:23 -0700 Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 X-Mailer: git-send-email 2.51.0.rc0.215.g125493bb4a-goog Message-ID: <20250812232126.1814253-1-wusamuel@google.com> Subject: [PATCH v1] PM: Support aborting suspend during filesystem sync From: Samuel Wu To: "Rafael J. Wysocki" , Len Brown , Pavel Machek , Greg Kroah-Hartman , Danilo Krummrich , Viresh Kumar , Ingo Molnar , Peter Zijlstra , Juri Lelli , Vincent Guittot , Dietmar Eggemann , Steven Rostedt , Ben Segall , Mel Gorman , Valentin Schneider , Lukasz Luba Cc: Samuel Wu , Saravana Kannan , kernel-team@android.com, "Rafael J. Wysocki" , linux-pm@vger.kernel.org, linux-kernel@vger.kernel.org Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" At the start of suspend, filesystems will sync to save the current state of the device. However, the long tail of the filesystem sync can take upwards of 25 seconds. If during this filesystem sync there is some wakeup or abort signal, it will not be processed until the sync is complete; from a user's perspective, this looks like the device is unresponsive to any form of input. This patch adds functionality to handle a suspend abort signal when in the filesystem sync phase of suspend. This topic was first discussed by Saravana Kannan at LPC 2024 [1], where the general consensus was to allow filesystem sync on a parallel thread. [1]: https://lpc.events/event/18/contributions/1845/ Suggested-by: Saravana Kannan Signed-off-by: Samuel Wu --- drivers/base/power/wakeup.c | 8 ++++ include/linux/suspend.h | 3 ++ kernel/power/process.c | 1 - kernel/power/suspend.c | 80 ++++++++++++++++++++++++++++++++++++- 4 files changed, 90 insertions(+), 2 deletions(-) diff --git a/drivers/base/power/wakeup.c b/drivers/base/power/wakeup.c index d1283ff1080b..304368c3a55f 100644 --- a/drivers/base/power/wakeup.c +++ b/drivers/base/power/wakeup.c @@ -570,6 +570,13 @@ static void wakeup_source_activate(struct wakeup_sourc= e *ws) =20 /* Increment the counter of events in progress. */ cec =3D atomic_inc_return(&combined_event_count); + /* + * To maintain the same behavior as pm_wakeup_pending(), + * aborting suspend will only happen if events_check_enabled. Similarly, + * the abort during fs_sync needs the same check. + */ + if (events_check_enabled) + suspend_abort_fs_sync(); =20 trace_wakeup_source_activate(ws->name, cec); } @@ -899,6 +906,7 @@ EXPORT_SYMBOL_GPL(pm_wakeup_pending); void pm_system_wakeup(void) { atomic_inc(&pm_abort_suspend); + suspend_abort_fs_sync(); s2idle_wake(); } EXPORT_SYMBOL_GPL(pm_system_wakeup); diff --git a/include/linux/suspend.h b/include/linux/suspend.h index 317ae31e89b3..21b1ea275c79 100644 --- a/include/linux/suspend.h +++ b/include/linux/suspend.h @@ -276,6 +276,8 @@ extern void arch_suspend_enable_irqs(void); =20 extern int pm_suspend(suspend_state_t state); extern bool sync_on_suspend_enabled; + +extern void suspend_abort_fs_sync(void); #else /* !CONFIG_SUSPEND */ #define suspend_valid_only_mem NULL =20 @@ -296,6 +298,7 @@ static inline bool idle_should_enter_s2idle(void) { ret= urn false; } static inline void __init pm_states_init(void) {} static inline void s2idle_set_ops(const struct platform_s2idle_ops *ops) {} static inline void s2idle_wake(void) {} +static inline void suspend_abort_fs_sync(void) {} #endif /* !CONFIG_SUSPEND */ =20 static inline bool pm_suspend_in_progress(void) diff --git a/kernel/power/process.c b/kernel/power/process.c index dc0dfc349f22..8ff68ebaa1e0 100644 --- a/kernel/power/process.c +++ b/kernel/power/process.c @@ -132,7 +132,6 @@ int freeze_processes(void) if (!pm_freezing) static_branch_inc(&freezer_active); =20 - pm_wakeup_clear(0); pm_freezing =3D true; error =3D try_to_freeze_tasks(true); if (!error) diff --git a/kernel/power/suspend.c b/kernel/power/suspend.c index b4ca17c2fecf..3bdb8aca00cc 100644 --- a/kernel/power/suspend.c +++ b/kernel/power/suspend.c @@ -31,6 +31,7 @@ #include #include #include +#include =20 #include "power.h" =20 @@ -74,6 +75,16 @@ bool pm_suspend_default_s2idle(void) } EXPORT_SYMBOL_GPL(pm_suspend_default_s2idle); =20 +static bool suspend_fs_sync_queued; +DEFINE_SPINLOCK(suspend_fs_sync_lock); +DECLARE_COMPLETION(suspend_fs_sync_complete); +void suspend_abort_fs_sync(void) +{ + spin_lock(&suspend_fs_sync_lock); + complete(&suspend_fs_sync_complete); + spin_unlock(&suspend_fs_sync_lock); +} + void s2idle_set_ops(const struct platform_s2idle_ops *ops) { unsigned int sleep_flags; @@ -403,6 +414,71 @@ void __weak arch_suspend_enable_irqs(void) local_irq_enable(); } =20 +static void sync_filesystems_fn(struct work_struct *work) +{ + ksys_sync_helper(); + + spin_lock(&suspend_fs_sync_lock); + suspend_fs_sync_queued =3D false; + complete(&suspend_fs_sync_complete); + spin_unlock(&suspend_fs_sync_lock); +} +static DECLARE_WORK(sync_filesystems, sync_filesystems_fn); + +/** + * suspend_fs_sync_with_abort- Start filesystem sync and handle potential = aborts + * + * Starts filesystem sync in a workqueue, while the main thread uses a + * completion to wait for either the filesystem sync to finish or for a wa= keup + * event. In the case of filesystem sync finishing and triggering the + * completion, the suspend path continues as normal. If the complete is du= e to a + * wakeup or abort signal, the code jumps to the suspend abort path while = the + * filesystem sync finishes in the background. + * + * An aborted suspend that is followed by another suspend is a potential + * scenario that complicates the sequence. This patch handles this by + * serializing any filesystem sync; a subsequent suspend's filesystem sync + * operation will only start when the previous suspend's filesystem sync h= as + * finished. Even while waiting for the previous suspend's filesystem sync= to + * finish, the subsequent suspend will still break early if a wakeup compl= etion + * is triggered, solving the original issue of filesystem sync blocking ab= ort. + */ +static int suspend_fs_sync_with_abort(void) +{ + bool need_suspend_fs_sync_requeue; + + pm_wakeup_clear(0); +Start_fs_sync: + spin_lock(&suspend_fs_sync_lock); + reinit_completion(&suspend_fs_sync_complete); + /* + * Handle the case where a suspend immediately follows a previous + * suspend that was aborted during fs_sync. In this case, serialize + * fs_sync by only starting fs_sync of the subsequent suspend when the + * fs_sync of the previous suspend has finished. + */ + if (suspend_fs_sync_queued) { + need_suspend_fs_sync_requeue =3D true; + } else { + need_suspend_fs_sync_requeue =3D false; + suspend_fs_sync_queued =3D true; + schedule_work(&sync_filesystems); + } + spin_unlock(&suspend_fs_sync_lock); + + /* + * Completion is triggered by fs_sync finishing or a suspend abort + * signal, whichever comes first + */ + wait_for_completion(&suspend_fs_sync_complete); + if (pm_wakeup_pending()) + return -EBUSY; + if (need_suspend_fs_sync_requeue) + goto Start_fs_sync; + + return 0; +} + /** * suspend_enter - Make the system enter the given sleep state. * @state: System sleep state to enter. @@ -590,8 +666,10 @@ static int enter_state(suspend_state_t state) =20 if (sync_on_suspend_enabled) { trace_suspend_resume(TPS("sync_filesystems"), 0, true); - ksys_sync_helper(); + error =3D suspend_fs_sync_with_abort(); trace_suspend_resume(TPS("sync_filesystems"), 0, false); + if (error) + goto Unlock; } =20 pm_pr_dbg("Preparing system for sleep (%s)\n", mem_sleep_labels[state]); --=20 2.51.0.rc0.215.g125493bb4a-goog