Linux filesystem development
 help / color / mirror / Atom feed
* [PATCH v2 0/5] iomap: trivial fixes for ext4 conversion
@ 2026-05-20  3:03 Zhang Yi
  2026-05-20  3:03 ` [PATCH v2 1/5] iomap: correct the range of a partial dirty clear Zhang Yi
                   ` (4 more replies)
  0 siblings, 5 replies; 14+ messages in thread
From: Zhang Yi @ 2026-05-20  3:03 UTC (permalink / raw)
  To: linux-fsdevel, linux-xfs
  Cc: linux-ext4, brauner, djwong, hch, joannelkoong, yi.zhang,
	yi.zhang, yizhang089, yangerkun, yukuai

Changes since v1:
 - Add fix tags to patch 01 and 04.
 - In patch 04, change ifs_set_range_uptodate() to always fall through
   to ifs_is_fully_uptodate(), preventing a false-positive uptodate
   mask.
 - Add patch 05, add comments for ifs_clear/set_range_dirty().

v1: https://lore.kernel.org/linux-fsdevel/20260514062955.1183976-1-yi.zhang@huaweicloud.com/


Original Cover-letter:

This patch series contains a few trivial iomap-related fixes in
preparation for converting ext4 buffered I/O to use iomap. 

The first three patches are taken from my ext4 conversion series [1], as
suggested by Christoph. The last patch fixes a bug originally reported
by Sashiko during review of my series; although unrelated to the ext4
conversion, it is worth fixing on its own. Please see the following
patches for detail.

Thanks,
Yi.

[1] https://lore.kernel.org/linux-ext4/20260511072344.191271-1-yi.zhang@huaweicloud.com/

Zhang Yi (5):
  iomap: correct the range of a partial dirty clear
  iomap: support invalidating partial folios
  iomap: fix incorrect did_zero setting in iomap_zero_iter()
  iomap: fix out-of-bounds bitmap_set() with zero-length range
  iomap: add comments for ifs_clear/set_range_dirty()

 fs/iomap/buffered-io.c | 58 ++++++++++++++++++++++++++++++++----------
 1 file changed, 44 insertions(+), 14 deletions(-)

-- 
2.52.0


^ permalink raw reply	[flat|nested] 14+ messages in thread

* [PATCH v2 1/5] iomap: correct the range of a partial dirty clear
  2026-05-20  3:03 [PATCH v2 0/5] iomap: trivial fixes for ext4 conversion Zhang Yi
@ 2026-05-20  3:03 ` Zhang Yi
  2026-07-08 20:57   ` Joanne Koong
  2026-07-08 21:40   ` Darrick J. Wong
  2026-05-20  3:03 ` [PATCH v2 2/5] iomap: support invalidating partial folios Zhang Yi
                   ` (3 subsequent siblings)
  4 siblings, 2 replies; 14+ messages in thread
From: Zhang Yi @ 2026-05-20  3:03 UTC (permalink / raw)
  To: linux-fsdevel, linux-xfs
  Cc: linux-ext4, brauner, djwong, hch, joannelkoong, yi.zhang,
	yi.zhang, yizhang089, yangerkun, yukuai

From: Zhang Yi <yi.zhang@huawei.com>

The block range calculation in ifs_clear_range_dirty() is incorrect when
partially clearing a range in a folio. We cannot clear the dirty bit of
the first block or the last block if the start or end offset is not
blocksize-aligned. This has not yet caused any issues since we always
clear a whole folio in iomap_writeback_folio().

Fix this by rounding up the first block to blocksize alignment, and
calculate the last block by rounding down (using truncation). Correct
the nr_blks calculation accordingly.

Fixes: 4ce02c679722 ("iomap: Add per-block dirty state tracking to improve performance")
Cc: <stable@vger.kernel.org> # v6.6
Signed-off-by: Zhang Yi <yi.zhang@huawei.com>
---
 fs/iomap/buffered-io.c | 12 ++++++++----
 1 file changed, 8 insertions(+), 4 deletions(-)

diff --git a/fs/iomap/buffered-io.c b/fs/iomap/buffered-io.c
index d7b648421a70..64351a448a8b 100644
--- a/fs/iomap/buffered-io.c
+++ b/fs/iomap/buffered-io.c
@@ -176,13 +176,17 @@ static void ifs_clear_range_dirty(struct folio *folio,
 {
 	struct inode *inode = folio->mapping->host;
 	unsigned int blks_per_folio = i_blocks_per_folio(inode, folio);
-	unsigned int first_blk = (off >> inode->i_blkbits);
-	unsigned int last_blk = (off + len - 1) >> inode->i_blkbits;
-	unsigned int nr_blks = last_blk - first_blk + 1;
+	unsigned int first_blk = round_up(off, i_blocksize(inode)) >>
+				 inode->i_blkbits;
+	unsigned int last_blk = (off + len) >> inode->i_blkbits;
 	unsigned long flags;
 
+	if (first_blk >= last_blk)
+		return;
+
 	spin_lock_irqsave(&ifs->state_lock, flags);
-	bitmap_clear(ifs->state, first_blk + blks_per_folio, nr_blks);
+	bitmap_clear(ifs->state, first_blk + blks_per_folio,
+		     last_blk - first_blk);
 	spin_unlock_irqrestore(&ifs->state_lock, flags);
 }
 
-- 
2.52.0


^ permalink raw reply related	[flat|nested] 14+ messages in thread

* [PATCH v2 2/5] iomap: support invalidating partial folios
  2026-05-20  3:03 [PATCH v2 0/5] iomap: trivial fixes for ext4 conversion Zhang Yi
  2026-05-20  3:03 ` [PATCH v2 1/5] iomap: correct the range of a partial dirty clear Zhang Yi
