cachepc-qemu

Fork of AMDESE/qemu with changes for cachepc side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-qemu
Log | Files | Refs | Submodules | LICENSE | sfeed.txt

iov.c (20521B)


      1/*
      2 * Helpers for getting linearized buffers from iov / filling buffers into iovs
      3 *
      4 * Copyright IBM, Corp. 2007, 2008
      5 * Copyright (C) 2010 Red Hat, Inc.
      6 *
      7 * Author(s):
      8 *  Anthony Liguori <aliguori@us.ibm.com>
      9 *  Amit Shah <amit.shah@redhat.com>
     10 *  Michael Tokarev <mjt@tls.msk.ru>
     11 *
     12 * This work is licensed under the terms of the GNU GPL, version 2.  See
     13 * the COPYING file in the top-level directory.
     14 *
     15 * Contributions after 2012-01-13 are licensed under the terms of the
     16 * GNU GPL, version 2 or (at your option) any later version.
     17 */
     18
     19#include "qemu/osdep.h"
     20#include "qemu-common.h"
     21#include "qemu/iov.h"
     22#include "qemu/sockets.h"
     23#include "qemu/cutils.h"
     24
     25size_t iov_from_buf_full(const struct iovec *iov, unsigned int iov_cnt,
     26                         size_t offset, const void *buf, size_t bytes)
     27{
     28    size_t done;
     29    unsigned int i;
     30    for (i = 0, done = 0; (offset || done < bytes) && i < iov_cnt; i++) {
     31        if (offset < iov[i].iov_len) {
     32            size_t len = MIN(iov[i].iov_len - offset, bytes - done);
     33            memcpy(iov[i].iov_base + offset, buf + done, len);
     34            done += len;
     35            offset = 0;
     36        } else {
     37            offset -= iov[i].iov_len;
     38        }
     39    }
     40    assert(offset == 0);
     41    return done;
     42}
     43
     44size_t iov_to_buf_full(const struct iovec *iov, const unsigned int iov_cnt,
     45                       size_t offset, void *buf, size_t bytes)
     46{
     47    size_t done;
     48    unsigned int i;
     49    for (i = 0, done = 0; (offset || done < bytes) && i < iov_cnt; i++) {
     50        if (offset < iov[i].iov_len) {
     51            size_t len = MIN(iov[i].iov_len - offset, bytes - done);
     52            memcpy(buf + done, iov[i].iov_base + offset, len);
     53            done += len;
     54            offset = 0;
     55        } else {
     56            offset -= iov[i].iov_len;
     57        }
     58    }
     59    assert(offset == 0);
     60    return done;
     61}
     62
     63size_t iov_memset(const struct iovec *iov, const unsigned int iov_cnt,
     64                  size_t offset, int fillc, size_t bytes)
     65{
     66    size_t done;
     67    unsigned int i;
     68    for (i = 0, done = 0; (offset || done < bytes) && i < iov_cnt; i++) {
     69        if (offset < iov[i].iov_len) {
     70            size_t len = MIN(iov[i].iov_len - offset, bytes - done);
     71            memset(iov[i].iov_base + offset, fillc, len);
     72            done += len;
     73            offset = 0;
     74        } else {
     75            offset -= iov[i].iov_len;
     76        }
     77    }
     78    assert(offset == 0);
     79    return done;
     80}
     81
     82size_t iov_size(const struct iovec *iov, const unsigned int iov_cnt)
     83{
     84    size_t len;
     85    unsigned int i;
     86
     87    len = 0;
     88    for (i = 0; i < iov_cnt; i++) {
     89        len += iov[i].iov_len;
     90    }
     91    return len;
     92}
     93
     94/* helper function for iov_send_recv() */
     95static ssize_t
     96do_send_recv(int sockfd, struct iovec *iov, unsigned iov_cnt, bool do_send)
     97{
     98#ifdef CONFIG_POSIX
     99    ssize_t ret;
    100    struct msghdr msg;
    101    memset(&msg, 0, sizeof(msg));
    102    msg.msg_iov = iov;
    103    msg.msg_iovlen = iov_cnt;
    104    do {
    105        ret = do_send
    106            ? sendmsg(sockfd, &msg, 0)
    107            : recvmsg(sockfd, &msg, 0);
    108    } while (ret < 0 && errno == EINTR);
    109    return ret;
    110#else
    111    /* else send piece-by-piece */
    112    /*XXX Note: windows has WSASend() and WSARecv() */
    113    unsigned i = 0;
    114    ssize_t ret = 0;
    115    while (i < iov_cnt) {
    116        ssize_t r = do_send
    117            ? send(sockfd, iov[i].iov_base, iov[i].iov_len, 0)
    118            : recv(sockfd, iov[i].iov_base, iov[i].iov_len, 0);
    119        if (r > 0) {
    120            ret += r;
    121        } else if (!r) {
    122            break;
    123        } else if (errno == EINTR) {
    124            continue;
    125        } else {
    126            /* else it is some "other" error,
    127             * only return if there was no data processed. */
    128            if (ret == 0) {
    129                ret = -1;
    130            }
    131            break;
    132        }
    133        i++;
    134    }
    135    return ret;
    136#endif
    137}
    138
    139ssize_t iov_send_recv(int sockfd, const struct iovec *_iov, unsigned iov_cnt,
    140                      size_t offset, size_t bytes,
    141                      bool do_send)
    142{
    143    ssize_t total = 0;
    144    ssize_t ret;
    145    size_t orig_len, tail;
    146    unsigned niov;
    147    struct iovec *local_iov, *iov;
    148
    149    if (bytes <= 0) {
    150        return 0;
    151    }
    152
    153    local_iov = g_new0(struct iovec, iov_cnt);
    154    iov_copy(local_iov, iov_cnt, _iov, iov_cnt, offset, bytes);
    155    offset = 0;
    156    iov = local_iov;
    157
    158    while (bytes > 0) {
    159        /* Find the start position, skipping `offset' bytes:
    160         * first, skip all full-sized vector elements, */
    161        for (niov = 0; niov < iov_cnt && offset >= iov[niov].iov_len; ++niov) {
    162            offset -= iov[niov].iov_len;
    163        }
    164
    165        /* niov == iov_cnt would only be valid if bytes == 0, which
    166         * we already ruled out in the loop condition.  */
    167        assert(niov < iov_cnt);
    168        iov += niov;
    169        iov_cnt -= niov;
    170
    171        if (offset) {
    172            /* second, skip `offset' bytes from the (now) first element,
    173             * undo it on exit */
    174            iov[0].iov_base += offset;
    175            iov[0].iov_len -= offset;
    176        }
    177        /* Find the end position skipping `bytes' bytes: */
    178        /* first, skip all full-sized elements */
    179        tail = bytes;
    180        for (niov = 0; niov < iov_cnt && iov[niov].iov_len <= tail; ++niov) {
    181            tail -= iov[niov].iov_len;
    182        }
    183        if (tail) {
    184            /* second, fixup the last element, and remember the original
    185             * length */
    186            assert(niov < iov_cnt);
    187            assert(iov[niov].iov_len > tail);
    188            orig_len = iov[niov].iov_len;
    189            iov[niov++].iov_len = tail;
    190            ret = do_send_recv(sockfd, iov, niov, do_send);
    191            /* Undo the changes above before checking for errors */
    192            iov[niov-1].iov_len = orig_len;
    193        } else {
    194            ret = do_send_recv(sockfd, iov, niov, do_send);
    195        }
    196        if (offset) {
    197            iov[0].iov_base -= offset;
    198            iov[0].iov_len += offset;
    199        }
    200
    201        if (ret < 0) {
    202            assert(errno != EINTR);
    203            g_free(local_iov);
    204            if (errno == EAGAIN && total > 0) {
    205                return total;
    206            }
    207            return -1;
    208        }
    209
    210        if (ret == 0 && !do_send) {
    211            /* recv returns 0 when the peer has performed an orderly
    212             * shutdown. */
    213            break;
    214        }
    215
    216        /* Prepare for the next iteration */
    217        offset += ret;
    218        total += ret;
    219        bytes -= ret;
    220    }
    221
    222    g_free(local_iov);
    223    return total;
    224}
    225
    226
    227void iov_hexdump(const struct iovec *iov, const unsigned int iov_cnt,
    228                 FILE *fp, const char *prefix, size_t limit)
    229{
    230    int v;
    231    size_t size = 0;
    232    char *buf;
    233
    234    for (v = 0; v < iov_cnt; v++) {
    235        size += iov[v].iov_len;
    236    }
    237    size = size > limit ? limit : size;
    238    buf = g_malloc(size);
    239    iov_to_buf(iov, iov_cnt, 0, buf, size);
    240    qemu_hexdump(fp, prefix, buf, size);
    241    g_free(buf);
    242}
    243
    244unsigned iov_copy(struct iovec *dst_iov, unsigned int dst_iov_cnt,
    245                 const struct iovec *iov, unsigned int iov_cnt,
    246                 size_t offset, size_t bytes)
    247{
    248    size_t len;
    249    unsigned int i, j;
    250    for (i = 0, j = 0;
    251         i < iov_cnt && j < dst_iov_cnt && (offset || bytes); i++) {
    252        if (offset >= iov[i].iov_len) {
    253            offset -= iov[i].iov_len;
    254            continue;
    255        }
    256        len = MIN(bytes, iov[i].iov_len - offset);
    257
    258        dst_iov[j].iov_base = iov[i].iov_base + offset;
    259        dst_iov[j].iov_len = len;
    260        j++;
    261        bytes -= len;
    262        offset = 0;
    263    }
    264    assert(offset == 0);
    265    return j;
    266}
    267
    268/* io vectors */
    269
    270void qemu_iovec_init(QEMUIOVector *qiov, int alloc_hint)
    271{
    272    qiov->iov = g_new(struct iovec, alloc_hint);
    273    qiov->niov = 0;
    274    qiov->nalloc = alloc_hint;
    275    qiov->size = 0;
    276}
    277
    278void qemu_iovec_init_external(QEMUIOVector *qiov, struct iovec *iov, int niov)
    279{
    280    int i;
    281
    282    qiov->iov = iov;
    283    qiov->niov = niov;
    284    qiov->nalloc = -1;
    285    qiov->size = 0;
    286    for (i = 0; i < niov; i++)
    287        qiov->size += iov[i].iov_len;
    288}
    289
    290void qemu_iovec_add(QEMUIOVector *qiov, void *base, size_t len)
    291{
    292    assert(qiov->nalloc != -1);
    293
    294    if (qiov->niov == qiov->nalloc) {
    295        qiov->nalloc = 2 * qiov->nalloc + 1;
    296        qiov->iov = g_renew(struct iovec, qiov->iov, qiov->nalloc);
    297    }
    298    qiov->iov[qiov->niov].iov_base = base;
    299    qiov->iov[qiov->niov].iov_len = len;
    300    qiov->size += len;
    301    ++qiov->niov;
    302}
    303
    304/*
    305 * Concatenates (partial) iovecs from src_iov to the end of dst.
    306 * It starts copying after skipping `soffset' bytes at the
    307 * beginning of src and adds individual vectors from src to
    308 * dst copies up to `sbytes' bytes total, or up to the end
    309 * of src_iov if it comes first.  This way, it is okay to specify
    310 * very large value for `sbytes' to indicate "up to the end
    311 * of src".
    312 * Only vector pointers are processed, not the actual data buffers.
    313 */
    314size_t qemu_iovec_concat_iov(QEMUIOVector *dst,
    315                             struct iovec *src_iov, unsigned int src_cnt,
    316                             size_t soffset, size_t sbytes)
    317{
    318    int i;
    319    size_t done;
    320
    321    if (!sbytes) {
    322        return 0;
    323    }
    324    assert(dst->nalloc != -1);
    325    for (i = 0, done = 0; done < sbytes && i < src_cnt; i++) {
    326        if (soffset < src_iov[i].iov_len) {
    327            size_t len = MIN(src_iov[i].iov_len - soffset, sbytes - done);
    328            qemu_iovec_add(dst, src_iov[i].iov_base + soffset, len);
    329            done += len;
    330            soffset = 0;
    331        } else {
    332            soffset -= src_iov[i].iov_len;
    333        }
    334    }
    335    assert(soffset == 0); /* offset beyond end of src */
    336
    337    return done;
    338}
    339
    340/*
    341 * Concatenates (partial) iovecs from src to the end of dst.
    342 * It starts copying after skipping `soffset' bytes at the
    343 * beginning of src and adds individual vectors from src to
    344 * dst copies up to `sbytes' bytes total, or up to the end
    345 * of src if it comes first.  This way, it is okay to specify
    346 * very large value for `sbytes' to indicate "up to the end
    347 * of src".
    348 * Only vector pointers are processed, not the actual data buffers.
    349 */
    350void qemu_iovec_concat(QEMUIOVector *dst,
    351                       QEMUIOVector *src, size_t soffset, size_t sbytes)
    352{
    353    qemu_iovec_concat_iov(dst, src->iov, src->niov, soffset, sbytes);
    354}
    355
    356/*
    357 * qiov_find_iov
    358 *
    359 * Return pointer to iovec structure, where byte at @offset in original vector
    360 * @iov exactly is.
    361 * Set @remaining_offset to be offset inside that iovec to the same byte.
    362 */
    363static struct iovec *iov_skip_offset(struct iovec *iov, size_t offset,
    364                                     size_t *remaining_offset)
    365{
    366    while (offset > 0 && offset >= iov->iov_len) {
    367        offset -= iov->iov_len;
    368        iov++;
    369    }
    370    *remaining_offset = offset;
    371
    372    return iov;
    373}
    374
    375/*
    376 * qiov_slice
    377 *
    378 * Find subarray of iovec's, containing requested range. @head would
    379 * be offset in first iov (returned by the function), @tail would be
    380 * count of extra bytes in last iovec (returned iov + @niov - 1).
    381 */
    382static struct iovec *qiov_slice(QEMUIOVector *qiov,
    383                                size_t offset, size_t len,
    384                                size_t *head, size_t *tail, int *niov)
    385{
    386    struct iovec *iov, *end_iov;
    387
    388    assert(offset + len <= qiov->size);
    389
    390    iov = iov_skip_offset(qiov->iov, offset, head);
    391    end_iov = iov_skip_offset(iov, *head + len, tail);
    392
    393    if (*tail > 0) {
    394        assert(*tail < end_iov->iov_len);
    395        *tail = end_iov->iov_len - *tail;
    396        end_iov++;
    397    }
    398
    399    *niov = end_iov - iov;
    400
    401    return iov;
    402}
    403
    404int qemu_iovec_subvec_niov(QEMUIOVector *qiov, size_t offset, size_t len)
    405{
    406    size_t head, tail;
    407    int niov;
    408
    409    qiov_slice(qiov, offset, len, &head, &tail, &niov);
    410
    411    return niov;
    412}
    413
    414/*
    415 * Compile new iovec, combining @head_buf buffer, sub-qiov of @mid_qiov,
    416 * and @tail_buf buffer into new qiov.
    417 */
    418int qemu_iovec_init_extended(
    419        QEMUIOVector *qiov,
    420        void *head_buf, size_t head_len,
    421        QEMUIOVector *mid_qiov, size_t mid_offset, size_t mid_len,
    422        void *tail_buf, size_t tail_len)
    423{
    424    size_t mid_head, mid_tail;
    425    int total_niov, mid_niov = 0;
    426    struct iovec *p, *mid_iov = NULL;
    427
    428    assert(mid_qiov->niov <= IOV_MAX);
    429
    430    if (SIZE_MAX - head_len < mid_len ||
    431        SIZE_MAX - head_len - mid_len < tail_len)
    432    {
    433        return -EINVAL;
    434    }
    435
    436    if (mid_len) {
    437        mid_iov = qiov_slice(mid_qiov, mid_offset, mid_len,
    438                             &mid_head, &mid_tail, &mid_niov);
    439    }
    440
    441    total_niov = !!head_len + mid_niov + !!tail_len;
    442    if (total_niov > IOV_MAX) {
    443        return -EINVAL;
    444    }
    445
    446    if (total_niov == 1) {
    447        qemu_iovec_init_buf(qiov, NULL, 0);
    448        p = &qiov->local_iov;
    449    } else {
    450        qiov->niov = qiov->nalloc = total_niov;
    451        qiov->size = head_len + mid_len + tail_len;
    452        p = qiov->iov = g_new(struct iovec, qiov->niov);
    453    }
    454
    455    if (head_len) {
    456        p->iov_base = head_buf;
    457        p->iov_len = head_len;
    458        p++;
    459    }
    460
    461    assert(!mid_niov == !mid_len);
    462    if (mid_niov) {
    463        memcpy(p, mid_iov, mid_niov * sizeof(*p));
    464        p[0].iov_base = (uint8_t *)p[0].iov_base + mid_head;
    465        p[0].iov_len -= mid_head;
    466        p[mid_niov - 1].iov_len -= mid_tail;
    467        p += mid_niov;
    468    }
    469
    470    if (tail_len) {
    471        p->iov_base = tail_buf;
    472        p->iov_len = tail_len;
    473    }
    474
    475    return 0;
    476}
    477
    478/*
    479 * Check if the contents of subrange of qiov data is all zeroes.
    480 */
    481bool qemu_iovec_is_zero(QEMUIOVector *qiov, size_t offset, size_t bytes)
    482{
    483    struct iovec *iov;
    484    size_t current_offset;
    485
    486    assert(offset + bytes <= qiov->size);
    487
    488    iov = iov_skip_offset(qiov->iov, offset, &current_offset);
    489
    490    while (bytes) {
    491        uint8_t *base = (uint8_t *)iov->iov_base + current_offset;
    492        size_t len = MIN(iov->iov_len - current_offset, bytes);
    493
    494        if (!buffer_is_zero(base, len)) {
    495            return false;
    496        }
    497
    498        current_offset = 0;
    499        bytes -= len;
    500        iov++;
    501    }
    502
    503    return true;
    504}
    505
    506void qemu_iovec_init_slice(QEMUIOVector *qiov, QEMUIOVector *source,
    507                           size_t offset, size_t len)
    508{
    509    int ret;
    510
    511    assert(source->size >= len);
    512    assert(source->size - len >= offset);
    513
    514    /* We shrink the request, so we can't overflow neither size_t nor MAX_IOV */
    515    ret = qemu_iovec_init_extended(qiov, NULL, 0, source, offset, len, NULL, 0);
    516    assert(ret == 0);
    517}
    518
    519void qemu_iovec_destroy(QEMUIOVector *qiov)
    520{
    521    if (qiov->nalloc != -1) {
    522        g_free(qiov->iov);
    523    }
    524
    525    memset(qiov, 0, sizeof(*qiov));
    526}
    527
    528void qemu_iovec_reset(QEMUIOVector *qiov)
    529{
    530    assert(qiov->nalloc != -1);
    531
    532    qiov->niov = 0;
    533    qiov->size = 0;
    534}
    535
    536size_t qemu_iovec_to_buf(QEMUIOVector *qiov, size_t offset,
    537                         void *buf, size_t bytes)
    538{
    539    return iov_to_buf(qiov->iov, qiov->niov, offset, buf, bytes);
    540}
    541
    542size_t qemu_iovec_from_buf(QEMUIOVector *qiov, size_t offset,
    543                           const void *buf, size_t bytes)
    544{
    545    return iov_from_buf(qiov->iov, qiov->niov, offset, buf, bytes);
    546}
    547
    548size_t qemu_iovec_memset(QEMUIOVector *qiov, size_t offset,
    549                         int fillc, size_t bytes)
    550{
    551    return iov_memset(qiov->iov, qiov->niov, offset, fillc, bytes);
    552}
    553
    554/**
    555 * Check that I/O vector contents are identical
    556 *
    557 * The IO vectors must have the same structure (same length of all parts).
    558 * A typical usage is to compare vectors created with qemu_iovec_clone().
    559 *
    560 * @a:          I/O vector
    561 * @b:          I/O vector
    562 * @ret:        Offset to first mismatching byte or -1 if match
    563 */
    564ssize_t qemu_iovec_compare(QEMUIOVector *a, QEMUIOVector *b)
    565{
    566    int i;
    567    ssize_t offset = 0;
    568
    569    assert(a->niov == b->niov);
    570    for (i = 0; i < a->niov; i++) {
    571        size_t len = 0;
    572        uint8_t *p = (uint8_t *)a->iov[i].iov_base;
    573        uint8_t *q = (uint8_t *)b->iov[i].iov_base;
    574
    575        assert(a->iov[i].iov_len == b->iov[i].iov_len);
    576        while (len < a->iov[i].iov_len && *p++ == *q++) {
    577            len++;
    578        }
    579
    580        offset += len;
    581
    582        if (len != a->iov[i].iov_len) {
    583            return offset;
    584        }
    585    }
    586    return -1;
    587}
    588
    589typedef struct {
    590    int src_index;
    591    struct iovec *src_iov;
    592    void *dest_base;
    593} IOVectorSortElem;
    594
    595static int sortelem_cmp_src_base(const void *a, const void *b)
    596{
    597    const IOVectorSortElem *elem_a = a;
    598    const IOVectorSortElem *elem_b = b;
    599
    600    /* Don't overflow */
    601    if (elem_a->src_iov->iov_base < elem_b->src_iov->iov_base) {
    602        return -1;
    603    } else if (elem_a->src_iov->iov_base > elem_b->src_iov->iov_base) {
    604        return 1;
    605    } else {
    606        return 0;
    607    }
    608}
    609
    610static int sortelem_cmp_src_index(const void *a, const void *b)
    611{
    612    const IOVectorSortElem *elem_a = a;
    613    const IOVectorSortElem *elem_b = b;
    614
    615    return elem_a->src_index - elem_b->src_index;
    616}
    617
    618/**
    619 * Copy contents of I/O vector
    620 *
    621 * The relative relationships of overlapping iovecs are preserved.  This is
    622 * necessary to ensure identical semantics in the cloned I/O vector.
    623 */
    624void qemu_iovec_clone(QEMUIOVector *dest, const QEMUIOVector *src, void *buf)
    625{
    626    IOVectorSortElem sortelems[src->niov];
    627    void *last_end;
    628    int i;
    629
    630    /* Sort by source iovecs by base address */
    631    for (i = 0; i < src->niov; i++) {
    632        sortelems[i].src_index = i;
    633        sortelems[i].src_iov = &src->iov[i];
    634    }
    635    qsort(sortelems, src->niov, sizeof(sortelems[0]), sortelem_cmp_src_base);
    636
    637    /* Allocate buffer space taking into account overlapping iovecs */
    638    last_end = NULL;
    639    for (i = 0; i < src->niov; i++) {
    640        struct iovec *cur = sortelems[i].src_iov;
    641        ptrdiff_t rewind = 0;
    642
    643        /* Detect overlap */
    644        if (last_end && last_end > cur->iov_base) {
    645            rewind = last_end - cur->iov_base;
    646        }
    647
    648        sortelems[i].dest_base = buf - rewind;
    649        buf += cur->iov_len - MIN(rewind, cur->iov_len);
    650        last_end = MAX(cur->iov_base + cur->iov_len, last_end);
    651    }
    652
    653    /* Sort by source iovec index and build destination iovec */
    654    qsort(sortelems, src->niov, sizeof(sortelems[0]), sortelem_cmp_src_index);
    655    for (i = 0; i < src->niov; i++) {
    656        qemu_iovec_add(dest, sortelems[i].dest_base, src->iov[i].iov_len);
    657    }
    658}
    659
    660void iov_discard_undo(IOVDiscardUndo *undo)
    661{
    662    /* Restore original iovec if it was modified */
    663    if (undo->modified_iov) {
    664        *undo->modified_iov = undo->orig;
    665    }
    666}
    667
    668size_t iov_discard_front_undoable(struct iovec **iov,
    669                                  unsigned int *iov_cnt,
    670                                  size_t bytes,
    671                                  IOVDiscardUndo *undo)
    672{
    673    size_t total = 0;
    674    struct iovec *cur;
    675
    676    if (undo) {
    677        undo->modified_iov = NULL;
    678    }
    679
    680    for (cur = *iov; *iov_cnt > 0; cur++) {
    681        if (cur->iov_len > bytes) {
    682            if (undo) {
    683                undo->modified_iov = cur;
    684                undo->orig = *cur;
    685            }
    686
    687            cur->iov_base += bytes;
    688            cur->iov_len -= bytes;
    689            total += bytes;
    690            break;
    691        }
    692
    693        bytes -= cur->iov_len;
    694        total += cur->iov_len;
    695        *iov_cnt -= 1;
    696    }
    697
    698    *iov = cur;
    699    return total;
    700}
    701
    702size_t iov_discard_front(struct iovec **iov, unsigned int *iov_cnt,
    703                         size_t bytes)
    704{
    705    return iov_discard_front_undoable(iov, iov_cnt, bytes, NULL);
    706}
    707
    708size_t iov_discard_back_undoable(struct iovec *iov,
    709                                 unsigned int *iov_cnt,
    710                                 size_t bytes,
    711                                 IOVDiscardUndo *undo)
    712{
    713    size_t total = 0;
    714    struct iovec *cur;
    715
    716    if (undo) {
    717        undo->modified_iov = NULL;
    718    }
    719
    720    if (*iov_cnt == 0) {
    721        return 0;
    722    }
    723
    724    cur = iov + (*iov_cnt - 1);
    725
    726    while (*iov_cnt > 0) {
    727        if (cur->iov_len > bytes) {
    728            if (undo) {
    729                undo->modified_iov = cur;
    730                undo->orig = *cur;
    731            }
    732
    733            cur->iov_len -= bytes;
    734            total += bytes;
    735            break;
    736        }
    737
    738        bytes -= cur->iov_len;
    739        total += cur->iov_len;
    740        cur--;
    741        *iov_cnt -= 1;
    742    }
    743
    744    return total;
    745}
    746
    747size_t iov_discard_back(struct iovec *iov, unsigned int *iov_cnt,
    748                        size_t bytes)
    749{
    750    return iov_discard_back_undoable(iov, iov_cnt, bytes, NULL);
    751}
    752
    753void qemu_iovec_discard_back(QEMUIOVector *qiov, size_t bytes)
    754{
    755    size_t total;
    756    unsigned int niov = qiov->niov;
    757
    758    assert(qiov->size >= bytes);
    759    total = iov_discard_back(qiov->iov, &niov, bytes);
    760    assert(total == bytes);
    761
    762    qiov->niov = niov;
    763    qiov->size -= bytes;
    764}