* GNU GPL, version 2 or (at your option) any later version.
*/
+#include "qemu/osdep.h"
#include "qemu/iov.h"
#include "qemu/sockets.h"
+#include "qemu/cutils.h"
-size_t iov_from_buf(const struct iovec *iov, unsigned int iov_cnt,
- size_t offset, const void *buf, size_t bytes)
+size_t iov_from_buf_full(const struct iovec *iov, unsigned int iov_cnt,
+ size_t offset, const void *buf, size_t bytes)
{
size_t done;
unsigned int i;
return done;
}
-size_t iov_to_buf(const struct iovec *iov, const unsigned int iov_cnt,
- size_t offset, void *buf, size_t bytes)
+size_t iov_to_buf_full(const struct iovec *iov, const unsigned int iov_cnt,
+ size_t offset, void *buf, size_t bytes)
{
size_t done;
unsigned int i;
/*XXX Note: windows has WSASend() and WSARecv() */
unsigned i = 0;
ssize_t ret = 0;
+ ssize_t off = 0;
while (i < iov_cnt) {
ssize_t r = do_send
- ? send(sockfd, iov[i].iov_base, iov[i].iov_len, 0)
- : recv(sockfd, iov[i].iov_base, iov[i].iov_len, 0);
+ ? send(sockfd, iov[i].iov_base + off, iov[i].iov_len - off, 0)
+ : recv(sockfd, iov[i].iov_base + off, iov[i].iov_len - off, 0);
if (r > 0) {
ret += r;
+ off += r;
+ if (off < iov[i].iov_len) {
+ continue;
+ }
} else if (!r) {
break;
} else if (errno == EINTR) {
}
break;
}
+ off = 0;
i++;
}
return ret;
#endif
}
-ssize_t iov_send_recv(int sockfd, struct iovec *iov, unsigned iov_cnt,
+ssize_t iov_send_recv(int sockfd, const struct iovec *_iov, unsigned iov_cnt,
size_t offset, size_t bytes,
bool do_send)
{
ssize_t ret;
size_t orig_len, tail;
unsigned niov;
+ struct iovec *local_iov, *iov;
+
+ if (bytes <= 0) {
+ return 0;
+ }
+
+ local_iov = g_new0(struct iovec, iov_cnt);
+ iov_copy(local_iov, iov_cnt, _iov, iov_cnt, offset, bytes);
+ offset = 0;
+ iov = local_iov;
while (bytes > 0) {
/* Find the start position, skipping `offset' bytes:
if (ret < 0) {
assert(errno != EINTR);
+ g_free(local_iov);
if (errno == EAGAIN && total > 0) {
return total;
}
bytes -= ret;
}
+ g_free(local_iov);
return total;
}
size = size > limit ? limit : size;
buf = g_malloc(size);
iov_to_buf(iov, iov_cnt, 0, buf, size);
- qemu_hexdump(buf, fp, prefix, size);
+ qemu_hexdump(fp, prefix, buf, size);
g_free(buf);
}
{
size_t len;
unsigned int i, j;
- for (i = 0, j = 0; i < iov_cnt && j < dst_iov_cnt && bytes; i++) {
+ for (i = 0, j = 0;
+ i < iov_cnt && j < dst_iov_cnt && (offset || bytes); i++) {
if (offset >= iov[i].iov_len) {
offset -= iov[i].iov_len;
continue;
void qemu_iovec_init(QEMUIOVector *qiov, int alloc_hint)
{
- qiov->iov = g_malloc(alloc_hint * sizeof(struct iovec));
+ qiov->iov = g_new(struct iovec, alloc_hint);
qiov->niov = 0;
qiov->nalloc = alloc_hint;
qiov->size = 0;
if (qiov->niov == qiov->nalloc) {
qiov->nalloc = 2 * qiov->nalloc + 1;
- qiov->iov = g_realloc(qiov->iov, qiov->nalloc * sizeof(struct iovec));
+ qiov->iov = g_renew(struct iovec, qiov->iov, qiov->nalloc);
}
qiov->iov[qiov->niov].iov_base = base;
qiov->iov[qiov->niov].iov_len = len;
* of src".
* Only vector pointers are processed, not the actual data buffers.
*/
-void qemu_iovec_concat_iov(QEMUIOVector *dst,
- struct iovec *src_iov, unsigned int src_cnt,
- size_t soffset, size_t sbytes)
+size_t qemu_iovec_concat_iov(QEMUIOVector *dst,
+ struct iovec *src_iov, unsigned int src_cnt,
+ size_t soffset, size_t sbytes)
{
int i;
size_t done;
if (!sbytes) {
- return;
+ return 0;
}
assert(dst->nalloc != -1);
for (i = 0, done = 0; done < sbytes && i < src_cnt; i++) {
}
}
assert(soffset == 0); /* offset beyond end of src */
+
+ return done;
}
/*
qemu_iovec_concat_iov(dst, src->iov, src->niov, soffset, sbytes);
}
+/*
+ * qiov_find_iov
+ *
+ * Return pointer to iovec structure, where byte at @offset in original vector
+ * @iov exactly is.
+ * Set @remaining_offset to be offset inside that iovec to the same byte.
+ */
+static struct iovec *iov_skip_offset(struct iovec *iov, size_t offset,
+ size_t *remaining_offset)
+{
+ while (offset > 0 && offset >= iov->iov_len) {
+ offset -= iov->iov_len;
+ iov++;
+ }
+ *remaining_offset = offset;
+
+ return iov;
+}
+
+/*
+ * qemu_iovec_slice
+ *
+ * Find subarray of iovec's, containing requested range. @head would
+ * be offset in first iov (returned by the function), @tail would be
+ * count of extra bytes in last iovec (returned iov + @niov - 1).
+ */
+struct iovec *qemu_iovec_slice(QEMUIOVector *qiov,
+ size_t offset, size_t len,
+ size_t *head, size_t *tail, int *niov)
+{
+ struct iovec *iov, *end_iov;
+
+ assert(offset + len <= qiov->size);
+
+ iov = iov_skip_offset(qiov->iov, offset, head);
+ end_iov = iov_skip_offset(iov, *head + len, tail);
+
+ if (*tail > 0) {
+ assert(*tail < end_iov->iov_len);
+ *tail = end_iov->iov_len - *tail;
+ end_iov++;
+ }
+
+ *niov = end_iov - iov;
+
+ return iov;
+}
+
+int qemu_iovec_subvec_niov(QEMUIOVector *qiov, size_t offset, size_t len)
+{
+ size_t head, tail;
+ int niov;
+
+ qemu_iovec_slice(qiov, offset, len, &head, &tail, &niov);
+
+ return niov;
+}
+
+/*
+ * Check if the contents of subrange of qiov data is all zeroes.
+ */
+bool qemu_iovec_is_zero(QEMUIOVector *qiov, size_t offset, size_t bytes)
+{
+ struct iovec *iov;
+ size_t current_offset;
+
+ assert(offset + bytes <= qiov->size);
+
+ iov = iov_skip_offset(qiov->iov, offset, ¤t_offset);
+
+ while (bytes) {
+ uint8_t *base = (uint8_t *)iov->iov_base + current_offset;
+ size_t len = MIN(iov->iov_len - current_offset, bytes);
+
+ if (!buffer_is_zero(base, len)) {
+ return false;
+ }
+
+ current_offset = 0;
+ bytes -= len;
+ iov++;
+ }
+
+ return true;
+}
+
+void qemu_iovec_init_slice(QEMUIOVector *qiov, QEMUIOVector *source,
+ size_t offset, size_t len)
+{
+ struct iovec *slice_iov;
+ int slice_niov;
+ size_t slice_head, slice_tail;
+
+ assert(source->size >= len);
+ assert(source->size - len >= offset);
+
+ slice_iov = qemu_iovec_slice(source, offset, len,
+ &slice_head, &slice_tail, &slice_niov);
+ if (slice_niov == 1) {
+ qemu_iovec_init_buf(qiov, slice_iov[0].iov_base + slice_head, len);
+ } else {
+ qemu_iovec_init(qiov, slice_niov);
+ qemu_iovec_concat_iov(qiov, slice_iov, slice_niov, slice_head, len);
+ }
+}
+
void qemu_iovec_destroy(QEMUIOVector *qiov)
{
- assert(qiov->nalloc != -1);
+ if (qiov->nalloc != -1) {
+ g_free(qiov->iov);
+ }
- qemu_iovec_reset(qiov);
- g_free(qiov->iov);
- qiov->nalloc = 0;
- qiov->iov = NULL;
+ memset(qiov, 0, sizeof(*qiov));
}
void qemu_iovec_reset(QEMUIOVector *qiov)
*/
void qemu_iovec_clone(QEMUIOVector *dest, const QEMUIOVector *src, void *buf)
{
- IOVectorSortElem sortelems[src->niov];
+ g_autofree IOVectorSortElem *sortelems = g_new(IOVectorSortElem, src->niov);
void *last_end;
int i;
}
}
-size_t iov_discard_front(struct iovec **iov, unsigned int *iov_cnt,
- size_t bytes)
+void iov_discard_undo(IOVDiscardUndo *undo)
+{
+ /* Restore original iovec if it was modified */
+ if (undo->modified_iov) {
+ *undo->modified_iov = undo->orig;
+ }
+}
+
+size_t iov_discard_front_undoable(struct iovec **iov,
+ unsigned int *iov_cnt,
+ size_t bytes,
+ IOVDiscardUndo *undo)
{
size_t total = 0;
struct iovec *cur;
+ if (undo) {
+ undo->modified_iov = NULL;
+ }
+
for (cur = *iov; *iov_cnt > 0; cur++) {
if (cur->iov_len > bytes) {
+ if (undo) {
+ undo->modified_iov = cur;
+ undo->orig = *cur;
+ }
+
cur->iov_base += bytes;
cur->iov_len -= bytes;
total += bytes;
return total;
}
-size_t iov_discard_back(struct iovec *iov, unsigned int *iov_cnt,
- size_t bytes)
+size_t iov_discard_front(struct iovec **iov, unsigned int *iov_cnt,
+ size_t bytes)
+{
+ return iov_discard_front_undoable(iov, iov_cnt, bytes, NULL);
+}
+
+size_t iov_discard_back_undoable(struct iovec *iov,
+ unsigned int *iov_cnt,
+ size_t bytes,
+ IOVDiscardUndo *undo)
{
size_t total = 0;
struct iovec *cur;
+ if (undo) {
+ undo->modified_iov = NULL;
+ }
+
if (*iov_cnt == 0) {
return 0;
}
while (*iov_cnt > 0) {
if (cur->iov_len > bytes) {
+ if (undo) {
+ undo->modified_iov = cur;
+ undo->orig = *cur;
+ }
+
cur->iov_len -= bytes;
total += bytes;
break;
return total;
}
+
+size_t iov_discard_back(struct iovec *iov, unsigned int *iov_cnt,
+ size_t bytes)
+{
+ return iov_discard_back_undoable(iov, iov_cnt, bytes, NULL);
+}
+
+void qemu_iovec_discard_back(QEMUIOVector *qiov, size_t bytes)
+{
+ size_t total;
+ unsigned int niov = qiov->niov;
+
+ assert(qiov->size >= bytes);
+ total = iov_discard_back(qiov->iov, &niov, bytes);
+ assert(total == bytes);
+
+ qiov->niov = niov;
+ qiov->size -= bytes;
+}