@ 2026-05-20  3:03 ` Zhang Yi
  2026-07-08 21:30   ` Joanne Koong
  2026-05-20  3:03 ` [PATCH v2 3/5] iomap: fix incorrect did_zero setting in iomap_zero_iter() Zhang Yi
                   ` (2 subsequent siblings)
  4 siblings, 1 reply; 14+ messages in thread
From: Zhang Yi @ 2026-05-20  3:03 UTC (permalink / raw)
  To: linux-fsdevel, linux-xfs
  Cc: linux-ext4, brauner, djwong, hch, joannelkoong, yi.zhang,
	yi.zhang, yizhang089, yangerkun, yukuai

From: Zhang Yi <yi.zhang@huawei.com>

Current iomap_invalidate_folio() can only invalidate an entire folio. If
we truncate a partial folio on a filesystem where the block size is
smaller than the folio size, it will leave behind dirty bits for the
truncated or punched blocks. During the write-back process, it will
attempt to map the invalid hole range. Fortunately, this has not caused
any real problems so far because the ->writeback_range() function
corrects the length.

However, the implementation of FALLOC_FL_ZERO_RANGE in ext4 depends on
the support for invalidating partial folios. When ext4 partially zeroes
out a dirty and unwritten folio, it does not perform a flush first like
XFS. Therefore, if the dirty bits of the corresponding area cannot be
cleared, the zeroed area after writeback remains in the written state
rather than reverting to the unwritten state. Fix this by supporting
invalidation of partial folios.

Signed-off-by: Zhang Yi <yi.zhang@huawei.com>
Reviewed-by: Darrick J. Wong <djwong@kernel.org>
---
 fs/iomap/buffered-io.c | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/fs/iomap/buffered-io.c b/fs/iomap/buffered-io.c
index 64351a448a8b..876c2f507f58 100644
--- a/fs/iomap/buffered-io.c
+++ b/fs/iomap/buffered-io.c
@@ -761,6 +761,8 @@ void iomap_invalidate_folio(struct folio *folio, size_t offset, size_t len)
 		WARN_ON_ONCE(folio_test_writeback(folio));
 		folio_cancel_dirty(folio);
 		ifs_free(folio);
+	} else {
+		iomap_clear_range_dirty(folio, offset, len);
 	}
 }
 EXPORT_SYMBOL_GPL(iomap_invalidate_folio);
-- 
2.52.0


^ permalink raw reply related	[flat|nested] 14+ messages in thread

* [PATCH v2 3/5] iomap: fix incorrect did_zero setting in iomap_zero_iter()
  2026-05-20  3:03 [PATCH v2 0/5] iomap: trivial fixes for ext4 conversion Zhang Yi
  2026-05-20  3:03 ` [PATCH v2 1/5] iomap: correct the range of a partial dirty clear Zhang Yi
  2026-05-20  3:03 ` [PATCH v2 2/5] iomap: support invalidating partial folios Zhang Yi
