linux-fsdevel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH] iomap: fix race when reading in all bytes of a folio
@ 2025-10-24 21:50 Joanne Koong
  2025-10-27  7:02 ` Christoph Hellwig
  2025-10-27 12:20 ` Brian Foster
  0 siblings, 2 replies; 7+ messages in thread
From: Joanne Koong @ 2025-10-24 21:50 UTC (permalink / raw)
  To: brauner; +Cc: bfoster, hch, djwong, linux-fsdevel

There is a race where if all bytes in a folio need to get read in and
the filesystem finishes reading the bytes in before the call to
iomap_read_end(), then bytes_accounted in iomap_read_end() will be 0 and
the following "ifs->read_bytes_pending -= bytes_accounting" will also be
0 which will trigger an extra folio_end_read() call. This extra
folio_end_read() unlocks the folio for the 2nd time, which sets the lock
bit on the folio, resulting in a permanent lockup.

Fix this by returning from iomap_read_end() early if all bytes are read
in by the filesystem.

Additionally, add some comments to clarify how this accounting logic works.

Signed-off-by: Joanne Koong <joannelkoong@gmail.com>
Fixes: 51311f045375 ("iomap: track pending read bytes more optimally")
Reported-by: Brian Foster <bfoster@redhat.com>
--
This is a fix for commit 51311f045375 in the 'vfs-6.19.iomap' branch. It
would be great if this could get folded up into that original commit, if it's
not too logistically messy to do so.

Thanks,
Joanne
---
 fs/iomap/buffered-io.c | 22 ++++++++++++++++++++++
 1 file changed, 22 insertions(+)

diff --git a/fs/iomap/buffered-io.c b/fs/iomap/buffered-io.c
index 72196e5021b1..c31d30643e2d 100644
--- a/fs/iomap/buffered-io.c
+++ b/fs/iomap/buffered-io.c
@@ -358,6 +358,25 @@ static void iomap_read_init(struct folio *folio)
 	if (ifs) {
 		size_t len = folio_size(folio);
 
+		/*
+		 * ifs->read_bytes_pending is used to track how many bytes are
+		 * read in asynchronously by the filesystem. We need to track
+		 * this so that we can know when the filesystem has finished
+		 * reading in the folio whereupon folio_end_read() should be
+		 * called.
+		 *
+		 * We first set ifs->read_bytes_pending to the entire folio
+		 * size. Then we track how many bytes are read in by the
+		 * filesystem. At the end, in iomap_read_end(), we subtract
+		 * ifs->read_bytes_pending by the number of bytes NOT read in so
+		 * that ifs->read_bytes_pending will be 0 when the filesystem
+		 * has finished reading in all pending bytes.
+		 *
+		 * ifs->read_bytes_pending is initialized to the folio size
+		 * because we do not easily know in the beginning how many
+		 * bytes need to get read in by the filesystem (eg some ranges
+		 * may already be uptodate).
+		 */
 		spin_lock_irq(&ifs->state_lock);
 		ifs->read_bytes_pending += len;
 		spin_unlock_irq(&ifs->state_lock);
@@ -383,6 +402,9 @@ static void iomap_read_end(struct folio *folio, size_t bytes_pending)
 		bool end_read, uptodate;
 		size_t bytes_accounted = folio_size(folio) - bytes_pending;
 
+		if (!bytes_accounted)
+			return;
+
 		spin_lock_irq(&ifs->state_lock);
 		ifs->read_bytes_pending -= bytes_accounted;
 		/*
-- 
2.47.3


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

end of thread, other threads:[~2025-10-28 17:31 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-10-24 21:50 [PATCH] iomap: fix race when reading in all bytes of a folio Joanne Koong
2025-10-27  7:02 ` Christoph Hellwig
2025-10-27 12:20 ` Brian Foster
2025-10-27 16:43   ` Joanne Koong
2025-10-28 11:20     ` Brian Foster
2025-10-28 17:11       ` Joanne Koong
2025-10-28 17:31         ` Joanne Koong

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).