iomap: fix iomap_read_end() for already uptodate folios

There are some cases where when iomap_read_end() is called, the folio
may already have been marked uptodate. For example, if the iomap block
needed zeroing, then the folio may have been marked uptodate after the
zeroing.

iomap_read_end() should unlock the folio instead of calling
folio_end_read(), which is how these cases were handled prior to commit
f8eaf79406 ("iomap: simplify ->read_folio_range() error handling for
reads"). Calling folio_end_read() on an uptodate folio leads to buggy
behavior where marking an already uptodate folio as uptodate will XOR it
to be marked nonuptodate.

Fixes: f8eaf79406 ("iomap: simplify ->read_folio_range() error handling for reads")
Signed-off-by: Joanne Koong <joannelkoong@gmail.com>
Link: https://patch.msgid.link/20251118211111.1027272-2-joannelkoong@gmail.com
Tested-by: Matthew Wilcox (Oracle) <willy@infradead.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
Reported-by: Matthew Wilcox (Oracle) <willy@infradead.org>
Signed-off-by: Christian Brauner <brauner@kernel.org>
This commit is contained in:
Joanne Koong 2025-11-18 13:11:11 -08:00 committed by Christian Brauner
parent 5ec58e6acd
commit d7ff85d4b8
No known key found for this signature in database
GPG Key ID: 91C61BC06578DCA2
1 changed files with 19 additions and 18 deletions

View File

@ -458,25 +458,26 @@ static void iomap_read_end(struct folio *folio, size_t bytes_submitted)
spin_lock_irq(&ifs->state_lock); spin_lock_irq(&ifs->state_lock);
if (!ifs->read_bytes_pending) { if (!ifs->read_bytes_pending) {
WARN_ON_ONCE(bytes_submitted); WARN_ON_ONCE(bytes_submitted);
end_read = true; spin_unlock_irq(&ifs->state_lock);
} else { folio_unlock(folio);
return;
}
/* /*
* Subtract any bytes that were initially accounted to * Subtract any bytes that were initially accounted to
* read_bytes_pending but skipped for IO. The +1 * read_bytes_pending but skipped for IO. The +1 accounts for
* accounts for the bias we added in iomap_read_init(). * the bias we added in iomap_read_init().
*/ */
size_t bytes_not_submitted = folio_size(folio) + 1 - ifs->read_bytes_pending -=
bytes_submitted; (folio_size(folio) + 1 - bytes_submitted);
ifs->read_bytes_pending -= bytes_not_submitted;
/* /*
* If !ifs->read_bytes_pending, this means all pending * If !ifs->read_bytes_pending, this means all pending reads by
* reads by the IO helper have already completed, which * the IO helper have already completed, which means we need to
* means we need to end the folio read here. If * end the folio read here. If ifs->read_bytes_pending != 0,
* ifs->read_bytes_pending != 0, the IO helper will end * the IO helper will end the folio read.
* the folio read.
*/ */
end_read = !ifs->read_bytes_pending; end_read = !ifs->read_bytes_pending;
}
if (end_read) if (end_read)
uptodate = ifs_is_fully_uptodate(folio, ifs); uptodate = ifs_is_fully_uptodate(folio, ifs);
spin_unlock_irq(&ifs->state_lock); spin_unlock_irq(&ifs->state_lock);