Skip to content

Commit aa35dd5

Browse files
joannekoongbrauner
authored andcommitted
iomap: fix invalid folio access after folio_end_read()
If the folio does not have an iomap_folio_state (ifs) attached and the folio gets read in by the filesystem's IO helper, folio_end_read() will be called by the IO helper at any time. For this case, we cannot access the folio after dispatching it to the IO helper, eg subsequent accesses like if (ctx->cur_folio && offset_in_folio(ctx->cur_folio, iter->pos) == 0) { are incorrect. Fix these invalid accesses by invalidating ctx->cur_folio if all bytes of the folio have been read in by the IO helper. This allows us to also remove the +1 bias added for the ifs case. The bias was previously added to ensure that if all bytes are read in, the IO helper does not end the read on the folio until iomap has decremented the bias. Fixes: b2f35ac ("iomap: add caller-provided callbacks for read and readahead") Signed-off-by: Joanne Koong <joannelkoong@gmail.com> Link: https://patch.msgid.link/20260126224107.2182262-2-joannelkoong@gmail.com Reviewed-by: Christoph Hellwig <hch@lst.de> Reviewed-by: Matthew Wilcox (Oracle) <willy@infradead.org> Signed-off-by: Christian Brauner <brauner@kernel.org>
1 parent 3431d38 commit aa35dd5

1 file changed

Lines changed: 27 additions & 24 deletions

File tree

fs/iomap/buffered-io.c

Lines changed: 27 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -409,32 +409,26 @@ static void iomap_read_init(struct folio *folio)
409409
struct iomap_folio_state *ifs = folio->private;
410410

411411
if (ifs) {
412-
size_t len = folio_size(folio);
413-
414412
/*
415413
* ifs->read_bytes_pending is used to track how many bytes are
416414
* read in asynchronously by the IO helper. We need to track
417415
* this so that we can know when the IO helper has finished
418416
* reading in all the necessary ranges of the folio and can end
419417
* the read.
420418
*
421-
* Increase ->read_bytes_pending by the folio size to start, and
422-
* add a +1 bias. We'll subtract the bias and any uptodate /
423-
* zeroed ranges that did not require IO in iomap_read_end()
424-
* after we're done processing the folio.
419+
* Increase ->read_bytes_pending by the folio size to start.
420+
* We'll subtract any uptodate / zeroed ranges that did not
421+
* require IO in iomap_read_end() after we're done processing
422+
* the folio.
425423
*
426424
* We do this because otherwise, we would have to increment
427425
* ifs->read_bytes_pending every time a range in the folio needs
428426
* to be read in, which can get expensive since the spinlock
429427
* needs to be held whenever modifying ifs->read_bytes_pending.
430-
*
431-
* We add the bias to ensure the read has not been ended on the
432-
* folio when iomap_read_end() is called, even if the IO helper
433-
* has already finished reading in the entire folio.
434428
*/
435429
spin_lock_irq(&ifs->state_lock);
436430
WARN_ON_ONCE(ifs->read_bytes_pending != 0);
437-
ifs->read_bytes_pending = len + 1;
431+
ifs->read_bytes_pending = folio_size(folio);
438432
spin_unlock_irq(&ifs->state_lock);
439433
}
440434
}
@@ -465,11 +459,9 @@ static void iomap_read_end(struct folio *folio, size_t bytes_submitted)
465459

466460
/*
467461
* Subtract any bytes that were initially accounted to
468-
* read_bytes_pending but skipped for IO. The +1 accounts for
469-
* the bias we added in iomap_read_init().
462+
* read_bytes_pending but skipped for IO.
470463
*/
471-
ifs->read_bytes_pending -=
472-
(folio_size(folio) + 1 - bytes_submitted);
464+
ifs->read_bytes_pending -= folio_size(folio) - bytes_submitted;
473465

474466
/*
475467
* If !ifs->read_bytes_pending, this means all pending reads by
@@ -483,14 +475,16 @@ static void iomap_read_end(struct folio *folio, size_t bytes_submitted)
483475
spin_unlock_irq(&ifs->state_lock);
484476
if (end_read)
485477
folio_end_read(folio, uptodate);
486-
} else if (!bytes_submitted) {
478+
} else {
487479
/*
488-
* If there were no bytes submitted, this means we are
489-
* responsible for unlocking the folio here, since no IO helper
490-
* has taken ownership of it. If there were bytes submitted,
491-
* then the IO helper will end the read via
492-
* iomap_finish_folio_read().
480+
* If a folio without an ifs is submitted to the IO helper, the
481+
* read must be on the entire folio and the IO helper takes
482+
* ownership of the folio. This means we should only enter
483+
* iomap_read_end() for the !ifs case if no bytes were submitted
484+
* to the IO helper, in which case we are responsible for
485+
* unlocking the folio here.
493486
*/
487+
WARN_ON_ONCE(bytes_submitted);
494488
folio_unlock(folio);
495489
}
496490
}
@@ -502,6 +496,7 @@ static int iomap_read_folio_iter(struct iomap_iter *iter,
502496
loff_t pos = iter->pos;
503497
loff_t length = iomap_length(iter);
504498
struct folio *folio = ctx->cur_folio;
499+
size_t folio_len = folio_size(folio);
505500
size_t poff, plen;
506501
loff_t pos_diff;
507502
int ret;
@@ -515,8 +510,7 @@ static int iomap_read_folio_iter(struct iomap_iter *iter,
515510

516511
ifs_alloc(iter->inode, folio, iter->flags);
517512

518-
length = min_t(loff_t, length,
519-
folio_size(folio) - offset_in_folio(folio, pos));
513+
length = min_t(loff_t, length, folio_len - offset_in_folio(folio, pos));
520514
while (length) {
521515
iomap_adjust_read_range(iter->inode, folio, &pos, length, &poff,
522516
&plen);
@@ -542,7 +536,15 @@ static int iomap_read_folio_iter(struct iomap_iter *iter,
542536
ret = ctx->ops->read_folio_range(iter, ctx, plen);
543537
if (ret)
544538
return ret;
539+
545540
*bytes_submitted += plen;
541+
/*
542+
* If the entire folio has been read in by the IO
543+
* helper, then the helper owns the folio and will end
544+
* the read on it.
545+
*/
546+
if (*bytes_submitted == folio_len)
547+
ctx->cur_folio = NULL;
546548
}
547549

548550
ret = iomap_iter_advance(iter, plen);
@@ -576,7 +578,8 @@ void iomap_read_folio(const struct iomap_ops *ops,
576578
if (ctx->ops->submit_read)
577579
ctx->ops->submit_read(ctx);
578580

579-
iomap_read_end(folio, bytes_submitted);
581+
if (ctx->cur_folio)
582+
iomap_read_end(ctx->cur_folio, bytes_submitted);
580583
}
581584
EXPORT_SYMBOL_GPL(iomap_read_folio);
582585

0 commit comments

Comments
 (0)