@ 2026-05-20  3:03 ` Zhang Yi
  2026-05-20  3:03 ` [PATCH v2 4/5] iomap: fix out-of-bounds bitmap_set() with zero-length range Zhang Yi
  2026-05-20  3:03 ` [PATCH v2 5/5] iomap: add comments for ifs_clear/set_range_dirty() Zhang Yi
  4 siblings, 0 replies; 14+ messages in thread
From: Zhang Yi @ 2026-05-20  3:03 UTC (permalink / raw)
  To: linux-fsdevel, linux-xfs
  Cc: linux-ext4, brauner, djwong, hch, joannelkoong, yi.zhang,
	yi.zhang, yizhang089, yangerkun, yukuai

From: Zhang Yi <yi.zhang@huawei.com>

The did_zero output parameter was unconditionally set after the loop,
which is incorrect. It should only be set when the zeroing operation
actually completes, not when IOMAP_F_STALE is set or when
IOMAP_F_FOLIO_BATCH is set but !folio causes the loop to break early,
or when iomap_iter_advance() returns an error.

This causes did_zero to be incorrectly set when zeroing a clean
unwritten extent because the loop exits early without actually zeroing
any data.

Fix it by using a local variable to track whether any folio was actually
zeroed, and only set did_zero after the loop if zeroing happened.

Signed-off-by: Zhang Yi <yi.zhang@huawei.com>
Reviewed-by: "Darrick J. Wong" <djwong@kernel.org>
---
 fs/iomap/buffered-io.c | 8 ++++++--
 1 file changed, 6 insertions(+), 2 deletions(-)

diff --git a/fs/iomap/buffered-io.c b/fs/iomap/buffered-io.c
index 876c2f507f58..27ab33edbdee 100644
--- a/fs/iomap/buffered-io.c
+++ b/fs/iomap/buffered-io.c
@@ -1542,6 +1542,7 @@ static int iomap_zero_iter(struct iomap_iter *iter, bool *did_zero,
 		const struct iomap_write_ops *write_ops)
 {
 	u64 bytes = iomap_length(iter);
+	bool zeroed = false;
 	int status;
 
 	do {
@@ -1560,6 +1561,8 @@ static int iomap_zero_iter(struct iomap_iter *iter, bool *did_zero,
 		/* a NULL folio means we're done with a folio batch */
 		if (!folio) {
 			status = iomap_iter_advance_full(iter);
+			if (status)
+				return status;
 			break;
 		}
 
@@ -1570,6 +1573,7 @@ static int iomap_zero_iter(struct iomap_iter *iter, bool *did_zero,
 				bytes);
 
 		folio_zero_range(folio, offset, bytes);
+		zeroed = true;
 		folio_mark_accessed(folio);
 
 		ret = iomap_write_end(iter, bytes, bytes, folio);
@@ -1579,10 +1583,10 @@ static int iomap_zero_iter(struct iomap_iter *iter, bool *did_zero,
 
 		status = iomap_iter_advance(iter, bytes);
 		if (status)
-			break;
+			return status;
 	} while ((bytes = iomap_length(iter)) > 0);
 
-	if (did_zero)
+	if (did_zero && zeroed)
 		*did_zero = true;
 	return status;
 }
-- 
2.52.0


^ permalink raw reply related	[flat|nested] 14+ messages in thread

* [PATCH v2 4/5] iomap: fix out-of-bounds bitmap_set() with zero-length range
  2026-05-20  3:03 [PATCH v2 0/5] iomap: trivial fixes for ext4 conversion Zhang Yi
                   ` (2 preceding siblings ...)
  2026-05-20  3:03 ` [PATCH v2 3/5] iomap: fix incorrect did_zero setting in iomap_zero_iter() Zhang Yi
@ 2026-05-20  3:03 ` Zhang Yi
  2026-07-08 20:40   ` Joanne Koong
  2026-07-08 23:26   ` Darrick J. Wong
  2026-05-20  3:03 ` [PATCH v2 5/5] iomap: add comments for ifs_clear/set_range_dirty() Zhang Yi
  4 siblings, 2 replies; 14+ messages in thread
From: Zhang Yi @ 2026-05-20  3:03 UTC (permalink / raw)
  To: linux-fsdevel, linux-xfs
  Cc: linux-ext4, brauner, djwong, hch, joannelkoong, yi.zhang,
	yi.zhang, yizhang089, yangerkun, yukuai

From: Zhang Yi <yi.zhang@huawei.com>

ifs_set_range_dirty() and ifs_set_range_uptodate() compute last_blk
as (off + len - 1) >> i_blkbits.  When off is 0 and len is 0, the
unsigned subtraction underflows to SIZE_MAX, producing a huge
last_blk and nr_blks value that causes bitmap_set() to write far
beyond the ifs->state allocation.

Regarding ifs_set_range_uptodate(), it is temporarily safe because len
cannot be passed in as 0. However, for ifs_set_range_dirty() this is
reachable from __iomap_write_end(): when copy_folio_from_iter_atomic()
returns 0 (e.g. user buffer fault) and the folio is already uptodate,
the guard at the top of __iomap_write_end() does not trigger because
!folio_test_uptodate() is false, and iomap_set_range_dirty() is called
with copied == 0.

Add a !len guard to both functions before the computation, so that a
zero-length range is a no-op.

Fixes: 4ce02c679722 ("iomap: Add per-block dirty state tracking to improve performance")
Cc: <stable@vger.kernel.org> # v6.6
Signed-off-by: Zhang Yi <yi.zhang@huawei.com>
---
 fs/iomap/buffered-io.c | 22 ++++++++++++++--------
 1 file changed, 14 insertions(+), 8 deletions(-)

diff --git a/fs/iomap/buffered-io.c b/fs/iomap/buffered-io.c
index 27ab33edbdee..76f9a43e283c 100644
--- a/fs/iomap/buffered-io.c
+++ b/fs/iomap/buffered-io.c
@@ -67,11 +67,13 @@ static bool ifs_set_range_uptodate(struct folio *folio,
 		struct iomap_folio_state *ifs, size_t off, size_t len)
 {
 	struct inode *inode = folio->mapping->host;
-	unsigned int first_blk = off >> inode->i_blkbits;
-	unsigned int last_blk = (off + len - 1) >> inode->i_blkbits;
-	unsigned int nr_blks = last_blk - first_blk + 1;
+	unsigned int first_blk, last_blk;
 
-	bitmap_set(ifs->state, first_blk, nr_blks);
+	if (len) {
+		first_blk = off >> inode->i_blkbits;
+		last_blk = (off + len - 1) >> inode->i_blkbits;
+		bitmap_set(ifs->state, first_blk, last_blk - first_blk + 1);
+	}
 	return ifs_is_fully_uptodate(folio, ifs);
 }
 
@@ -203,13 +205,17 @@ static void ifs_set_range_dirty(struct folio *folio,
 {
 	struct inode *inode = folio->mapping->host;
 	unsigned int blks_per_folio = i_blocks_per_folio(inode, folio);
-	unsigned int first_blk = (off >> inode->i_blkbits);
-	unsigned int last_blk = (off + len - 1) >> inode->i_blkbits;
-	unsigned int nr_blks = last_blk - first_blk + 1;
+	unsigned int first_blk, last_blk;
 	unsigned long flags;
 
+	if (!len)
+		return;
+
+	first_blk = off >> inode->i_blkbits;
+	last_blk = (off + len - 1) >> inode->i_blkbits;
 	spin_lock_irqsave(&ifs->state_lock, flags);
-	bitmap_set(ifs->state, first_blk + blks_per_folio, nr_blks);
+	bitmap_set(ifs->state, first_blk + blks_per_folio,
+		   last_blk - first_blk + 1);
 	spin_unlock_irqrestore(&ifs->state_lock, flags);
 }
 
-- 
2.52.0


^ permalink raw reply related	[flat|nested] 14+ messages in thread

* [PATCH v2 5/5] iomap: add comments for ifs_clear/set_range_dirty()
  2026-05-20  3:03 [PATCH v2 0/5] iomap: trivial fixes for ext4 conversion Zhang Yi
                   ` (3 preceding siblings ...)
  2026-05-20  3:03 ` [PATCH v2 4/5] iomap: fix out-of-bounds bitmap_set() with zero-length range Zhang Yi
@ 2026-05-20  3:03 ` Zhang Yi
  2026-07-08 20:49   ` Joanne Koong
  4 siblings, 1 reply; 14+ messages in thread
From: Zhang Yi @ 2026-05-20  3:03 UTC (permalink / raw)
  To: linux-fsdevel, linux-xfs
  Cc: linux-ext4, brauner, djwong, hch, joannelkoong, yi.zhang,
	yi.zhang, yizhang089, yangerkun, yukuai

From: Zhang Yi <yi.zhang@huawei.com>

The range alignment strategy differs between ifs_clear_range_dirty() and
ifs_set_range_dirty(). The former rounds inwards to clear only
fully-covered blocks, while the latter rounds outwards to mark any
partially-touched block as dirty. Add comments to document this
asymmetry in block range calculation.

Suggested-by: "Darrick J. Wong" <djwong@kernel.org>
Signed-off-by: Zhang Yi <yi.zhang@huawei.com>
---
 fs/iomap/buffered-io.c | 14 ++++++++++++++
 1 file changed, 14 insertions(+)

diff --git a/fs/iomap/buffered-io.c b/fs/iomap/buffered-io.c
index 76f9a43e283c..b1d917504d5d 100644
--- a/fs/iomap/buffered-io.c
+++ b/fs/iomap/buffered-io.c
@@ -173,6 +173,13 @@ static unsigned iomap_find_dirty_range(struct folio *folio, u64 *range_start,
 	return range_end - *range_start;
 }
 
+/*
+ * Clear the per-block dirty bits for the range [@off, @off + @len) within a
+ * folio.  The range is rounded inwards so that only blocks fully covered by
+ * the range are cleared.  This is required for operations like folio
+ * invalidation, where we must ensure a block is fully clean before discarding
+ * it.
+ */
 static void ifs_clear_range_dirty(struct folio *folio,
 		struct iomap_folio_state *ifs, size_t off, size_t len)
 {
@@ -200,6 +207,13 @@ static void iomap_clear_range_dirty(struct folio *folio, size_t off, size_t len)
 		ifs_clear_range_dirty(folio, ifs, off, len);
 }
 
+/*
+ * Set the per-block dirty bits for the range [@off, @off + @len) within a
+ * folio.  The range is rounded outwards so that any block partially touched
+ * by the range is marked dirty.  This ensures blocks containing even a
+ * single dirty byte will be included in subsequent writeback, preventing
+ * data loss when partial blocks are written.
+ */
 static void ifs_set_range_dirty(struct folio *folio,
 		struct iomap_folio_state *ifs, size_t off, size_t len)
 {
-- 
2.52.0


^ permalink raw reply related	[flat|nested] 14+ messages in thread

* Re: [PATCH v2 4/5] iomap: fix out-of-bounds bitmap_set() with zero-length range
  2026-05-20  3:03 ` [PATCH v2 4/5] iomap: fix out-of-bounds bitmap_set() with zero-length range Zhang Yi
