iomap: simplify ->read_folio_range() error handling for reads

Instead of requiring that the caller calls iomap_finish_folio_read()
even if the ->read_folio_range() callback returns an error, account for
this internally in iomap instead, which makes the interface simpler and
makes it match writeback's ->read_folio_range() error handling
expectations.

Signed-off-by: Joanne Koong <joannelkoong@gmail.com>
Link: https://patch.msgid.link/20251111193658.3495942-6-joannelkoong@gmail.com
Reviewed-by: Christoph Hellwig <hch@lst.de>
Reviewed-by: Darrick J. Wong <djwong@kernel.org>
Signed-off-by: Christian Brauner <brauner@kernel.org>
This commit is contained in:
Joanne Koong 2025-11-11 11:36:54 -08:00 committed by Christian Brauner
parent 6b1fd2281f
commit f8eaf79406
No known key found for this signature in database
GPG Key ID: 91C61BC06578DCA2
4 changed files with 41 additions and 44 deletions

View File

@ -149,10 +149,9 @@ These ``struct kiocb`` flags are significant for buffered I/O with iomap:
iomap calls these functions: iomap calls these functions:
- ``read_folio_range``: Called to read in the range. This must be provided - ``read_folio_range``: Called to read in the range. This must be provided
by the caller. The caller is responsible for calling by the caller. If this succeeds, iomap_finish_folio_read() must be called
iomap_finish_folio_read() after reading in the folio range. This should be after the range is read in, regardless of whether the read succeeded or
done even if an error is encountered during the read. This returns 0 on failed.
success or a negative error on failure.
- ``submit_read``: Submit any pending read requests. This function is - ``submit_read``: Submit any pending read requests. This function is
optional. optional.

View File

@ -922,13 +922,6 @@ static int fuse_iomap_read_folio_range_async(const struct iomap_iter *iter,
if (ctx->rac) { if (ctx->rac) {
ret = fuse_handle_readahead(folio, ctx->rac, data, pos, len); ret = fuse_handle_readahead(folio, ctx->rac, data, pos, len);
/*
* If fuse_handle_readahead was successful, fuse_readpages_end
* will do the iomap_finish_folio_read, else we need to call it
* here
*/
if (ret)
iomap_finish_folio_read(folio, off, len, ret);
} else { } else {
/* /*
* for non-readahead read requests, do reads synchronously * for non-readahead read requests, do reads synchronously
@ -936,6 +929,7 @@ static int fuse_iomap_read_folio_range_async(const struct iomap_iter *iter,
* out-of-order reads * out-of-order reads
*/ */
ret = fuse_do_readfolio(file, folio, off, len); ret = fuse_do_readfolio(file, folio, off, len);
if (!ret)
iomap_finish_folio_read(folio, off, len, ret); iomap_finish_folio_read(folio, off, len, ret);
} }
return ret; return ret;

View File

@ -398,7 +398,8 @@ static void iomap_read_init(struct folio *folio)
* has already finished reading in the entire folio. * has already finished reading in the entire folio.
*/ */
spin_lock_irq(&ifs->state_lock); spin_lock_irq(&ifs->state_lock);
ifs->read_bytes_pending += len + 1; WARN_ON_ONCE(ifs->read_bytes_pending != 0);
ifs->read_bytes_pending = len + 1;
spin_unlock_irq(&ifs->state_lock); spin_unlock_irq(&ifs->state_lock);
} }
} }
@ -414,43 +415,47 @@ static void iomap_read_init(struct folio *folio)
*/ */
static void iomap_read_end(struct folio *folio, size_t bytes_submitted) static void iomap_read_end(struct folio *folio, size_t bytes_submitted)
{ {
struct iomap_folio_state *ifs; struct iomap_folio_state *ifs = folio->private;
/*
* If there are no bytes submitted, this means we are responsible for
* unlocking the folio here, since no IO helper has taken ownership of
* it.
*/
if (!bytes_submitted) {
folio_unlock(folio);
return;
}
ifs = folio->private;
if (ifs) { if (ifs) {
bool end_read, uptodate; bool end_read, uptodate;
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 * Subtract any bytes that were initially accounted to
* read_bytes_pending but skipped for IO. * read_bytes_pending but skipped for IO. The +1
* The +1 accounts for the bias we added in iomap_read_init(). * accounts for the bias we added in iomap_read_init().
*/ */
size_t bytes_not_submitted = folio_size(folio) + 1 - size_t bytes_not_submitted = folio_size(folio) + 1 -
bytes_submitted; bytes_submitted;
spin_lock_irq(&ifs->state_lock);
ifs->read_bytes_pending -= bytes_not_submitted; ifs->read_bytes_pending -= bytes_not_submitted;
/* /*
* If !ifs->read_bytes_pending, this means all pending reads * If !ifs->read_bytes_pending, this means all pending
* by the IO helper have already completed, which means we need * reads by the IO helper have already completed, which
* to end the folio read here. If ifs->read_bytes_pending != 0, * means we need to end the folio read here. If
* the IO helper will end the folio read. * ifs->read_bytes_pending != 0, the IO helper will end
* 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);
if (end_read) if (end_read)
folio_end_read(folio, uptodate); folio_end_read(folio, uptodate);
} else if (!bytes_submitted) {
/*
* If there were no bytes submitted, this means we are
* responsible for unlocking the folio here, since no IO helper
* has taken ownership of it. If there were bytes submitted,
* then the IO helper will end the read via
* iomap_finish_folio_read().
*/
folio_unlock(folio);
} }
} }
@ -498,10 +503,10 @@ static int iomap_read_folio_iter(struct iomap_iter *iter,
} else { } else {
if (!*bytes_submitted) if (!*bytes_submitted)
iomap_read_init(folio); iomap_read_init(folio);
*bytes_submitted += plen;
ret = ctx->ops->read_folio_range(iter, ctx, plen); ret = ctx->ops->read_folio_range(iter, ctx, plen);
if (ret) if (ret)
return ret; return ret;
*bytes_submitted += plen;
} }
ret = iomap_iter_advance(iter, plen); ret = iomap_iter_advance(iter, plen);

View File

@ -495,9 +495,8 @@ struct iomap_read_ops {
/* /*
* Read in a folio range. * Read in a folio range.
* *
* The caller is responsible for calling iomap_finish_folio_read() after * If this succeeds, iomap_finish_folio_read() must be called after the
* reading in the folio range. This should be done even if an error is * range is read in, regardless of whether the read succeeded or failed.
* encountered during the read.
* *
* Returns 0 on success or a negative error on failure. * Returns 0 on success or a negative error on failure.
*/ */