diff options
Diffstat (limited to 'iov.c')
-rw-r--r-- | iov.c | 199 |
1 files changed, 187 insertions, 12 deletions
@@ -26,7 +26,8 @@ #include "iov.h" -/* iov_skip_bytes() - Skip leading bytes of an IO vector +/** + * iov_skip_bytes() - Skip leading bytes of an IO vector * @iov: IO vector * @n: Number of entries in @iov * @skip: Number of leading bytes of @iov to skip @@ -56,8 +57,8 @@ size_t iov_skip_bytes(const struct iovec *iov, size_t n, } /** - * iov_from_buf - Copy data from a buffer to an I/O vector (struct iovec) - * efficiently. + * iov_from_buf() - Copy data from a buffer to an I/O vector (struct iovec) + * efficiently. * * @iov: Pointer to the array of struct iovec describing the * scatter/gather I/O vector. @@ -66,9 +67,8 @@ size_t iov_skip_bytes(const struct iovec *iov, size_t n, * @buf: Pointer to the source buffer containing the data to copy. * @bytes: Total number of bytes to copy from buf to iov. * - * Returns: The number of bytes successfully copied. + * Return: the number of bytes successfully copied. */ -/* cppcheck-suppress unusedFunction */ size_t iov_from_buf(const struct iovec *iov, size_t iov_cnt, size_t offset, const void *buf, size_t bytes) { @@ -97,8 +97,8 @@ size_t iov_from_buf(const struct iovec *iov, size_t iov_cnt, } /** - * iov_to_buf - Copy data from a scatter/gather I/O vector (struct iovec) to - * a buffer efficiently. + * iov_to_buf() - Copy data from a scatter/gather I/O vector (struct iovec) to + * a buffer efficiently. * * @iov: Pointer to the array of struct iovec describing the scatter/gather * I/O vector. @@ -107,9 +107,8 @@ size_t iov_from_buf(const struct iovec *iov, size_t iov_cnt, * @buf: Pointer to the destination buffer where data will be copied. * @bytes: Total number of bytes to copy from iov to buf. * - * Returns: The number of bytes successfully copied. + * Return: the number of bytes successfully copied. */ -/* cppcheck-suppress unusedFunction */ size_t iov_to_buf(const struct iovec *iov, size_t iov_cnt, size_t offset, void *buf, size_t bytes) { @@ -127,6 +126,7 @@ size_t iov_to_buf(const struct iovec *iov, size_t iov_cnt, /* copying data */ for (copied = 0; copied < bytes && i < iov_cnt; i++) { size_t len = MIN(iov[i].iov_len - offset, bytes - copied); + ASSERT(iov[i].iov_base); memcpy((char *)buf + copied, (char *)iov[i].iov_base + offset, len); copied += len; @@ -137,14 +137,14 @@ size_t iov_to_buf(const struct iovec *iov, size_t iov_cnt, } /** - * iov_size - Calculate the total size of a scatter/gather I/O vector - * (struct iovec). + * iov_size() - Calculate the total size of a scatter/gather I/O vector + * (struct iovec). * * @iov: Pointer to the array of struct iovec describing the * scatter/gather I/O vector. * @iov_cnt: Number of elements in the iov array. * - * Returns: The total size in bytes. + * Return: the total size in bytes. */ size_t iov_size(const struct iovec *iov, size_t iov_cnt) { @@ -156,3 +156,178 @@ size_t iov_size(const struct iovec *iov, size_t iov_cnt) return len; } + +/** + * iov_tail_prune() - Remove any unneeded buffers from an IOV tail + * @tail: IO vector tail (modified) + * + * If an IOV tail's offset is large enough, it may not include any bytes from + * the first (or first several) buffers in the underlying IO vector. Modify the + * tail's representation so it contains the same logical bytes, but only + * includes buffers that are actually needed. This will avoid stepping through + * unnecessary elements of the underlying IO vector on future operations. + * + * Return: true if the tail still contains any bytes, otherwise false + */ +bool iov_tail_prune(struct iov_tail *tail) +{ + size_t i; + + i = iov_skip_bytes(tail->iov, tail->cnt, tail->off, &tail->off); + tail->iov += i; + tail->cnt -= i; + + return !!tail->cnt; +} + +/** + * iov_tail_size() - Calculate the total size of an IO vector tail + * @tail: IO vector tail + * + * Return: the total size in bytes. + */ +size_t iov_tail_size(struct iov_tail *tail) +{ + iov_tail_prune(tail); + return iov_size(tail->iov, tail->cnt) - tail->off; +} + +/** + * iov_drop_header() - Discard a header from an IOV tail + * @tail: IO vector tail + * @len: length to move the head of the tail + * + * Return: true if the item still contains any bytes, otherwise false + */ +bool iov_drop_header(struct iov_tail *tail, size_t len) +{ + tail->off = tail->off + len; + + return iov_tail_prune(tail); +} + +/** + * iov_check_header() - Check if a header can be accessed + * @tail: IOV tail to get header from + * @len: Length of header to get, in bytes + * @align: Required alignment of header, in bytes + * + * @tail may be pruned, but will represent the same bytes as before. + * + * Return: pointer to the first @len logical bytes of the tail, NULL if that + * overruns the IO vector, is not contiguous or doesn't have the + * requested alignment. + */ +static void *iov_check_header(struct iov_tail *tail, size_t len, size_t align) +{ + char *p; + + if (!iov_tail_prune(tail)) + return NULL; /* Nothing left */ + + if (tail->off + len < tail->off) + return NULL; /* Overflow */ + + if (tail->off + len > tail->iov[0].iov_len) + return NULL; /* Not contiguous */ + + p = (char *)tail->iov[0].iov_base + tail->off; + if ((uintptr_t)p % align) + return NULL; /* not aligned */ + + return p; +} + +/** + * iov_peek_header_() - Get pointer to a header from an IOV tail + * @tail: IOV tail to get header from + * @v: Temporary memory to use if the memory in @tail + * is discontinuous + * @len: Length of header to get, in bytes + * @align: Required alignment of header, in bytes + * + * @tail may be pruned, but will represent the same bytes as before. + * + * Return: pointer to the first @len logical bytes of the tail, or to + * a copy if that overruns the IO vector, is not contiguous or + * doesn't have the requested alignment. NULL if that overruns the + * IO vector. + */ +/* cppcheck-suppress [staticFunction,unmatchedSuppression] */ +void *iov_peek_header_(struct iov_tail *tail, void *v, size_t len, size_t align) +{ + char *p = iov_check_header(tail, len, align); + size_t l; + + if (p) + return p; + + l = iov_to_buf(tail->iov, tail->cnt, tail->off, v, len); + if (l != len) + return NULL; + + return v; +} + +/** + * iov_remove_header_() - Remove a header from an IOV tail + * @tail: IOV tail to remove header from (modified) + * @v: Temporary memory to use if the memory in @tail + * is discontinuous + * @len: Length of header to remove, in bytes + * @align: Required alignment of header, in bytes + * + * On success, @tail is updated so that it longer includes the bytes of the + * returned header. + * + * Return: pointer to the first @len logical bytes of the tail, or to + * a copy if that overruns the IO vector, is not contiguous or + * doesn't have the requested alignment. NULL if that overruns the + * IO vector. + */ +void *iov_remove_header_(struct iov_tail *tail, void *v, size_t len, size_t align) +{ + char *p = iov_peek_header_(tail, v, len, align); + + if (!p) + return NULL; + + tail->off = tail->off + len; + + return p; +} + +/** + * iov_tail_clone() - Clone an iov tail into a new iovec array + * + * @dst_iov: Pointer to the destination array of struct iovec describing + * the scatter/gather I/O vector to shallow copy to. + * @dst_iov_cnt: Maximum number of elements in the destination iov array. + * @tail: Pointer to the source iov_tail + * + * Return: the number of elements successfully referenced from the destination + * iov array, a negative value if there is not enough room in the + * destination iov array + */ +ssize_t iov_tail_clone(struct iovec *dst_iov, size_t dst_iov_cnt, + struct iov_tail *tail) +{ + const struct iovec *iov = &tail->iov[0]; + size_t iov_cnt = tail->cnt; + size_t offset = tail->off; + unsigned int i, j; + + i = iov_skip_bytes(iov, iov_cnt, offset, &offset); + + /* assign iov references referencing a subset of the source one */ + for (j = 0; i < iov_cnt && j < dst_iov_cnt; i++, j++) { + dst_iov[j].iov_base = (char *)iov[i].iov_base + offset; + dst_iov[j].iov_len = iov[i].iov_len - offset; + offset = 0; + } + + if (j == dst_iov_cnt && i != iov_cnt) + return -1; + + return j; +} |