@ 2026-07-08 20:40   ` Joanne Koong
  2026-07-08 23:26   ` Darrick J. Wong
  1 sibling, 0 replies; 14+ messages in thread
From: Joanne Koong @ 2026-07-08 20:40 UTC (permalink / raw)
  To: Zhang Yi
  Cc: linux-fsdevel, linux-xfs, linux-ext4, brauner, djwong, hch,
	yi.zhang, yizhang089, yangerkun, yukuai

On Tue, May 19, 2026 at 8:14 PM Zhang Yi <yi.zhang@huaweicloud.com> wrote:
>
> From: Zhang Yi <yi.zhang@huawei.com>
>
> ifs_set_range_dirty() and ifs_set_range_uptodate() compute last_blk
> as (off + len - 1) >> i_blkbits.  When off is 0 and len is 0, the
> unsigned subtraction underflows to SIZE_MAX, producing a huge
> last_blk and nr_blks value that causes bitmap_set() to write far
> beyond the ifs->state allocation.
>
> Regarding ifs_set_range_uptodate(), it is temporarily safe because len
> cannot be passed in as 0. However, for ifs_set_range_dirty() this is
> reachable from __iomap_write_end(): when copy_folio_from_iter_atomic()
> returns 0 (e.g. user buffer fault) and the folio is already uptodate,
> the guard at the top of __iomap_write_end() does not trigger because
> !folio_test_uptodate() is false, and iomap_set_range_dirty() is called
> with copied == 0.
>
> Add a !len guard to both functions before the computation, so that a
> zero-length range is a no-op.
>
> Fixes: 4ce02c679722 ("iomap: Add per-block dirty state tracking to improve performance")
> Cc: <stable@vger.kernel.org> # v6.6
> Signed-off-by: Zhang Yi <yi.zhang@huawei.com>

