From nobody Thu Sep 24 16:09:09 2026 Received: from dggsgout11.his.huawei.com (dggsgout11.his.huawei.com [45.249.212.51]) (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 3B67653A391; Tue, 22 Sep 2026 11:15:20 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=45.249.212.51 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1790075724; cv=none; b=acEYmgZnCPJP/xXozj0sPppdc/UT2Z+BGTs+YLvaFO2cjKMefHzb17iKO4OtpysqHylyoWXLQRkXL+izCjko+SeqI8h+eiV/0yG9KEweO7fh/zPZgf5Puli2Yfvbdd+Lzyenwop+LfId2XQVAaZZKuovVqpNkCLg9NF3FCXyCdE= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1790075724; c=relaxed/simple; bh=BApdRnBK8PH7pmLXBHTfDNROM3ARlWw+jwMxlgC/fno=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=ZZWNr9PCCM7oU79nPZXXP4ekBBTtjJcftGsmbMXxICLFCZjMNLtUPU7PLh37qq2k9xHDlw8Pd6AK5otJ7rCl4gAmkJE44CrmiI5gLVyrBb0A17FBCCCRrBICGUXOK+/lawv10Nf7VBZunpTRdT/HMnZGJ0Jami1dwgXE7OD0hKU= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=huaweicloud.com; spf=pass smtp.mailfrom=huaweicloud.com; arc=none smtp.client-ip=45.249.212.51 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=huaweicloud.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=huaweicloud.com Received: from mail.maildlp.com (unknown [172.19.163.170]) by dggsgout11.his.huawei.com (SkyGuard) with ESMTPS id 4hpyDj5hTVzYQvhc; Tue, 22 Sep 2026 19:15:01 +0800 (CST) Received: from mail02.huawei.com (unknown [10.116.40.128]) by mail.maildlp.com (Postfix) with ESMTP id E2E1840572; Tue, 22 Sep 2026 19:15:16 +0800 (CST) Received: from huaweicloud.com (unknown [10.50.85.155]) by APP4 (Coremail) with UTF8SMTPSA id gCh0CgD3xik4Y7JqtosFBQ--.39851S5; Tue, 22 Sep 2026 19:15:16 +0800 (CST) From: Zhang Yi To: linux-mm@kvack.org Cc: linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org, linux-ext4@vger.kernel.org, akpm@linux-foundation.org, david@kernel.org, ljs@kernel.org, liam@infradead.org, vbabka@kernel.org, rppt@kernel.org, surenb@google.com, mhocko@suse.com, hughd@google.com, baolin.wang@linux.alibaba.com, willy@infradead.org, jack@suse.cz, ziy@nvidia.com, bfoster@redhat.com, joannelkoong@gmail.com, djwong@kernel.org, yi.zhang@huawei.com, yi.zhang@huaweicloud.com, yizhang089@gmail.com, yangerkun@huawei.com, chengzhihao1@huawei.com, wangkefeng.wang@huawei.com, yukuai@fnnas.com Subject: [PATCH v4 1/4] mm/truncate: align truncation boundaries to mapping minimum folio order Date: Tue, 22 Sep 2026 19:07:00 +0800 Message-ID: <20260922110703.468389-2-yi.zhang@huaweicloud.com> X-Mailer: git-send-email 2.52.0 In-Reply-To: <20260922110703.468389-1-yi.zhang@huaweicloud.com> References: <20260922110703.468389-1-yi.zhang@huaweicloud.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-CM-TRANSID: gCh0CgD3xik4Y7JqtosFBQ--.39851S5 X-Coremail-Antispam: 1UD129KBjvJXoWxZr4Utr13Cw18Wr1rJr18Zrb_yoWrAF18pF ZrGrnxArWkGrW7Cr48ua1vvr4rXayDXa15AFyxJasayFnYgF1qkFy2v3W0vw4UGryxJF1F yF4jyFWqg3WqvFJanT9S1TB71UUUUU7qnTZGkaVYY2UrUUUUjbIjqfuFe4nvWSU5nxnvy2 9KBjDU0xBIdaVrnRJUUUmY14x267AKxVWrJVCq3wAFc2x0x2IEx4CE42xK8VAvwI8IcIk0 rVWrJVCq3wAFIxvE14AKwVWUJVWUGwA2048vs2IY020E87I2jVAFwI0_Jr4l82xGYIkIc2 x26xkF7I0E14v26ryj6s0DM28lY4IEw2IIxxk0rwA2F7IY1VAKz4vEj48ve4kI8wA2z4x0 Y4vE2Ix0cI8IcVAFwI0_JFI_Gr1l84ACjcxK6xIIjxv20xvEc7CjxVAFwI0_Cr0_Gr1UM2 8EF7xvwVC2z280aVAFwI0_Cr1j6rxdM28EF7xvwVC2z280aVCY1x0267AKxVW0oVCq3wAS 0I0E0xvYzxvE52x082IY62kv0487Mc02F40EFcxC0VAKzVAqx4xG6I80ewAv7VC0I7IYx2 IY67AKxVWUJVWUGwAv7VC2z280aVAFwI0_Jr0_Gr1lOx8S6xCaFVCjc4AY6r1j6r4UM4x0 Y48IcxkI7VAKI48JM4x0x7Aq67IIx4CEVc8vx2IErcIFxwACI402YVCY1x02628vn2kIc2 xKxwCY1x0262kKe7AKxVW8ZVWrXwCF04k20xvY0x0EwIxGrwCFx2IqxVCFs4IE7xkEbVWU JVW8JwC20s026c02F40E14v26r1j6r18MI8I3I0E7480Y4vE14v26r106r1rMI8E67AF67 kF1VAFwI0_GFv_WrylIxkGc2Ij64vIr41lIxAIcVC0I7IYx2IY67AKxVWUJVWUCwCI42IY 6xIIjxv20xvEc7CjxVAFwI0_Cr0_Gr1UMIIF0xvE42xK8VAvwI8IcIk0rVWUJVWUCwCI42 IY6I8E87Iv67AKxVWUJVW8JwCI42IY6I8E87Iv6xkF7I0E14v26r4j6r4UJbIYCTnIWIev Ja73UjIFyTuYvjTRMfOzDUUUU X-CM-SenderInfo: d1lo6xhdqjqx5xdzvxpfor3voofrz/ Content-Type: text/plain; charset="utf-8" From: Zhang Yi When the mapping has a non-zero minimum folio order (min_order), folio_split() in truncate_inode_partial_folio() stops at min_order instead of order 0, so the sub-folio containing a split point stays aligned to 1 << min_order rather than to a single page. The original boundaries in truncate_inode_pages_range() were based on page granularity, so either boundary could land inside the min_order chunk at its edge, and the truncation loop would drop that whole chunk, valid out-of-range tail included. For example, a 64K (order-4) folio with min_order =3D 2 (16K) punched from offset 0 to 36K: split @p0 -> [p0-p3, p4-p7, p8-p15] # non-uniform, min_order folio2 =3D p8-p15 # straddles: p8 in range, p9-p15 tail valid 2nd split of folio2 -> [p8-p11, p12-p15] # success end(old) =3D p9 # BUG: p9 inside [p8-p11] loop truncates ... p8-p11 # p9-p11's valid tail is lost It has gone unnoticed so far for two reasons. A non-zero min_order is only used by filesystems with a block or sector size larger than the page size, and those either always write back the affected range before punching a hole or truncating, or they carry filesystem private data on dirty folios (e.g. buffer_head), which makes filemap_release_folio() fail and folio_split() abort with -EBUSY, so the folio is never split and the old start/end boundaries remain valid. The bug only becomes reachable on paths that truncate dirty large folios without prior writeback and without filesystem private data, such as the upcoming ext4 iomap buffered I/O path. Align both start (rounded up) and end (rounded down) to the mapping minimum folio order so they always fall on a folio boundary. Reported-by: Joanne Koong Link: https://lore.kernel.org/linux-mm/CAJnrk1bQYUe6+1ryyJur5EEnZYrC+_5AYsy= =3DOWzVRgD4202y1g@mail.gmail.com/ Fixes: e220917fa5077 ("mm: split a folio in minimum folio order chunks") Suggested-by: Zi Yan Signed-off-by: Zhang Yi Reviewed-by: Brian Foster Reviewed-by: Jan Kara Reviewed-by: Zi Yan --- mm/truncate.c | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/mm/truncate.c b/mm/truncate.c index b58ba940be47..f9625bb4916f 100644 --- a/mm/truncate.c +++ b/mm/truncate.c @@ -345,9 +345,11 @@ long mapping_evict_folio(struct address_space *mapping= , struct folio *folio) * @lstart: offset from which to truncate * @lend: offset to which to truncate (inclusive) * - * Truncate the page cache, removing the pages that are between - * specified offsets (and zeroing out partial pages - * if lstart or lend + 1 is not page aligned). + * Truncate the page cache, removing the folios that are between specified + * offsets (and zeroing out partial folios if lstart or lend + 1 is not + * folio aligned). For mappings with a non-zero minimum folio order, the + * boundaries are aligned inwards to 1 << min_order so the edge sub-folio + * straddling the range is kept. * * Truncate takes two passes - the first pass is nonblocking. It will not * block on page locks and it will not block on writeback. The second pass @@ -374,14 +376,14 @@ void truncate_inode_pages_range(struct address_space = *mapping, int i; struct folio *folio; bool same_folio; + pgoff_t min_nrpages =3D mapping_min_folio_nrpages(mapping); =20 if (mapping_empty(mapping)) return; =20 /* - * 'start' and 'end' always covers the range of pages to be fully - * truncated. Partial pages are covered with 'partial_start' at the - * start of the range and 'partial_end' at the end of the range. + * 'start' and 'end' always covers the range of folios to be fully + * truncated, with both boundaries aligned inwards to 1 << min_order. * Note that 'end' is exclusive while 'lend' is inclusive. */ start =3D (lstart + PAGE_SIZE - 1) >> PAGE_SHIFT; @@ -395,6 +397,10 @@ void truncate_inode_pages_range(struct address_space *= mapping, else end =3D (lend + 1) >> PAGE_SHIFT; =20 + start =3D round_up(start, min_nrpages); + if (end !=3D (pgoff_t)-1) + end =3D round_down(end, min_nrpages); + folio_batch_init(&fbatch); index =3D start; while (index < end && find_lock_entries(mapping, &index, end - 1, --=20 2.52.0 From nobody Thu Sep 24 16:09:09 2026 Received: from dggsgout11.his.huawei.com (dggsgout11.his.huawei.com [45.249.212.51]) (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 AF23B53A39A; Tue, 22 Sep 2026 11:15:21 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=45.249.212.51 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1790075725; cv=none; b=jUt3ujf0Offl5mJtPfksJ6hmQrqFz8Pu3g6DQbmo6Iprf0c46yC7czX6DiydPMSlWgQGyjcfq2P569p9Qo1edi78UQsT69+gT3kCv34nwJ1brdlZIvJDIh4n/uoPZYLi6A1aORX4JLpdbLQ53vjF7AisuLRGak6CQla6ArQvmdg= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1790075725; c=relaxed/simple; bh=RQQXjrlWGv8ZPWsAJeovpmU7boL0Ryw4AYbxrrW5dGc=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=X72fKX8rcEkT96JwX1besdmXz9gFGmhIeGbpnJYTurwEEpglNihagZ6uW/V1tWpbZQdc93GMIL+czlMuwkegdx8zKXE/69CDPkBvKSvI+9NK2cpLtaER+w7ETR3q9DT0KO/t7oYGHTr9Hlvk83MBS98qJx2GRGt1lQt49jhIQJw= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=huaweicloud.com; spf=pass smtp.mailfrom=huaweicloud.com; arc=none smtp.client-ip=45.249.212.51 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=huaweicloud.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=huaweicloud.com Received: from mail.maildlp.com (unknown [172.19.163.177]) by dggsgout11.his.huawei.com (SkyGuard) with ESMTPS id 4hpyDj6yKszYQvj1; Tue, 22 Sep 2026 19:15:01 +0800 (CST) Received: from mail02.huawei.com (unknown [10.116.40.128]) by mail.maildlp.com (Postfix) with ESMTP id 10A6440ACB; Tue, 22 Sep 2026 19:15:17 +0800 (CST) Received: from huaweicloud.com (unknown [10.50.85.155]) by APP4 (Coremail) with UTF8SMTPSA id gCh0CgD3xik4Y7JqtosFBQ--.39851S6; Tue, 22 Sep 2026 19:15:16 +0800 (CST) From: Zhang Yi To: linux-mm@kvack.org Cc: linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org, linux-ext4@vger.kernel.org, akpm@linux-foundation.org, david@kernel.org, ljs@kernel.org, liam@infradead.org, vbabka@kernel.org, rppt@kernel.org, surenb@google.com, mhocko@suse.com, hughd@google.com, baolin.wang@linux.alibaba.com, willy@infradead.org, jack@suse.cz, ziy@nvidia.com, bfoster@redhat.com, joannelkoong@gmail.com, djwong@kernel.org, yi.zhang@huawei.com, yi.zhang@huaweicloud.com, yizhang089@gmail.com, yangerkun@huawei.com, chengzhihao1@huawei.com, wangkefeng.wang@huawei.com, yukuai@fnnas.com Subject: [PATCH v4 2/4] mm/truncate: look up the end-edge straddler by index Date: Tue, 22 Sep 2026 19:07:01 +0800 Message-ID: <20260922110703.468389-3-yi.zhang@huaweicloud.com> X-Mailer: git-send-email 2.52.0 In-Reply-To: <20260922110703.468389-1-yi.zhang@huaweicloud.com> References: <20260922110703.468389-1-yi.zhang@huaweicloud.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-CM-TRANSID: gCh0CgD3xik4Y7JqtosFBQ--.39851S6 X-Coremail-Antispam: 1UD129KBjvJXoWxJFW7XrWkGF1UJry5Xr4xZwb_yoW5tFW5pr WYgrn5GrWkWr1Ikwsrua1jyrn8Ca4rXayUCFyxGw17AFn0q3WDKryjga48W3y7tw1kA348 XrWjqFyY9Fy5Aa7anT9S1TB71UUUUU7qnTZGkaVYY2UrUUUUjbIjqfuFe4nvWSU5nxnvy2 9KBjDU0xBIdaVrnRJUUUmF14x267AKxVWrJVCq3wAFc2x0x2IEx4CE42xK8VAvwI8IcIk0 rVWrJVCq3wAFIxvE14AKwVWUJVWUGwA2048vs2IY020E87I2jVAFwI0_Jryl82xGYIkIc2 x26xkF7I0E14v26ryj6s0DM28lY4IEw2IIxxk0rwA2F7IY1VAKz4vEj48ve4kI8wA2z4x0 Y4vE2Ix0cI8IcVAFwI0_JFI_Gr1l84ACjcxK6xIIjxv20xvEc7CjxVAFwI0_Cr0_Gr1UM2 8EF7xvwVC2z280aVAFwI0_Cr1j6rxdM28EF7xvwVC2z280aVCY1x0267AKxVW0oVCq3wAS 0I0E0xvYzxvE52x082IY62kv0487Mc02F40EFcxC0VAKzVAqx4xG6I80ewAv7VC0I7IYx2 IY67AKxVWUJVWUGwAv7VC2z280aVAFwI0_Jr0_Gr1lOx8S6xCaFVCjc4AY6r1j6r4UM4x0 Y48IcxkI7VAKI48JM4x0x7Aq67IIx4CEVc8vx2IErcIFxwACI402YVCY1x02628vn2kIc2 xKxwCY1x0262kKe7AKxVW8ZVWrXwCF04k20xvY0x0EwIxGrwCFx2IqxVCFs4IE7xkEbVWU JVW8JwC20s026c02F40E14v26r1j6r18MI8I3I0E7480Y4vE14v26r106r1rMI8E67AF67 kF1VAFwI0_GFv_WrylIxkGc2Ij64vIr41lIxAIcVC0I7IYx2IY67AKxVWUJVWUCwCI42IY 6xIIjxv20xvEc7CjxVAFwI0_Cr0_Gr1UMIIF0xvE42xK8VAvwI8IcIk0rVWUJVWUCwCI42 IY6I8E87Iv67AKxVWUJVW8JwCI42IY6I8E87Iv6xkF7I0E14v26r4UJVWxJrUvcSsGvfC2 KfnxnUUI43ZEXa7sRipB-tUUUUU== X-CM-SenderInfo: d1lo6xhdqjqx5xdzvxpfor3voofrz/ Content-Type: text/plain; charset="utf-8" From: Zhang Yi In truncate_inode_partial_folio(), after the first split at the start edge, folio_split() unlocks and drops the refcount of the after-split sub-folios. The sub-folio straddling the end of the truncation range is therefore unlocked and only transiently ref'd in the page cache while the code still derives it from a page pointer inside the original folio. Between the first split finishing and page_folio() resolving split_at2, that tail page can be reclaimed, freed and reallocated as a new large folio in the same mapping at a different file offset. folio2 then points at a folio that does not cover the end boundary, yet folio2->mapping =3D=3D folio->mapping still holds, so the stale pointer passes the mapping check and folio_split_or_unmap() splits a folio at a wrong position (or, with a transient refcount, a use-after-free window opens between try_get and the split). __folio_split()'s own folio !=3D page_folio(split_at) check cannot catch this either since split_at2 has been reallocated as part of the new folio, so page_folio(split_at2) resolves back to folio2. Look the straddler up by its page index instead. __filemap_get_folio() returns the folio currently covering the boundary, ref'd and locked, with the mapping validated under the lock, so the split target is always the real folio at the end edge. Reported-by: Sashiko Link: https://sashiko.dev/#/message/20260916094500.C30061F00893%40smtp.kern= el.org Link: https://lore.kernel.org/linux-mm/DLGXT0ERY79Z.3C5DYVJVX6S9Z@nvidia.co= m/ Fixes: 7460b470a131 ("mm/truncate: use folio_split() in truncate operation") Cc: stable@vger.kernel.org Suggested-by: Jan Kara Suggested-by: Zi Yan Signed-off-by: Zhang Yi Acked-by: Zi Yan Reviewed-by: Brian Foster Reviewed-by: Jan Kara --- mm/truncate.c | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/mm/truncate.c b/mm/truncate.c index f9625bb4916f..a8a179b38252 100644 --- a/mm/truncate.c +++ b/mm/truncate.c @@ -259,30 +259,32 @@ bool truncate_inode_partial_folio(struct folio *folio= , loff_t start, loff_t end) * for shmem truncate */ struct folio *folio2; + pgoff_t end_idx; =20 if (offset + length =3D=3D size) goto no_split; =20 - split_at2 =3D folio_page(folio, - PAGE_ALIGN_DOWN(offset + length) / PAGE_SIZE); - folio2 =3D page_folio(split_at2); - - if (!folio_try_get(folio2)) + /* + * After the first split at the start edge, the folio at the + * end edge may be freed and reused concurrently. + * __filemap_get_folio() looks up the straddler at end_idx + * and returns it locked and ref'd with the mapping + * validated. + */ + end_idx =3D (pos + offset + length) >> PAGE_SHIFT; + folio2 =3D __filemap_get_folio(folio->mapping, end_idx, + FGP_LOCK | FGP_NOWAIT, 0); + if (IS_ERR(folio2)) goto no_split; =20 + /* make sure folio2 is large */ if (!folio_test_large(folio2)) goto out; =20 - if (!folio_trylock(folio2)) - goto out; - - /* make sure folio2 is large and does not change its mapping */ - if (folio_test_large(folio2) && - folio2->mapping =3D=3D folio->mapping) - folio_split_or_unmap(folio2, split_at2, min_order); - - folio_unlock(folio2); + split_at2 =3D folio_page(folio2, (end_idx - folio2->index)); + folio_split_or_unmap(folio2, split_at2, min_order); out: + folio_unlock(folio2); folio_put(folio2); no_split: return true; --=20 2.52.0 From nobody Thu Sep 24 16:09:09 2026 Received: from dggsgout12.his.huawei.com (dggsgout12.his.huawei.com [45.249.212.56]) (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 69E0753C3A9; Tue, 22 Sep 2026 11:15:29 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=45.249.212.56 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1790075732; cv=none; b=JX3ViCV846JOqwUPXI5A4J9PLSx4oRZNpatks1ZmJsvrkzqavrqpjBf2CYLzbchIjmPlWEl9WpGzujcP9QeNGj1lL6uwqnon2NaHFGR6My9aXuG/DBq5V0a6np8nOrnkGurPWzwqYVWr111e5ulAO6ixfue+EVHijOUEUc/Wc+0= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1790075732; c=relaxed/simple; bh=G9dil8fSoJaxMTnGQpG/LZRDRpkJuMKVQjJOM0FqmUE=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=FdV+e9vwsOIz6C5oAQy3K6qrJI8g1BmzaICRCbY/eNvo6QXLiUqHtr51pO+bO21QzVlRYTSwaL1TJ6vQUj+Vj7jZCoInlbjRFXwFje/q2L+KoA/JdoGntTY4bYMRnmcSMeE99cJxz8MBuPAmbVM582mao7QqCDnEWkJGSzDSOXk= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=huaweicloud.com; spf=pass smtp.mailfrom=huaweicloud.com; arc=none smtp.client-ip=45.249.212.56 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=huaweicloud.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=huaweicloud.com Received: from mail.maildlp.com (unknown [172.19.163.177]) by dggsgout12.his.huawei.com (SkyGuard) with ESMTPS id 4hpyDY0MKVzKHNKY; Tue, 22 Sep 2026 19:14:53 +0800 (CST) Received: from mail02.huawei.com (unknown [10.116.40.128]) by mail.maildlp.com (Postfix) with ESMTP id 28C2240AC0; Tue, 22 Sep 2026 19:15:17 +0800 (CST) Received: from huaweicloud.com (unknown [10.50.85.155]) by APP4 (Coremail) with UTF8SMTPSA id gCh0CgD3xik4Y7JqtosFBQ--.39851S7; Tue, 22 Sep 2026 19:15:16 +0800 (CST) From: Zhang Yi To: linux-mm@kvack.org Cc: linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org, linux-ext4@vger.kernel.org, akpm@linux-foundation.org, david@kernel.org, ljs@kernel.org, liam@infradead.org, vbabka@kernel.org, rppt@kernel.org, surenb@google.com, mhocko@suse.com, hughd@google.com, baolin.wang@linux.alibaba.com, willy@infradead.org, jack@suse.cz, ziy@nvidia.com, bfoster@redhat.com, joannelkoong@gmail.com, djwong@kernel.org, yi.zhang@huawei.com, yi.zhang@huaweicloud.com, yizhang089@gmail.com, yangerkun@huawei.com, chengzhihao1@huawei.com, wangkefeng.wang@huawei.com, yukuai@fnnas.com Subject: [PATCH v4 3/4] mm/truncate: fix data loss when splitting straddling large folios fails Date: Tue, 22 Sep 2026 19:07:02 +0800 Message-ID: <20260922110703.468389-4-yi.zhang@huaweicloud.com> X-Mailer: git-send-email 2.52.0 In-Reply-To: <20260922110703.468389-1-yi.zhang@huaweicloud.com> References: <20260922110703.468389-1-yi.zhang@huaweicloud.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-CM-TRANSID: gCh0CgD3xik4Y7JqtosFBQ--.39851S7 X-Coremail-Antispam: 1UD129KBjvJXoW3KrWkCF17Ww1kGr4ktFW5Jrb_yoWkArWUpa yUK3sxtrZ5Ww42krnrZa1UXw45tas3XFWUAFWxGwnxCan0qw17KF1UK3WUKFW7Jr97Zryr XF1jyay7WF1UJFDanT9S1TB71UUUUU7qnTZGkaVYY2UrUUUUjbIjqfuFe4nvWSU5nxnvy2 9KBjDU0xBIdaVrnRJUUUmI14x267AKxVWrJVCq3wAFc2x0x2IEx4CE42xK8VAvwI8IcIk0 rVWrJVCq3wAFIxvE14AKwVWUJVWUGwA2048vs2IY020E87I2jVAFwI0_JrWl82xGYIkIc2 x26xkF7I0E14v26ryj6s0DM28lY4IEw2IIxxk0rwA2F7IY1VAKz4vEj48ve4kI8wA2z4x0 Y4vE2Ix0cI8IcVAFwI0_JFI_Gr1l84ACjcxK6xIIjxv20xvEc7CjxVAFwI0_Gr1j6F4UJw A2z4x0Y4vEx4A2jsIE14v26F4UJVW0owA2z4x0Y4vEx4A2jsIEc7CjxVAFwI0_GcCE3s1l e2I262IYc4CY6c8Ij28IcVAaY2xG8wAqx4xG64xvF2IEw4CE5I8CrVC2j2WlYx0E2Ix0cI 8IcVAFwI0_Jr0_Jr4lYx0Ex4A2jsIE14v26r1j6r4UMcvjeVCFs4IE7xkEbVWUJVW8JwAC jcxG0xvY0x0EwIxGrwACjI8F5VA0II8E6IAqYI8I648v4I1lFIxGxcIEc7CjxVA2Y2ka0x kIwI1lc7CjxVAaw2AFwI0_GFv_Wryl42xK82IYc2Ij64vIr41l4I8I3I0E4IkC6x0Yz7v_ Jr0_Gr1lx2IqxVAqx4xG67AKxVWUJVWUGwC20s026x8GjcxK67AKxVWUGVWUWwC2zVAF1V AY17CE14v26r4a6rW5MIIYrxkI7VAKI48JMIIF0xvE2Ix0cI8IcVAFwI0_Jr0_JF4lIxAI cVC0I7IYx2IY6xkF7I0E14v26r4UJVWxJr1lIxAIcVCF04k26cxKx2IYs7xG6r1j6r1xMI IF0xvEx4A2jsIE14v26r1j6r4UMIIF0xvEx4A2jsIEc7CjxVAFwI0_Gr1j6F4UJbIYCTnI WIevJa73UjIFyTuYvjTRM6wCDUUUU X-CM-SenderInfo: d1lo6xhdqjqx5xdzvxpfor3voofrz/ Content-Type: text/plain; charset="utf-8" From: Zhang Yi truncate_inode_partial_folio() splits a large folio so that the caller's truncate loop can drop the in-range sub-folios while keeping the out-of-range tail. The first split at the punch start edge is non-uniform, which leaves the sub-folio at the truncation end edge as large as possible, this means it may still straddle the range, holding both zeroed in-range and valid out-of-range data. The function then attempts a second split at offset + length to isolate that tail. If the second split fails the straddling sub-folio stays merged. The function returned true unconditionally on all exit paths of the success block, telling the caller it was fully handled. The caller kept its default end and the truncate loop truncated every sub-folio below it, including the merged straddler, discarding the valid out-of-range tail. For example, a 4-page order-2 folio punched from offset 0 to the middle of the last page: truncate_inode_pages_range() truncate_inode_partial_folio() # same_folio =3D=3D true 1st split at page0 -> [p0, p1, p2-3] # non-uniform, success folio2 =3D p2-3 # straddles: p2 zeroed, p3 tail valid 2nd split of folio2 fails / cannot lock return true # BUG: caller keeps default end end =3D 3 loop truncates p0, p1, p2-3 # p3's valid tail is lost This became reachable after commit 7460b470a131 ("mm/truncate: use folio_split() in truncate operation") replaced the atomic split_folio() with folio_split(), whose non-uniform split can partially split a folio and leave the end edge merged. It has gone unnoticed because a dirty large folio normally carries the filesystem's private data, for example buffer_head, so filemap_release_folio() fails on a dirty folio and folio_split() aborts with -EBUSY before any split, leaving the straddler safely unsplit. The bug is only reachable on paths that produce dirty large folios without filesystem private data, and it was caught on the upcoming ext4 iomap buffered I/O path when no ifs is attached. Rework the contract so the caller is told the folio range to discard: - Add pgoff_t *pstart and *pend out-parameters that receive the folio range fully covered by [lstart, lend] after any split (or none), aligned inwards to min_order, i.e. the folios wholly within the range and safe to discard. - Report a reliable end position to the caller. The straddler is looked up at an index aligned inwards to the mapping minimum folio order, and *pend is set to that boundary on success. If nothing covers the boundary, discarding up to it stays safe. If the straddler is locked by someone else, fall back to folio->index. This best-effort fallback may leave the in-range sub-folios to a later pass but never discards the out-of-range tail. If the straddler cannot be split, fall back to folio2->index so the caller keeps the out-of-range tail. - Rename the byte-range parameters start/end to lstart/lend to better express their semantics. Callers in truncate_inode_pages_range() and shmem_undo_range() pass &pstart for the folio at the start edge and &pend for the folio at the end edge, so the truncate loop drops exactly the fully covered pages and never touches a straddling folio that still holds valid out-of-range data. Suggested-by: Brian Foster Link: https://lore.kernel.org/linux-fsdevel/anH-WKA1coW6wtfG@bfoster/ Fixes: 7460b470a131 ("mm/truncate: use folio_split() in truncate operation") Signed-off-by: Zhang Yi Acked-by: Zi Yan Reviewed-by: Brian Foster Reviewed-by: Jan Kara --- mm/internal.h | 4 +-- mm/shmem.c | 13 +++----- mm/truncate.c | 88 +++++++++++++++++++++++++++++++++++---------------- 3 files changed, 68 insertions(+), 37 deletions(-) diff --git a/mm/internal.h b/mm/internal.h index 38b1165212c9..3278a5e360e3 100644 --- a/mm/internal.h +++ b/mm/internal.h @@ -624,8 +624,8 @@ unsigned find_lock_entries(struct address_space *mappin= g, pgoff_t *start, unsigned find_get_entries(struct address_space *mapping, pgoff_t *start, pgoff_t end, struct folio_batch *fbatch, pgoff_t *indices); int truncate_inode_folio(struct address_space *mapping, struct folio *foli= o); -bool truncate_inode_partial_folio(struct folio *folio, loff_t start, - loff_t end); +bool truncate_inode_partial_folio(struct folio *folio, loff_t lstart, + loff_t lend, pgoff_t *pstart, pgoff_t *pend); long mapping_evict_folio(struct address_space *mapping, struct folio *foli= o); unsigned long mapping_try_invalidate(struct address_space *mapping, pgoff_t start, pgoff_t end, unsigned long *nr_failed); diff --git a/mm/shmem.c b/mm/shmem.c index 897fa2b61346..30e7df7d7309 100644 --- a/mm/shmem.c +++ b/mm/shmem.c @@ -1176,11 +1176,8 @@ static void shmem_undo_range(struct inode *inode, lo= ff_t lstart, uoff_t lend, if (folio) { same_folio =3D lend < folio_next_pos(folio); folio_mark_dirty(folio); - if (!truncate_inode_partial_folio(folio, lstart, lend)) { - start =3D folio_next_index(folio); - if (same_folio) - end =3D folio->index; - } + truncate_inode_partial_folio(folio, lstart, lend, &start, + same_folio ? &end : NULL); folio_unlock(folio); folio_put(folio); folio =3D NULL; @@ -1190,8 +1187,7 @@ static void shmem_undo_range(struct inode *inode, lof= f_t lstart, uoff_t lend, folio =3D shmem_get_partial_folio(inode, lend >> PAGE_SHIFT); if (folio) { folio_mark_dirty(folio); - if (!truncate_inode_partial_folio(folio, lstart, lend)) - end =3D folio->index; + truncate_inode_partial_folio(folio, lstart, lend, NULL, &end); folio_unlock(folio); folio_put(folio); } @@ -1259,7 +1255,8 @@ static void shmem_undo_range(struct inode *inode, lof= f_t lstart, uoff_t lend, =20 if (!folio_test_large(folio)) { truncate_inode_folio(mapping, folio); - } else if (truncate_inode_partial_folio(folio, lstart, lend)) { + } else if (truncate_inode_partial_folio(folio, + lstart, lend, NULL, NULL)) { /* * If we split a page, reset the loop so * that we pick up the new sub pages. diff --git a/mm/truncate.c b/mm/truncate.c index a8a179b38252..81fb4de6226b 100644 --- a/mm/truncate.c +++ b/mm/truncate.c @@ -206,30 +206,43 @@ static int folio_split_or_unmap(struct folio *folio, = struct page *split_at, /* * Handle partial folios. The folio may be entirely within the * range if a split has raced with us. If not, we zero the part of the - * folio that's within the [start, end] range, and then split the folio if + * folio that's within the [lstart, lend] range, and then split the folio = if * it's large. split_page_range() will discard pages which now lie beyond * i_size, and we rely on the caller to discard pages which lie within a * newly created hole. * + * When @pstart and/or @pend are non-NULL they receive the indexes of the + * folio range fully covered by [lstart, lend] after any split (or none), + * aligned inwards to min_order, i.e. the range of folios wholly within + * [lstart, lend] and so safe to discard. + * * Returns false if splitting failed so the caller can avoid * discarding the entire folio which is stubbornly unsplit. */ -bool truncate_inode_partial_folio(struct folio *folio, loff_t start, loff_= t end) +bool truncate_inode_partial_folio(struct folio *folio, loff_t lstart, + loff_t lend, pgoff_t *pstart, pgoff_t *pend) { loff_t pos =3D folio_pos(folio); size_t size =3D folio_size(folio); unsigned int offset, length; struct page *split_at, *split_at2; + unsigned long min_nrbytes; unsigned int min_order; =20 - if (pos < start) - offset =3D start - pos; + if (pos < lstart) + offset =3D lstart - pos; else offset =3D 0; - if (pos + size <=3D (u64)end) + if (pos + size <=3D (u64)lend) length =3D size - offset; else - length =3D end + 1 - pos - offset; + length =3D lend + 1 - pos - offset; + + if (pstart) + *pstart =3D offset ? folio_next_index(folio) : folio->index; + if (pend) + *pend =3D (pos + size > (u64)lend) ? folio->index : + folio_next_index(folio); =20 folio_wait_writeback(folio); if (length =3D=3D size) { @@ -251,6 +264,7 @@ bool truncate_inode_partial_folio(struct folio *folio, = loff_t start, loff_t end) return true; =20 min_order =3D mapping_min_folio_order(folio->mapping); + min_nrbytes =3D mapping_min_folio_nrbytes(folio->mapping); split_at =3D folio_page(folio, PAGE_ALIGN_DOWN(offset) / PAGE_SIZE); if (!folio_split_or_unmap(folio, split_at, min_order)) { /* @@ -259,34 +273,57 @@ bool truncate_inode_partial_folio(struct folio *folio= , loff_t start, loff_t end) * for shmem truncate */ struct folio *folio2; - pgoff_t end_idx; + pgoff_t end, aligned_end =3D round_down(pos + offset + length, + min_nrbytes) >> PAGE_SHIFT; =20 + if (pstart) + *pstart =3D round_up(pos + offset, + min_nrbytes) >> PAGE_SHIFT; + + end =3D aligned_end; if (offset + length =3D=3D size) - goto no_split; + goto out; =20 /* * After the first split at the start edge, the folio at the * end edge may be freed and reused concurrently. - * __filemap_get_folio() looks up the straddler at end_idx + * __filemap_get_folio() looks up the straddler at aligned_end * and returns it locked and ref'd with the mapping * validated. */ - end_idx =3D (pos + offset + length) >> PAGE_SHIFT; - folio2 =3D __filemap_get_folio(folio->mapping, end_idx, + folio2 =3D __filemap_get_folio(folio->mapping, aligned_end, FGP_LOCK | FGP_NOWAIT, 0); - if (IS_ERR(folio2)) - goto no_split; - - /* make sure folio2 is large */ - if (!folio_test_large(folio2)) + if (IS_ERR(folio2)) { + /* + * No sub-folio straddles the boundary when + * aligned_end is empty, so discarding up to it is + * safe. Otherwise the straddler is locked by + * someone else and we cannot obtain a reliable end + * position, so we fall back to folio->index, which + * is safe but leaves the sub-folios split off at + * the offset edge in the page cache. + */ + if (PTR_ERR(folio2) !=3D -ENOENT) + end =3D folio->index; goto out; + } =20 - split_at2 =3D folio_page(folio2, (end_idx - folio2->index)); - folio_split_or_unmap(folio2, split_at2, min_order); -out: + /* Already at the minimum order, nothing to split */ + if (folio_order(folio2) =3D=3D min_order) + goto out_put; + + split_at2 =3D folio_page(folio2, (aligned_end - folio2->index)); + + /* Split failed, keep the straddler intact */ + if (folio_split_or_unmap(folio2, split_at2, min_order)) + end =3D folio2->index; + +out_put: folio_unlock(folio2); folio_put(folio2); -no_split: +out: + if (pend) + *pend =3D end; return true; } if (folio_test_dirty(folio)) @@ -421,11 +458,8 @@ void truncate_inode_pages_range(struct address_space *= mapping, folio =3D __filemap_get_folio(mapping, lstart >> PAGE_SHIFT, FGP_LOCK, 0); if (!IS_ERR(folio)) { same_folio =3D lend < folio_next_pos(folio); - if (!truncate_inode_partial_folio(folio, lstart, lend)) { - start =3D folio_next_index(folio); - if (same_folio) - end =3D folio->index; - } + truncate_inode_partial_folio(folio, lstart, lend, &start, + same_folio ? &end : NULL); folio_unlock(folio); folio_put(folio); folio =3D NULL; @@ -435,8 +469,8 @@ void truncate_inode_pages_range(struct address_space *m= apping, folio =3D __filemap_get_folio(mapping, lend >> PAGE_SHIFT, FGP_LOCK, 0); if (!IS_ERR(folio)) { - if (!truncate_inode_partial_folio(folio, lstart, lend)) - end =3D folio->index; + truncate_inode_partial_folio(folio, lstart, lend, + NULL, &end); folio_unlock(folio); folio_put(folio); } --=20 2.52.0 From nobody Thu Sep 24 16:09:09 2026 Received: from dggsgout12.his.huawei.com (dggsgout12.his.huawei.com [45.249.212.56]) (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 674A953A897; Tue, 22 Sep 2026 11:15:25 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=45.249.212.56 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1790075727; cv=none; b=okUdL4j9/9FWftKXu/uKHlB1KbJ2vUrh4kIAOSX6FotHwXngVbDY2UVatwLSpJAV6e48AoDvRLVWRGNmGxuYWdP1PPKqMleAOT/SspBOOExsWMR1TKetIWa29rlQfCQqtJefx070thAPTdIbAdNAbcJMHEnJ9hEHrMND3Cbk40s= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1790075727; c=relaxed/simple; bh=hrnY9xXKMdQiSgF/5PKuc6Gy4OvAxvZZS5u+6YkJ3zo=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=iZJg1wv4sZZJLuTzOZQYtscluAAeD9cOcE9nKsqTFGI3fMy0P0EUlqpyWLRH+7juLFyLQxSMJGGOT0V1sPMm/NiQWPLS1hjgPYNEm7P//oijKU4FpAsPe9AH6HWjdsux71AgZUf3/snBbmnM7cO/sDNSVzU/7iqJEbsn9WTieMM= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=huaweicloud.com; spf=none smtp.mailfrom=huaweicloud.com; arc=none smtp.client-ip=45.249.212.56 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=huaweicloud.com Authentication-Results: smtp.subspace.kernel.org; spf=none smtp.mailfrom=huaweicloud.com Received: from mail.maildlp.com (unknown [172.19.163.170]) by dggsgout12.his.huawei.com (SkyGuard) with ESMTPS id 4hpyDY1S4KzKHNK9; Tue, 22 Sep 2026 19:14:53 +0800 (CST) Received: from mail02.huawei.com (unknown [10.116.40.128]) by mail.maildlp.com (Postfix) with ESMTP id 4DF924056D; Tue, 22 Sep 2026 19:15:17 +0800 (CST) Received: from huaweicloud.com (unknown [10.50.85.155]) by APP4 (Coremail) with UTF8SMTPSA id gCh0CgD3xik4Y7JqtosFBQ--.39851S8; Tue, 22 Sep 2026 19:15:16 +0800 (CST) From: Zhang Yi To: linux-mm@kvack.org Cc: linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org, linux-ext4@vger.kernel.org, akpm@linux-foundation.org, david@kernel.org, ljs@kernel.org, liam@infradead.org, vbabka@kernel.org, rppt@kernel.org, surenb@google.com, mhocko@suse.com, hughd@google.com, baolin.wang@linux.alibaba.com, willy@infradead.org, jack@suse.cz, ziy@nvidia.com, bfoster@redhat.com, joannelkoong@gmail.com, djwong@kernel.org, yi.zhang@huawei.com, yi.zhang@huaweicloud.com, yizhang089@gmail.com, yangerkun@huawei.com, chengzhihao1@huawei.com, wangkefeng.wang@huawei.com, yukuai@fnnas.com Subject: [PATCH v4 4/4] mm/truncate: clarify return value of truncate_inode_partial_folio() Date: Tue, 22 Sep 2026 19:07:03 +0800 Message-ID: <20260922110703.468389-5-yi.zhang@huaweicloud.com> X-Mailer: git-send-email 2.52.0 In-Reply-To: <20260922110703.468389-1-yi.zhang@huaweicloud.com> References: <20260922110703.468389-1-yi.zhang@huaweicloud.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-CM-TRANSID: gCh0CgD3xik4Y7JqtosFBQ--.39851S8 X-Coremail-Antispam: 1UD129KBjvJXoW7ZFW3CF4xuryfAry3XF15CFg_yoW8Kr43pa y3K3sxJ395Wa1Ikw1xuF4kZw4YyF9agrWUAFWxG3s7C3Z8Xa1UKr1Ut3WUtw4fJr1kZ340 qF1jyFW3W3WUJF7anT9S1TB71UUUUU7qnTZGkaVYY2UrUUUUjbIjqfuFe4nvWSU5nxnvy2 9KBjDU0xBIdaVrnRJUUUmq14x267AKxVWrJVCq3wAFc2x0x2IEx4CE42xK8VAvwI8IcIk0 rVWrJVCq3wAFIxvE14AKwVWUJVWUGwA2048vs2IY020E87I2jVAFwI0_JF0E3s1l82xGYI kIc2x26xkF7I0E14v26ryj6s0DM28lY4IEw2IIxxk0rwA2F7IY1VAKz4vEj48ve4kI8wA2 z4x0Y4vE2Ix0cI8IcVAFwI0_JFI_Gr1l84ACjcxK6xIIjxv20xvEc7CjxVAFwI0_Gr1j6F 4UJwA2z4x0Y4vEx4A2jsIE14v26F4UJVW0owA2z4x0Y4vEx4A2jsIEc7CjxVAFwI0_GcCE 3s1le2I262IYc4CY6c8Ij28IcVAaY2xG8wAqx4xG64xvF2IEw4CE5I8CrVC2j2WlYx0E2I x0cI8IcVAFwI0_Jr0_Jr4lYx0Ex4A2jsIE14v26r1j6r4UMcvjeVCFs4IE7xkEbVWUJVW8 JwACjcxG0xvY0x0EwIxGrwACjI8F5VA0II8E6IAqYI8I648v4I1lFIxGxcIEc7CjxVA2Y2 ka0xkIwI1lc7CjxVAaw2AFwI0_GFv_Wryl42xK82IYc2Ij64vIr41l4I8I3I0E4IkC6x0Y z7v_Jr0_Gr1lx2IqxVAqx4xG67AKxVWUJVWUGwC20s026x8GjcxK67AKxVWUGVWUWwC2zV AF1VAY17CE14v26r4a6rW5MIIYrxkI7VAKI48JMIIF0xvE2Ix0cI8IcVAFwI0_Jr0_JF4l IxAIcVC0I7IYx2IY6xkF7I0E14v26r4UJVWxJr1lIxAIcVCF04k26cxKx2IYs7xG6r1j6r 1xMIIF0xvEx4A2jsIE14v26r1j6r4UMIIF0xvEx4A2jsIEc7CjxVAFwI0_Gr1j6F4UJbIY CTnIWIevJa73UjIFyTuYvjTRNdb1DUUUU X-CM-SenderInfo: d1lo6xhdqjqx5xdzvxpfor3voofrz/ Content-Type: text/plain; charset="utf-8" From: Zhang Yi With the earlier rework the callers no longer rely on the return value of truncate_inode_partial_folio() to decide whether to adjust the truncation range. The pstart/pend out-parameters carry that information instead. The callers now only use the return value as a flag indicating whether the loop should be reset to pick up newly split sub-folios on the shmem path. Return true if at least one split succeeded, and false otherwise. This clarifies the existing confusing return value semantics. Signed-off-by: Zhang Yi Acked-by: Zi Yan Reviewed-by: Brian Foster Reviewed-by: Jan Kara --- mm/truncate.c | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/mm/truncate.c b/mm/truncate.c index 81fb4de6226b..317cb4ae3625 100644 --- a/mm/truncate.c +++ b/mm/truncate.c @@ -216,8 +216,7 @@ static int folio_split_or_unmap(struct folio *folio, st= ruct page *split_at, * aligned inwards to min_order, i.e. the range of folios wholly within * [lstart, lend] and so safe to discard. * - * Returns false if splitting failed so the caller can avoid - * discarding the entire folio which is stubbornly unsplit. + * Return %true if at least one split succeeded, %false otherwise. */ bool truncate_inode_partial_folio(struct folio *folio, loff_t lstart, loff_t lend, pgoff_t *pstart, pgoff_t *pend) @@ -247,7 +246,7 @@ bool truncate_inode_partial_folio(struct folio *folio, = loff_t lstart, folio_wait_writeback(folio); if (length =3D=3D size) { truncate_inode_folio(folio->mapping, folio); - return true; + return false; } =20 /* @@ -261,7 +260,7 @@ bool truncate_inode_partial_folio(struct folio *folio, = loff_t lstart, if (folio_needs_release(folio)) folio_invalidate(folio, offset, length); if (!folio_test_large(folio)) - return true; + return false; =20 min_order =3D mapping_min_folio_order(folio->mapping); min_nrbytes =3D mapping_min_folio_nrbytes(folio->mapping); @@ -326,10 +325,9 @@ bool truncate_inode_partial_folio(struct folio *folio,= loff_t lstart, *pend =3D end; return true; } - if (folio_test_dirty(folio)) - return false; - truncate_inode_folio(folio->mapping, folio); - return true; + if (!folio_test_dirty(folio)) + truncate_inode_folio(folio->mapping, folio); + return false; } =20 /* --=20 2.52.0