Skip to content

Commit bc264fe

Browse files
Brian Fosterbrauner
authored andcommitted
iomap: support incremental iomap_iter advances
The current iomap_iter iteration model reads the mapping from the filesystem, processes the subrange of the operation associated with the current mapping, and returns the number of bytes processed back to the iteration code. The latter advances the position and remaining length of the iter in preparation for the next iteration. At the _iter() handler level, this tends to produce a processing loop where the local code pulls the current position and remaining length out of the iter, iterates it locally based on file offset, and then breaks out when the associated range has been fully processed. This works well enough for current handlers, but upcoming enhancements require a bit more flexibility in certain situations. Enhancements for zero range will lead to a situation where the processing loop is no longer a pure ascending offset walk, but rather dictated by pagecache state and folio lookup. Since folio lookup and write preparation occur at different levels, it is more difficult to manage position and length outside of the iter. To provide more flexibility to certain iomap operations, introduce support for incremental iomap_iter advances from within the operation itself. This allows more granular advances for operations that might not use the typical file offset based walk. Note that the semantics for operations that use incremental advances is slightly different than traditional operations. Operations that advance the iter directly are expected to return success or failure (i.e. 0 or negative error code) in iter.processed rather than the number of bytes processed. Signed-off-by: Brian Foster <bfoster@redhat.com> Link: https://lore.kernel.org/r/20250207143253.314068-8-bfoster@redhat.com Reviewed-by: Christoph Hellwig <hch@lst.de> Reviewed-by: "Darrick J. Wong" <djwong@kernel.org> Signed-off-by: Christian Brauner <brauner@kernel.org>
1 parent b51d30f commit bc264fe

File tree

2 files changed

+31
-9
lines changed

2 files changed

+31
-9
lines changed

fs/iomap/iter.c

Lines changed: 25 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@ static inline void iomap_iter_done(struct iomap_iter *iter)
3535
WARN_ON_ONCE(iter->iomap.offset + iter->iomap.length <= iter->pos);
3636
WARN_ON_ONCE(iter->iomap.flags & IOMAP_F_STALE);
3737

38+
iter->iter_start_pos = iter->pos;
39+
3840
trace_iomap_iter_dstmap(iter->inode, &iter->iomap);
3941
if (iter->srcmap.type != IOMAP_HOLE)
4042
trace_iomap_iter_srcmap(iter->inode, &iter->srcmap);
@@ -58,6 +60,8 @@ static inline void iomap_iter_done(struct iomap_iter *iter)
5860
int iomap_iter(struct iomap_iter *iter, const struct iomap_ops *ops)
5961
{
6062
bool stale = iter->iomap.flags & IOMAP_F_STALE;
63+
ssize_t advanced = iter->processed > 0 ? iter->processed : 0;
64+
u64 olen = iter->len;
6165
s64 processed;
6266
int ret;
6367

@@ -66,11 +70,22 @@ int iomap_iter(struct iomap_iter *iter, const struct iomap_ops *ops)
6670
if (!iter->iomap.length)
6771
goto begin;
6872

73+
/*
74+
* If iter.processed is zero, the op may still have advanced the iter
75+
* itself. Calculate the advanced and original length bytes based on how
76+
* far pos has advanced for ->iomap_end().
77+
*/
78+
if (!advanced) {
79+
advanced = iter->pos - iter->iter_start_pos;
80+
olen += advanced;
81+
}
82+
6983
if (ops->iomap_end) {
70-
ret = ops->iomap_end(iter->inode, iter->pos, iomap_length(iter),
71-
iter->processed > 0 ? iter->processed : 0,
72-
iter->flags, &iter->iomap);
73-
if (ret < 0 && !iter->processed)
84+
ret = ops->iomap_end(iter->inode, iter->iter_start_pos,
85+
iomap_length_trim(iter, iter->iter_start_pos,
86+
olen),
87+
advanced, iter->flags, &iter->iomap);
88+
if (ret < 0 && !advanced)
7489
return ret;
7590
}
7691

@@ -81,16 +96,19 @@ int iomap_iter(struct iomap_iter *iter, const struct iomap_ops *ops)
8196
}
8297

8398
/*
84-
* Advance the iter and clear state from the previous iteration. Use
85-
* iter->len to determine whether to continue onto the next mapping.
99+
* Advance the iter and clear state from the previous iteration. This
100+
* passes iter->processed because that reflects the bytes processed but
101+
* not yet advanced by the iter handler.
102+
*
103+
* Use iter->len to determine whether to continue onto the next mapping.
86104
* Explicitly terminate in the case where the current iter has not
87105
* advanced at all (i.e. no work was done for some reason) unless the
88106
* mapping has been marked stale and needs to be reprocessed.
89107
*/
90108
ret = iomap_iter_advance(iter, &processed);
91109
if (!ret && iter->len > 0)
92110
ret = 1;
93-
if (ret > 0 && !iter->processed && !stale)
111+
if (ret > 0 && !advanced && !stale)
94112
ret = 0;
95113
iomap_iter_reset_iomap(iter);
96114
if (ret <= 0)

include/linux/iomap.h

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -218,8 +218,11 @@ struct iomap_ops {
218218
* calls to iomap_iter(). Treat as read-only in the body.
219219
* @len: The remaining length of the file segment we're operating on.
220220
* It is updated at the same time as @pos.
221-
* @processed: The number of bytes processed by the body in the most recent
222-
* iteration, or a negative errno. 0 causes the iteration to stop.
221+
* @iter_start_pos: The original start pos for the current iomap. Used for
222+
* incremental iter advance.
223+
* @processed: The number of bytes the most recent iteration needs iomap_iter()
224+
* to advance the iter, zero if the iter was already advanced, or a
225+
* negative errno for an error during the operation.
223226
* @flags: Zero or more of the iomap_begin flags above.
224227
* @iomap: Map describing the I/O iteration
225228
* @srcmap: Source map for COW operations
@@ -228,6 +231,7 @@ struct iomap_iter {
228231
struct inode *inode;
229232
loff_t pos;
230233
u64 len;
234+
loff_t iter_start_pos;
231235
s64 processed;
232236
unsigned flags;
233237
struct iomap iomap;

0 commit comments

Comments
 (0)