From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from mail-pl1-f170.google.com (mail-pl1-f170.google.com [209.85.214.170]) (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 AE2DF3955FE for ; Thu, 6 Aug 2026 17:01:14 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.214.170 ARC-Seal:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1786035676; cv=none; b=lEDddNAjj2GEgY45w3wmf5JlvwUkrNgxjm5V+HKp62NyXkxYIwxP3LYjRf3BeGkUvQ1kiV2fkfW69384v8yjUx9D9yGS6Dd1wfLjYleBwizpls3dp5/vb+bWY+uyhO5GSu5Hz+4NP1K3atwGGUy0GPyitAXnEc1mH46wxUXTyfo= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1786035676; c=relaxed/simple; bh=D7jQTaMJbmiSDuDTkaH8GQDy3HB3esW3XFUiP/y8mrk=; h=From:To:Cc:Subject:Date:Message-ID:MIME-Version; b=CueMEsTL/+1E3D6RoUmSJrejHGSeKahoIwmXVNiA2DzbJ+76PI9ooLxcdVupTNKjpdPKkIeAwJR4qN2mbgbH+6QOy2cGrCF1ie0bH/p1ltwfcwSB8gGrbhZbsBqy48VoU40IeMrzorp29NxTnK0PqyUjney2IhDDrLDJpmuWdug= ARC-Authentication-Results:i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=pEVGUfl1; arc=none smtp.client-ip=209.85.214.170 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="pEVGUfl1" Received: by mail-pl1-f170.google.com with SMTP id d9443c01a7336-2cad8076b01so30456635ad.2 for ; Thu, 06 Aug 2026 10:01:14 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1786035674; x=1786640474; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:from:to:cc:subject:date:message-id:reply-to:content-type; bh=L0SMwlBFnyx74CBd/tXEFrEAcxTvSO7TVd4l/vFfe8k=; b=pEVGUfl1IrcxiXjXNNjtDQDpTOT77XY5EZNU9ushGkhnh4ASMkRawIMYiCMQ8pFkgW K+azFnDOpGF8pnC3bftMd/26sgGhN4WKueLFgwX71vPYmAhdV9EU+nIzvsnZj/WB1sHN mgJTYMjwzio99VFmkJkNwOFy4KB19ZxmfJug48Xc4nTJDaCKtpPKq/L03qfZKzqlsIEW H4DSmBwuAj6KXqewj6eI8QP+OUZJRrrsdPkaOtwl1OIFL9FQtz1V8PUZZx3WPFtE9YjC /7piJ4iiu6NqsfxsJIB1FbpkCBtKqep9+J+NBsyUS8ugyrwYuanBGg7WUs/b0UrqupQT 8fnA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1786035674; x=1786640474; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:x-gm-gg:x-gm-message-state:from:to:cc:subject:date :message-id:reply-to:content-type; bh=L0SMwlBFnyx74CBd/tXEFrEAcxTvSO7TVd4l/vFfe8k=; b=E0NjmSDWHheV1gUmrk9+a0k0/MIBz8q2OQAM5LJr0Wx0Ffu/MmhGAY3pE89uRGKSkR PuVdivgLM2EGAiBur83PNnM97Pyn4+96w+atLI8zCQPlawgP1KZbSKGoS8Yjvz4FvKvF F/cki6x7Pkt6JIdQZwkKAAbLovg+JlqI14gxIee4ZCe8JQN1xKNmbk+wTyObnERhZ/ub v63GoIHoK5ZU6bguJB3bo/bMK/u5VMd4sJ0jP6qiHkrF4P0dMVQv5Qrj+ABRFhe5hPw0 Yy1H3uIzDZu6a1sMdWqqjDOmFActh8+FcHd2E2GFpRlufaeCfMXM9GhZsjZmcwutXKL5 i7Sg== X-Gm-Message-State: AOJu0Yz4af5R005gcmnPtnxlfzcWzzP2GZagvh1hnjcD4WLiac/ln8o6 M9sj/QZllneeRaa/vbZkacoV0QMyTaUjZucA6thOYa/GM7Mv1INJBk9yjGWwRA== X-Gm-Gg: AR+sD10bwLb/mbwWo6f/2CXIJVwgk06hCI3NFUP9b96ISetcbRFezcM/hlP4JsZkGIo kqp1HH603UH7kg2PbdfKVaxQfOUcrhc+N+5Pf7ax3/goCQudaK8VLN1gqlRwHmYkbPSx06afIrX GStuPJFG6Mj48sAMP52uH7vQYK+9XhCG2uS/G8ewGjBeECHQxoMmo1a/JhIrRGIpnbXMEGs5jgK s10EyDBEficbK8jnVPFNk8GK4uNL16WDvWTqEC8YAVVm9/i1ZgYBfgaAJJIffuSIvtVhOqBNR4I Z4YMWazG8/O+mc8Rj+NGI8jBW+8zaM/tav50xLqbyX3Fmv1BBbo9Nw7IQkrf2EW6sMcw7dEsNfF CY+fpXmuo3OcxVR6ID5sMUuFADq+U9LcAALbwF3lt016FzIbIFFobNJEjwC+P0ViptECAVmNSM7 0V9wWW12azaJS/m+pM6PXb4KDSG1P/AZXq2ol/pLwmCzK1EInJj5AE8Rfp8A0ljpYTG7neaRg8n hQuMMaitJw/71TSx8wxK6VJUHN+ek0lTVjVY5OIYtjBN401sIGeDpCuicB2+zk1meh5z/opWC2Q wLmH2BNOzdKj7hT327YIyf0= X-Received: by 2002:a05:6a21:3a82:b0:3c3:704d:57c4 with SMTP id adf61e73a8af0-3cb85ded954mr20531218637.12.1786035673609; Thu, 06 Aug 2026 10:01:13 -0700 (PDT) Received: from daehojeong-desktop.mtv.corp.google.com ([2a00:79e0:2e7c:8:40a2:8f83:8b73:662d]) by smtp.gmail.com with ESMTPSA id 5a478bee46e88-3158673be99sm47751776eec.15.2026.08.06.10.01.11 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 06 Aug 2026 10:01:12 -0700 (PDT) From: Daeho Jeong To: linux-kernel@vger.kernel.org, linux-f2fs-devel@lists.sourceforge.net, kernel-team@android.com Cc: Daeho Jeong Subject: [PATCH v2] f2fs: quiesce background threads during system suspend using PM notifier Date: Thu, 6 Aug 2026 10:01:08 -0700 Message-ID: <20260806170108.2100312-1-daeho43@gmail.com> X-Mailer: git-send-email 2.55.0.654.g21b8a5bc05-goog Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: 8bit From: Daeho Jeong During system suspend, a race condition can cause f2fs_gc and f2fs_discard threads to call submit_bio() while the underlying block device (e.g., UFS) is in Runtime PM suspend. Because Runtime PM worker threads are already frozen during task freezing, the threads become trapped in __bio_queue_enter() waiting on mq_freeze_wq, leading to a PM freezer timeout. To prevent this deadlock, register a PM notifier to set SBI_IS_SUSPENDING during PM_SUSPEND_PREPARE. Background GC and discard threads check this flag and immediately stop issuing new bios, allowing them to enter a freezable sleep state cleanly before process freezing begins. In addition, check freezing() as a fast path to stop issuing new I/O when non-PM freezing (e.g. dm-snapshot or cgroup freezer) is requested. Signed-off-by: Daeho Jeong --- v2: check freezing() together for non-PM freezing. --- fs/f2fs/f2fs.h | 9 +++++++++ fs/f2fs/gc.c | 13 ++++++++----- fs/f2fs/segment.c | 12 ++++++++---- fs/f2fs/super.c | 25 +++++++++++++++++++++++++ 4 files changed, 50 insertions(+), 9 deletions(-) diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index f1774d4e18d2..2a7b0fe8419b 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -25,6 +25,7 @@ #include #include #include +#include #include #include @@ -1492,6 +1493,7 @@ enum { SBI_IS_FREEZING, /* freezefs is in process */ SBI_IS_WRITABLE, /* remove ro mountoption transiently */ SBI_ENABLE_CHECKPOINT, /* indicate it's during f2fs_enable_checkpoint() */ + SBI_IS_SUSPENDING, /* system suspend is in progress */ MAX_SBI_FLAG, }; @@ -1755,6 +1757,7 @@ struct f2fs_sb_info { struct f2fs_rwsem sb_lock; /* lock for raw super block */ int valid_super_block; /* valid super block no */ unsigned long s_flag; /* flags for sbi */ + struct notifier_block pm_nb; /* for PM notifier */ struct mutex writepages; /* mutex for writepages() */ #ifdef CONFIG_BLK_DEV_ZONED @@ -2309,6 +2312,12 @@ static inline void clear_sbi_flag(struct f2fs_sb_info *sbi, unsigned int type) clear_bit(type, &sbi->s_flag); } +static inline bool f2fs_is_suspending(struct f2fs_sb_info *sbi) +{ + return is_sbi_flag_set(sbi, SBI_IS_SUSPENDING) || + unlikely(freezing(current)); +} + static inline unsigned long long cur_cp_version(struct f2fs_checkpoint *cp) { return le64_to_cpu(cp->checkpoint_ver); diff --git a/fs/f2fs/gc.c b/fs/f2fs/gc.c index ffaa7ba76a1b..6c7ea38eb70d 100644 --- a/fs/f2fs/gc.c +++ b/fs/f2fs/gc.c @@ -71,7 +71,8 @@ static int gc_thread_func(void *data) if (kthread_should_stop()) break; - if (sbi->sb->s_writers.frozen >= SB_FREEZE_WRITE) { + if (sbi->sb->s_writers.frozen >= SB_FREEZE_WRITE || + f2fs_is_suspending(sbi)) { increase_sleep_time(gc_th, &wait_ms); stat_other_skip_bggc_count(sbi); continue; @@ -1064,8 +1065,9 @@ static int gc_node_segment(struct f2fs_sb_info *sbi, struct node_info ni; int err; - /* stop BG_GC if there is not enough free sections. */ - if (gc_type == BG_GC && has_not_enough_free_secs(sbi, 0, 0)) + /* stop BG_GC if there is not enough free sections or suspending/freezing. */ + if (gc_type == BG_GC && (has_not_enough_free_secs(sbi, 0, 0) || + f2fs_is_suspending(sbi))) return submitted; if (check_valid_map(sbi, segno, off) == 0) @@ -1611,7 +1613,8 @@ static int gc_data_segment(struct f2fs_sb_info *sbi, struct f2fs_summary *sum, * Or, stop GC if the segment becomes fully valid caused by * race condition along with SSR block allocation. */ - if ((gc_type == BG_GC && has_not_enough_free_secs(sbi, 0, 0)) || + if ((gc_type == BG_GC && (has_not_enough_free_secs(sbi, 0, 0) || + f2fs_is_suspending(sbi))) || (!force_migrate && get_valid_blocks(sbi, segno, true) == CAP_BLKS_PER_SEC(sbi))) return submitted; @@ -2015,7 +2018,7 @@ int f2fs_gc(struct f2fs_sb_info *sbi, struct f2fs_gc_control *gc_control) goto stop; } retry: - if (unlikely(freezing(current))) { + if (f2fs_is_suspending(sbi)) { ret = 0; goto stop; } diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index d71ddb3ee918..7b03b3d06161 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -1300,7 +1300,7 @@ static int __submit_discard_cmd(struct f2fs_sb_info *sbi, if (dc->state != D_PREP) return 0; - if (is_sbi_flag_set(sbi, SBI_NEED_FSCK)) + if (is_sbi_flag_set(sbi, SBI_NEED_FSCK) || f2fs_is_suspending(sbi)) return 0; #ifdef CONFIG_BLK_DEV_ZONED @@ -1341,6 +1341,9 @@ static int __submit_discard_cmd(struct f2fs_sb_info *sbi, unsigned long flags; bool last = true; + if (f2fs_is_suspending(sbi)) + break; + if (len > max_discard_blocks) { len = max_discard_blocks; last = false; @@ -1615,7 +1618,7 @@ static void __issue_discard_cmd_orderly(struct f2fs_sb_info *sbi, if (dc->state != D_PREP) goto next; - if (*issued > 0 && unlikely(freezing(current))) + if (f2fs_is_suspending(sbi)) break; if (dpolicy->io_aware && !is_idle(sbi, DISCARD_TIME)) { @@ -1688,7 +1691,7 @@ static int __issue_discard_cmd(struct f2fs_sb_info *sbi, list_for_each_entry_safe(dc, tmp, pend_list, list) { f2fs_bug_on(sbi, dc->state != D_PREP); - if (issued > 0 && unlikely(freezing(current))) { + if (f2fs_is_suspending(sbi)) { suspended = true; break; } @@ -1955,7 +1958,8 @@ static int issue_discard_thread(void *data) continue; if (kthread_should_stop()) return 0; - if (is_sbi_flag_set(sbi, SBI_NEED_FSCK) || + if (f2fs_is_suspending(sbi) || + is_sbi_flag_set(sbi, SBI_NEED_FSCK) || !atomic_read(&dcc->discard_cmd_cnt)) { wait_ms = dpolicy.max_interval; continue; diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index c448d992ff2a..8c97b5d1ee68 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -1979,6 +1979,26 @@ static void destroy_device_list(struct f2fs_sb_info *sbi) kvfree(sbi->devs); } +static int f2fs_pm_notifier(struct notifier_block *nb, + unsigned long action, void *ptr) +{ + struct f2fs_sb_info *sbi = container_of(nb, struct f2fs_sb_info, pm_nb); + + switch (action) { + case PM_HIBERNATION_PREPARE: + case PM_SUSPEND_PREPARE: + case PM_RESTORE_PREPARE: + set_sbi_flag(sbi, SBI_IS_SUSPENDING); + break; + case PM_POST_SUSPEND: + case PM_POST_HIBERNATION: + case PM_POST_RESTORE: + clear_sbi_flag(sbi, SBI_IS_SUSPENDING); + break; + } + return NOTIFY_OK; +} + static void f2fs_put_super(struct super_block *sb) { struct f2fs_sb_info *sbi = F2FS_SB(sb); @@ -1986,6 +2006,8 @@ static void f2fs_put_super(struct super_block *sb) int err = 0; bool done; + unregister_pm_notifier(&sbi->pm_nb); + /* unregister procfs/sysfs entries in advance to avoid race case */ f2fs_unregister_sysfs(sbi); @@ -5436,6 +5458,9 @@ static int f2fs_fill_super(struct super_block *sb, struct fs_context *fc) f2fs_update_time(sbi, REQ_TIME); clear_sbi_flag(sbi, SBI_CP_DISABLED_QUICK); + sbi->pm_nb.notifier_call = f2fs_pm_notifier; + register_pm_notifier(&sbi->pm_nb); + sbi->umount_lock_holder = NULL; return 0; -- 2.55.0.654.g21b8a5bc05-goog