Reviewed-by: Joanne Koong <joannelkoong@gmail.com>

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH v2 5/5] iomap: add comments for ifs_clear/set_range_dirty()
  2026-05-20  3:03 ` [PATCH v2 5/5] iomap: add comments for ifs_clear/set_range_dirty() Zhang Yi
@ 2026-07-08 20:49   ` Joanne Koong
  0 siblings, 0 replies; 14+ messages in thread
From: Joanne Koong @ 2026-07-08 20:49 UTC (permalink / raw)
  To: Zhang Yi
  Cc: linux-fsdevel, linux-xfs, linux-ext4, brauner, djwong, hch,
	yi.zhang, yizhang089, yangerkun, yukuai

On Tue, May 19, 2026 at 8:15 PM Zhang Yi <yi.zhang@huaweicloud.com> wrote:
>
> From: Zhang Yi <yi.zhang@huawei.com>
>
> The range alignment strategy differs between ifs_clear_range_dirty() and
> ifs_set_range_dirty(). The former rounds inwards to clear only
> fully-covered blocks, while the latter rounds outwards to mark any
> partially-touched block as dirty. Add comments to document this
> asymmetry in block range calculation.
>
> Suggested-by: "Darrick J. Wong" <djwong@kernel.org>
> Signed-off-by: Zhang Yi <yi.zhang@huawei.com>

Reviewed-by: Joanne Koong <joannelkoong@gmail.com>

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH v2 1/5] iomap: correct the range of a partial dirty clear
  2026-05-20  3:03 ` [PATCH v2 1/5] iomap: correct the range of a partial dirty clear Zhang Yi
@ 2026-07-08 20:57   ` Joanne Koong
  2026-07-09  2:36     ` Zhang Yi
  2026-07-08 21:40   ` Darrick J. Wong
  1 sibling, 1 reply; 14+ messages in thread
From: Joanne Koong @ 2026-07-08 20:57 UTC (permalink / raw)
  To: Zhang Yi
  Cc: linux-fsdevel, linux-xfs, linux-ext4, brauner, djwong, hch,
	yi.zhang, yizhang089, yangerkun, yukuai

On Tue, May 19, 2026 at 8:09 PM Zhang Yi <yi.zhang@huaweicloud.com> wrote:
>
> From: Zhang Yi <yi.zhang@huawei.com>
>
> The block range calculation in ifs_clear_range_dirty() is incorrect when
> partially clearing a range in a folio. We cannot clear the dirty bit of
> the first block or the last block if the start or end offset is not
> blocksize-aligned. This has not yet caused any issues since we always
> clear a whole folio in iomap_writeback_folio().

If this isn't currently reachable, does this still need the stable@
tag? My understanding from the stable document [1] is that "it must
either fix a real bug that bothers people or just add a device ID"?
[1]

>
> Fix this by rounding up the first block to blocksize alignment, and
> calculate the last block by rounding down (using truncation). Correct
> the nr_blks calculation accordingly.
>
> Fixes: 4ce02c679722 ("iomap: Add per-block dirty state tracking to improve performance")
> Cc: <stable@vger.kernel.org> # v6.6
> Signed-off-by: Zhang Yi <yi.zhang@huawei.com>

Reviewed-by: Joanne Koong <joannelkoong@gmail.com>

Thanks,
Joanne

[1] https://www.kernel.org/doc/Documentation/process/stable-kernel-rules.rst

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH v2 2/5] iomap: support invalidating partial folios
  2026-05-20  3:03 ` [PATCH v2 2/5] iomap: support invalidating partial folios Zhang Yi
@ 2026-07-08 21:30   ` Joanne Koong
  0 siblings, 0 replies; 14+ messages in thread
From: Joanne Koong @ 2026-07-08 21:30 UTC (permalink / raw)
  To: Zhang Yi
  Cc: linux-fsdevel, linux-xfs, linux-ext4, brauner, djwong, hch,
	yi.zhang, yizhang089, yangerkun, yukuai

On Tue, May 19, 2026 at 8:09 PM Zhang Yi <yi.zhang@huaweicloud.com> wrote:
>
> From: Zhang Yi <yi.zhang@huawei.com>
>
> Current iomap_invalidate_folio() can only invalidate an entire folio. If
> we truncate a partial folio on a filesystem where the block size is
> smaller than the folio size, it will leave behind dirty bits for the
> truncated or punched blocks. During the write-back process, it will
> attempt to map the invalid hole range. Fortunately, this has not caused
> any real problems so far because the ->writeback_range() function
> corrects the length.
>
> However, the implementation of FALLOC_FL_ZERO_RANGE in ext4 depends on
> the support for invalidating partial folios. When ext4 partially zeroes
> out a dirty and unwritten folio, it does not perform a flush first like
> XFS. Therefore, if the dirty bits of the corresponding area cannot be
> cleared, the zeroed area after writeback remains in the written state
> rather than reverting to the unwritten state. Fix this by supporting
> invalidation of partial folios.
>
> Signed-off-by: Zhang Yi <yi.zhang@huawei.com>
> Reviewed-by: Darrick J. Wong <djwong@kernel.org>
> ---
>  fs/iomap/buffered-io.c | 2 ++
>  1 file changed, 2 insertions(+)
>
> diff --git a/fs/iomap/buffered-io.c b/fs/iomap/buffered-io.c
> index 64351a448a8b..876c2f507f58 100644
> --- a/fs/iomap/buffered-io.c
> +++ b/fs/iomap/buffered-io.c
> @@ -761,6 +761,8 @@ void iomap_invalidate_folio(struct folio *folio, size_t offset, size_t len)
>                 WARN_ON_ONCE(folio_test_writeback(folio));
>                 folio_cancel_dirty(folio);
>                 ifs_free(folio);
> +       } else {
> +               iomap_clear_range_dirty(folio, offset, len);
>         }
>  }
>  EXPORT_SYMBOL_GPL(iomap_invalidate_folio);
> --
> 2.52.0
>
>

