1 // SPDX-License-Identifier: GPL-2.0
2 /*
3 * Copyright (C) 2010 Red Hat, Inc.
4 * Copyright (c) 2016-2021 Christoph Hellwig.
5 */
6 #include <linux/iomap.h>
7 #include "trace.h"
8
iomap_iter_reset_iomap(struct iomap_iter * iter)9 static inline void iomap_iter_reset_iomap(struct iomap_iter *iter)
10 {
11 if (iter->fbatch) {
12 folio_batch_release(iter->fbatch);
13 kfree(iter->fbatch);
14 iter->fbatch = NULL;
15 }
16
17 iter->status = 0;
18 memset(&iter->iomap, 0, sizeof(iter->iomap));
19 memset(&iter->srcmap, 0, sizeof(iter->srcmap));
20 }
21
22 /* Advance the current iterator position and decrement the remaining length */
iomap_iter_advance(struct iomap_iter * iter,u64 count)23 int iomap_iter_advance(struct iomap_iter *iter, u64 count)
24 {
25 if (WARN_ON_ONCE(count > iomap_length(iter)))
26 return -EIO;
27 iter->pos += count;
28 iter->len -= count;
29 return 0;
30 }
31
iomap_iter_done(struct iomap_iter * iter)32 static inline void iomap_iter_done(struct iomap_iter *iter)
33 {
34 WARN_ON_ONCE(iter->iomap.offset > iter->pos);
35 WARN_ON_ONCE(iter->iomap.length == 0);
36 WARN_ON_ONCE(iter->iomap.offset + iter->iomap.length <= iter->pos);
37 WARN_ON_ONCE(iter->iomap.flags & IOMAP_F_STALE);
38
39 iter->iter_start_pos = iter->pos;
40
41 trace_iomap_iter_dstmap(iter->inode, &iter->iomap);
42 if (iter->srcmap.type != IOMAP_HOLE)
43 trace_iomap_iter_srcmap(iter->inode, &iter->srcmap);
44 }
45
46 /**
47 * iomap_iter - iterate over a ranges in a file
48 * @iter: iteration structue
49 * @ops: iomap ops provided by the file system
50 *
51 * Iterate over filesystem-provided space mappings for the provided file range.
52 *
53 * This function handles cleanup of resources acquired for iteration when the
54 * filesystem indicates there are no more space mappings, which means that this
55 * function must be called in a loop that continues as long it returns a
56 * positive value. If 0 or a negative value is returned, the caller must not
57 * return to the loop body. Within a loop body, there are two ways to break out
58 * of the loop body: leave @iter.status unchanged, or set it to a negative
59 * errno.
60 */
iomap_iter(struct iomap_iter * iter,const struct iomap_ops * ops)61 int iomap_iter(struct iomap_iter *iter, const struct iomap_ops *ops)
62 {
63 bool stale = iter->iomap.flags & IOMAP_F_STALE;
64 ssize_t advanced;
65 u64 olen;
66 int ret;
67
68 trace_iomap_iter(iter, ops, _RET_IP_);
69
70 if (!iter->iomap.length)
71 goto begin;
72
73 /*
74 * Calculate how far the iter was advanced and the original length bytes
75 * for ->iomap_end().
76 */
77 advanced = iter->pos - iter->iter_start_pos;
78 olen = iter->len + advanced;
79
80 if (ops->iomap_end) {
81 ret = ops->iomap_end(iter->inode, iter->iter_start_pos,
82 iomap_length_trim(iter, iter->iter_start_pos,
83 olen),
84 advanced, iter->flags, &iter->iomap);
85 if (ret < 0 && !advanced)
86 return ret;
87 }
88
89 /* detect old return semantics where this would advance */
90 if (WARN_ON_ONCE(iter->status > 0))
91 iter->status = -EIO;
92
93 /*
94 * Use iter->len to determine whether to continue onto the next mapping.
95 * Explicitly terminate on error status or if the current iter has not
96 * advanced at all (i.e. no work was done for some reason) unless the
97 * mapping has been marked stale and needs to be reprocessed.
98 */
99 if (iter->status < 0)
100 ret = iter->status;
101 else if (iter->len == 0 || (!advanced && !stale))
102 ret = 0;
103 else
104 ret = 1;
105 iomap_iter_reset_iomap(iter);
106 if (ret <= 0)
107 return ret;
108
109 begin:
110 ret = ops->iomap_begin(iter->inode, iter->pos, iter->len, iter->flags,
111 &iter->iomap, &iter->srcmap);
112 if (ret < 0)
113 return ret;
114 iomap_iter_done(iter);
115 return 1;
116 }
117