From: Joanne Koong Date: Tue, 18 Nov 2025 21:11:11 +0000 (-0800) Subject: iomap: fix iomap_read_end() for already uptodate folios X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=d7ff85d4b899e02b4b8a8ca9f44f54a06aee1b4d;p=thirdparty%2Flinux.git 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 f8eaf79406fe ("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: f8eaf79406fe ("iomap: simplify ->read_folio_range() error handling for reads") Signed-off-by: Joanne Koong Link: https://patch.msgid.link/20251118211111.1027272-2-joannelkoong@gmail.com Tested-by: Matthew Wilcox (Oracle) Reviewed-by: Christoph Hellwig Reported-by: Matthew Wilcox (Oracle) Signed-off-by: Christian Brauner --- diff --git a/fs/iomap/buffered-io.c b/fs/iomap/buffered-io.c index 089566a36cff5..f68fc6ac15e0c 100644 --- a/fs/iomap/buffered-io.c +++ b/fs/iomap/buffered-io.c @@ -458,25 +458,26 @@ static void iomap_read_end(struct folio *folio, size_t bytes_submitted) spin_lock_irq(&ifs->state_lock); if (!ifs->read_bytes_pending) { WARN_ON_ONCE(bytes_submitted); - end_read = true; - } else { - /* - * Subtract any bytes that were initially accounted to - * read_bytes_pending but skipped for IO. The +1 - * accounts for the bias we added in iomap_read_init(). - */ - size_t bytes_not_submitted = folio_size(folio) + 1 - - bytes_submitted; - ifs->read_bytes_pending -= bytes_not_submitted; - /* - * If !ifs->read_bytes_pending, this means all pending - * reads by the IO helper have already completed, which - * means we need to end the folio read here. If - * ifs->read_bytes_pending != 0, the IO helper will end - * the folio read. - */ - end_read = !ifs->read_bytes_pending; + spin_unlock_irq(&ifs->state_lock); + folio_unlock(folio); + return; } + + /* + * Subtract any bytes that were initially accounted to + * read_bytes_pending but skipped for IO. The +1 accounts for + * the bias we added in iomap_read_init(). + */ + ifs->read_bytes_pending -= + (folio_size(folio) + 1 - bytes_submitted); + + /* + * If !ifs->read_bytes_pending, this means all pending reads by + * the IO helper have already completed, which means we need to + * end the folio read here. If ifs->read_bytes_pending != 0, + * the IO helper will end the folio read. + */ + end_read = !ifs->read_bytes_pending; if (end_read) uptodate = ifs_is_fully_uptodate(folio, ifs); spin_unlock_irq(&ifs->state_lock);