Reviewed-by: Joanne Koong <joannelkoong@gmail.com>

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH v2 1/5] iomap: correct the range of a partial dirty clear
  2026-05-20  3:03 ` [PATCH v2 1/5] iomap: correct the range of a partial dirty clear Zhang Yi
  2026-07-08 20:57   ` Joanne Koong
@ 2026-07-08 21:40   ` Darrick J. Wong
  2026-07-09  2:47     ` Zhang Yi
  1 sibling, 1 reply; 14+ messages in thread
From: Darrick J. Wong @ 2026-07-08 21:40 UTC (permalink / raw)
  To: Zhang Yi
  Cc: linux-fsdevel, linux-xfs, linux-ext4, brauner, hch, joannelkoong,
	yi.zhang, yizhang089, yangerkun, yukuai

On Wed, May 20, 2026 at 11:03:53AM +0800, Zhang Yi wrote:
> From: Zhang Yi <yi.zhang@huawei.com>
> 
> The block range calculation in ifs_clear_range_dirty() is incorrect when
> partially clearing a range in a folio. We cannot clear the dirty bit of
> the first block or the last block if the start or end offset is not
> blocksize-aligned. This has not yet caused any issues since we always
> clear a whole folio in iomap_writeback_folio().
> 
> Fix this by rounding up the first block to blocksize alignment, and
> calculate the last block by rounding down (using truncation). Correct
> the nr_blks calculation accordingly.
> 
> Fixes: 4ce02c679722 ("iomap: Add per-block dirty state tracking to improve performance")
> Cc: <stable@vger.kernel.org> # v6.6
> Signed-off-by: Zhang Yi <yi.zhang@huawei.com>

My apologies for the unnecessary delay, I was on vacation when this came
through and I never saw it. :/

Reviewed-by: "Darrick J. Wong" <djwong@kernel.org>

--D

> ---
>  fs/iomap/buffered-io.c | 12 ++++++++----
>  1 file changed, 8 insertions(+), 4 deletions(-)
> 
> diff --git a/fs/iomap/buffered-io.c b/fs/iomap/buffered-io.c
> index d7b648421a70..64351a448a8b 100644
> --- a/fs/iomap/buffered-io.c
> +++ b/fs/iomap/buffered-io.c
> @@ -176,13 +176,17 @@ static void ifs_clear_range_dirty(struct folio *folio,
>  {
>  	struct inode *inode = folio->mapping->host;
>  	unsigned int blks_per_folio = i_blocks_per_folio(inode, folio);
> -	unsigned int first_blk = (off >> inode->i_blkbits);
> -	unsigned int last_blk = (off + len - 1) >> inode->i_blkbits;
> -	unsigned int nr_blks = last_blk - first_blk + 1;
> +	unsigned int first_blk = round_up(off, i_blocksize(inode)) >>
> +				 inode->i_blkbits;
> +	unsigned int last_blk = (off + len) >> inode->i_blkbits;
>  	unsigned long flags;
>  
> +	if (first_blk >= last_blk)
> +		return;
> +
>  	spin_lock_irqsave(&ifs->state_lock, flags);
> -	bitmap_clear(ifs->state, first_blk + blks_per_folio, nr_blks);
> +	bitmap_clear(ifs->state, first_blk + blks_per_folio,
> +		     last_blk - first_blk);
>  	spin_unlock_irqrestore(&ifs->state_lock, flags);
>  }
>  
> -- 
> 2.52.0
> 
> 

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH v2 4/5] iomap: fix out-of-bounds bitmap_set() with zero-length range
  2026-05-20  3:03 ` [PATCH v2 4/5] iomap: fix out-of-bounds bitmap_set() with zero-length range Zhang Yi
  2026-07-08 20:40   ` Joanne Koong
