Skip to content

Commit d3ccc4d

Browse files
committed
Merge patch "iomap: don't mark folio uptodate if read IO has bytes pending"
Joanne Koong <[email protected]> says: This is a fix for this scenario: ->read_folio() gets called on a folio size that is 16k while the file is 4k: a) ifs->read_bytes_pending gets initialized to 16k b) ->read_folio_range() is called for the 4k read c) the 4k read succeeds, ifs->read_bytes_pending is now 12k and the 0 to 4k range is marked uptodate d) the post-eof blocks are zeroed and marked uptodate in the call to iomap_set_range_uptodate() e) iomap_set_range_uptodate() sees all the ranges are marked uptodate and it marks the folio uptodate f) iomap_read_end() gets called to subtract the 12k from ifs->read_bytes_pending. it too sees all the ranges are marked uptodate and marks the folio uptodate using XOR g) the XOR call clears the uptodate flag on the folio The same situation can occur if the last range read for the folio is done as an inline read and all the previous ranges have already completed by the time the inline read completes. For more context, the full discussion can be found in [1]. There was a discussion about alternative approaches in that thread, but they had more complications. There is another discussion in v1 [2] about consolidating the read paths. Until that is resolved, this patch fixes the issue. [1] https://lore.kernel.org/linux-fsdevel/CAJnrk1Z9za5w4FoJqTGx50zR2haHHaoot1KJViQyEHJQq4=34w@mail.gmail.com/#t [2] https://lore.kernel.org/linux-fsdevel/[email protected]/T/#u * patches from https://patch.msgid.link/[email protected]: iomap: don't mark folio uptodate if read IO has bytes pending Link: https://patch.msgid.link/[email protected] Signed-off-by: Christian Brauner <[email protected]>
2 parents 1004714 + debc1a4 commit d3ccc4d

1 file changed

Lines changed: 12 additions & 3 deletions

File tree

fs/iomap/buffered-io.c

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -80,18 +80,27 @@ static void iomap_set_range_uptodate(struct folio *folio, size_t off,
8080
{
8181
struct iomap_folio_state *ifs = folio->private;
8282
unsigned long flags;
83-
bool uptodate = true;
83+
bool mark_uptodate = true;
8484

8585
if (folio_test_uptodate(folio))
8686
return;
8787

8888
if (ifs) {
8989
spin_lock_irqsave(&ifs->state_lock, flags);
90-
uptodate = ifs_set_range_uptodate(folio, ifs, off, len);
90+
/*
91+
* If a read with bytes pending is in progress, we must not call
92+
* folio_mark_uptodate(). The read completion path
93+
* (iomap_read_end()) will call folio_end_read(), which uses XOR
94+
* semantics to set the uptodate bit. If we set it here, the XOR
95+
* in folio_end_read() will clear it, leaving the folio not
96+
* uptodate.
97+
*/
98+
mark_uptodate = ifs_set_range_uptodate(folio, ifs, off, len) &&
99+
!ifs->read_bytes_pending;
91100
spin_unlock_irqrestore(&ifs->state_lock, flags);
92101
}
93102

94-
if (uptodate)
103+
if (mark_uptodate)
95104
folio_mark_uptodate(folio);
96105
}
97106

0 commit comments

Comments
 (0)