]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
iomap: correct the range of a partial dirty clear
authorZhang Yi <yi.zhang@huawei.com>
Tue, 14 Jul 2026 08:23:21 +0000 (16:23 +0800)
committerChristian Brauner <brauner@kernel.org>
Thu, 23 Jul 2026 09:26:27 +0000 (11:26 +0200)
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")
Signed-off-by: Zhang Yi <yi.zhang@huawei.com>
Link: https://patch.msgid.link/20260714082325.325163-2-yi.zhang@huaweicloud.com
Reviewed-by: Joanne Koong <joannelkoong@gmail.com>
Reviewed-by: "Darrick J. Wong" <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
fs/iomap/buffered-io.c

index 276720bc18dc525cf11958b050a0897cefd3fb63..238b8b1dea9113b41be45914f7ea3cc78739b975 100644 (file)
@@ -177,13 +177,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);
 }