@ 2026-07-08 23:26   ` Darrick J. Wong
  1 sibling, 0 replies; 14+ messages in thread
From: Darrick J. Wong @ 2026-07-08 23:26 UTC (permalink / raw)
  To: Zhang Yi
  Cc: linux-fsdevel, linux-xfs, linux-ext4, brauner, hch, joannelkoong,
	yi.zhang, yizhang089, yangerkun, yukuai

On Wed, May 20, 2026 at 11:03:56AM +0800, Zhang Yi wrote:
> From: Zhang Yi <yi.zhang@huawei.com>
> 
> ifs_set_range_dirty() and ifs_set_range_uptodate() compute last_blk
> as (off + len - 1) >> i_blkbits.  When off is 0 and len is 0, the
> unsigned subtraction underflows to SIZE_MAX, producing a huge
> last_blk and nr_blks value that causes bitmap_set() to write far
> beyond the ifs->state allocation.
> 
> Regarding ifs_set_range_uptodate(), it is temporarily safe because len
> cannot be passed in as 0. However, for ifs_set_range_dirty() this is
> reachable from __iomap_write_end(): when copy_folio_from_iter_atomic()
> returns 0 (e.g. user buffer fault) and the folio is already uptodate,
> the guard at the top of __iomap_write_end() does not trigger because
> !folio_test_uptodate() is false, and iomap_set_range_dirty() is called
> with copied == 0.
> 
> Add a !len guard to both functions before the computation, so that a
> zero-length range is a no-op.
> 
> Fixes: 4ce02c679722 ("iomap: Add per-block dirty state tracking to improve performance")
> Cc: <stable@vger.kernel.org> # v6.6
> Signed-off-by: Zhang Yi <yi.zhang@huawei.com>

Looks good!
Reviewed-by: "Darrick J. Wong" <djwong@kernel.org>

--D

> ---
>  fs/iomap/buffered-io.c | 22 ++++++++++++++--------
>  1 file changed, 14 insertions(+), 8 deletions(-)
> 
> diff --git a/fs/iomap/buffered-io.c b/fs/iomap/buffered-io.c
> index 27ab33edbdee..76f9a43e283c 100644
> --- a/fs/iomap/buffered-io.c
> +++ b/fs/iomap/buffered-io.c
> @@ -67,11 +67,13 @@ static bool ifs_set_range_uptodate(struct folio *folio,
>  		struct iomap_folio_state *ifs, size_t off, size_t len)
>  {
>  	struct inode *inode = folio->mapping->host;
> -	unsigned int first_blk = off >> inode->i_blkbits;
> -	unsigned int last_blk = (off + len - 1) >> inode->i_blkbits;
> -	unsigned int nr_blks = last_blk - first_blk + 1;
> +	unsigned int first_blk, last_blk;
>  
> -	bitmap_set(ifs->state, first_blk, nr_blks);
> +	if (len) {
> +		first_blk = off >> inode->i_blkbits;
> +		last_blk = (off + len - 1) >> inode->i_blkbits;
> +		bitmap_set(ifs->state, first_blk, last_blk - first_blk + 1);
> +	}
>  	return ifs_is_fully_uptodate(folio, ifs);
>  }
>  
> @@ -203,13 +205,17 @@ static void ifs_set_range_dirty(struct folio *folio,
>  {
>  	struct inode *inode = folio->mapping->host;
>  	unsigned int blks_per_folio = i_blocks_per_folio(inode, folio);
> -	unsigned int first_blk = (off >> inode->i_blkbits);
> -	unsigned int last_blk = (off + len - 1) >> inode->i_blkbits;
> -	unsigned int nr_blks = last_blk - first_blk + 1;
> +	unsigned int first_blk, last_blk;
>  	unsigned long flags;
>  
> +	if (!len)
> +		return;
> +
> +	first_blk = off >> inode->i_blkbits;
> +	last_blk = (off + len - 1) >> inode->i_blkbits;
>  	spin_lock_irqsave(&ifs->state_lock, flags);
> -	bitmap_set(ifs->state, first_blk + blks_per_folio, nr_blks);
> +	bitmap_set(ifs->state, first_blk + blks_per_folio,
> +		   last_blk - first_blk + 1);
>  	spin_unlock_irqrestore(&ifs->state_lock, flags);
>  }
>  
> -- 
> 2.52.0
> 
> 

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH v2 1/5] iomap: correct the range of a partial dirty clear
  2026-07-08 20:57   ` Joanne Koong
@ 2026-07-09  2:36     ` Zhang Yi
  0 siblings, 0 replies; 14+ messages in thread
From: Zhang Yi @ 2026-07-09  2:36 UTC (permalink / raw)
  To: Joanne Koong
  Cc: linux-fsdevel, linux-xfs, linux-ext4, brauner, djwong, hch,
	yi.zhang, yizhang089, yangerkun, yukuai

On 7/9/2026 4:57 AM, Joanne Koong wrote:
> On Tue, May 19, 2026 at 8:09 PM Zhang Yi <yi.zhang@huaweicloud.com> wrote:
>>
>> From: Zhang Yi <yi.zhang@huawei.com>
>>
>> The block range calculation in ifs_clear_range_dirty() is incorrect when
>> partially clearing a range in a folio. We cannot clear the dirty bit of
>> the first block or the last block if the start or end offset is not
>> blocksize-aligned. This has not yet caused any issues since we always
>> clear a whole folio in iomap_writeback_folio().
> 
> If this isn't currently reachable, does this still need the stable@
> tag? My understanding from the stable document [1] is that "it must
> either fix a real bug that bothers people or just add a device ID"?
> [1]
> 

Indeed, I will drop it. Thank you for the reminder.

Thanks,
Yi

>>
>> Fix this by rounding up the first block to blocksize alignment, and
>> calculate the last block by rounding down (using truncation). Correct
>> the nr_blks calculation accordingly.
>>
>> Fixes: 4ce02c679722 ("iomap: Add per-block dirty state tracking to improve performance")
>> Cc: <stable@vger.kernel.org> # v6.6
>> Signed-off-by: Zhang Yi <yi.zhang@huawei.com>
> 
> Reviewed-by: Joanne Koong <joannelkoong@gmail.com>
> 
> Thanks,
> Joanne
> 
> [1] https://www.kernel.org/doc/Documentation/process/stable-kernel-rules.rst


^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH v2 1/5] iomap: correct the range of a partial dirty clear
  2026-07-08 21:40   ` Darrick J. Wong
