| 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 | |
| 9 | static inline void iomap_iter_reset_iomap(struct iomap_iter *iter) |
| 10 | { |
| 11 | if (iter->iomap.flags & IOMAP_F_FOLIO_BATCH) { |
| 12 | folio_batch_release(fbatch: iter->fbatch); |
| 13 | folio_batch_reinit(fbatch: iter->fbatch); |
| 14 | iter->iomap.flags &= ~IOMAP_F_FOLIO_BATCH; |
| 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 */ |
| 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 | |
| 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(inode: iter->inode, iomap: &iter->iomap); |
| 42 | if (iter->srcmap.type != IOMAP_HOLE) |
| 43 | trace_iomap_iter_srcmap(inode: iter->inode, iomap: &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 | */ |
| 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, pos: iter->iter_start_pos, |
| 83 | len: 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 | |