@ 2026-07-09  2:47     ` Zhang Yi
  0 siblings, 0 replies; 14+ messages in thread
From: Zhang Yi @ 2026-07-09  2:47 UTC (permalink / raw)
  To: Darrick J. Wong
  Cc: linux-fsdevel, linux-xfs, linux-ext4, brauner, hch, joannelkoong,
	yi.zhang, yizhang089, yangerkun, yukuai

On 7/9/2026 5:40 AM, Darrick J. Wong wrote:
> On Wed, May 20, 2026 at 11:03:53AM +0800, Zhang Yi wrote:
>> From: Zhang Yi <yi.zhang@huawei.com>
>>
>> The block range calculation in ifs_clear_range_dirty() is incorrect when
>> partially clearing a range in a folio. We cannot clear the dirty bit of
>> the first block or the last block if the start or end offset is not
>> blocksize-aligned. This has not yet caused any issues since we always
>> clear a whole folio in iomap_writeback_folio().
>>
>> Fix this by rounding up the first block to blocksize alignment, and
>> calculate the last block by rounding down (using truncation). Correct
>> the nr_blks calculation accordingly.
>>
>> Fixes: 4ce02c679722 ("iomap: Add per-block dirty state tracking to improve performance")
>> Cc: <stable@vger.kernel.org> # v6.6
>> Signed-off-by: Zhang Yi <yi.zhang@huawei.com>
> 
> My apologies for the unnecessary delay, I was on vacation when this came
> through and I never saw it. :/
> 
> Reviewed-by: "Darrick J. Wong" <djwong@kernel.org>

Thank you for the review!

No worries. The ext4 iomap conversion work is still ongoing, so this isn't
particularly urgent. Now, I think this series is ready to go. Besides,
There is also one more fix patch that I need to send out, but I can send it
separately. :)

Thanks,
Yi.

> 
> --D
> 
>> ---
>>  fs/iomap/buffered-io.c | 12 ++++++++----
>>  1 file changed, 8 insertions(+), 4 deletions(-)
>>
>> diff --git a/fs/iomap/buffered-io.c b/fs/iomap/buffered-io.c
>> index d7b648421a70..64351a448a8b 100644
>> --- a/fs/iomap/buffered-io.c
>> +++ b/fs/iomap/buffered-io.c
>> @@ -176,13 +176,17 @@ static void ifs_clear_range_dirty(struct folio *folio,
>>  {
>>  	struct inode *inode = folio->mapping->host;
>>  	unsigned int blks_per_folio = i_blocks_per_folio(inode, folio);
>> -	unsigned int first_blk = (off >> inode->i_blkbits);
>> -	unsigned int last_blk = (off + len - 1) >> inode->i_blkbits;
>> -	unsigned int nr_blks = last_blk - first_blk + 1;
>> +	unsigned int first_blk = round_up(off, i_blocksize(inode)) >>
>> +				 inode->i_blkbits;
>> +	unsigned int last_blk = (off + len) >> inode->i_blkbits;
>>  	unsigned long flags;
>>  
>> +	if (first_blk >= last_blk)
>> +		return;
>> +
>>  	spin_lock_irqsave(&ifs->state_lock, flags);
>> -	bitmap_clear(ifs->state, first_blk + blks_per_folio, nr_blks);
>> +	bitmap_clear(ifs->state, first_blk + blks_per_folio,
>> +		     last_blk - first_blk);
>>  	spin_unlock_irqrestore(&ifs->state_lock, flags);
>>  }
>>  
>> -- 
>> 2.52.0
>>
>>


^ permalink raw reply	[flat|nested] 14+ messages in thread

end of thread, other threads:[~2026-07-09  2:47 UTC | newest]

Thread overview: 14+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-05-20  3:03 [PATCH v2 0/5] iomap: trivial fixes for ext4 conversion Zhang Yi
2026-05-20  3:03 ` [PATCH v2 1/5] iomap: correct the range of a partial dirty clear Zhang Yi
2026-07-08 20:57   ` Joanne Koong
2026-07-09  2:36     ` Zhang Yi
2026-07-08 21:40   ` Darrick J. Wong
2026-07-09  2:47     ` Zhang Yi
2026-05-20  3:03 ` [PATCH v2 2/5] iomap: support invalidating partial folios Zhang Yi
2026-07-08 21:30   ` Joanne Koong
2026-05-20  3:03 ` [PATCH v2 3/5] iomap: fix incorrect did_zero setting in iomap_zero_iter() Zhang Yi
2026-05-20  3:03 ` [PATCH v2 4/5] iomap: fix out-of-bounds bitmap_set() with zero-length range Zhang Yi
2026-07-08 20:40   ` Joanne Koong
2026-07-08 23:26   ` Darrick J. Wong
2026-05-20  3:03 ` [PATCH v2 5/5] iomap: add comments for ifs_clear/set_range_dirty() Zhang Yi
2026-07-08 20:49   ` Joanne Koong

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox