qemu

Форк
0
/
iov.c 
712 строк · 18.9 Кб
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/iov.h"
21
#include "qemu/sockets.h"
22
#include "qemu/cutils.h"
23

24
size_t iov_from_buf_full(const struct iovec *iov, unsigned int iov_cnt,
25
                         size_t offset, const void *buf, size_t bytes)
26
{
27
    size_t done;
28
    unsigned int i;
29
    for (i = 0, done = 0; (offset || done < bytes) && i < iov_cnt; i++) {
30
        if (offset < iov[i].iov_len) {
31
            size_t len = MIN(iov[i].iov_len - offset, bytes - done);
32
            memcpy(iov[i].iov_base + offset, buf + done, len);
33
            done += len;
34
            offset = 0;
35
        } else {
36
            offset -= iov[i].iov_len;
37
        }
38
    }
39
    assert(offset == 0);
40
    return done;
41
}
42

43
size_t iov_to_buf_full(const struct iovec *iov, const unsigned int iov_cnt,
44
                       size_t offset, void *buf, size_t bytes)
45
{
46
    size_t done;
47
    unsigned int i;
48
    for (i = 0, done = 0; (offset || done < bytes) && i < iov_cnt; i++) {
49
        if (offset < iov[i].iov_len) {
50
            size_t len = MIN(iov[i].iov_len - offset, bytes - done);
51
            memcpy(buf + done, iov[i].iov_base + offset, len);
52
            done += len;
53
            offset = 0;
54
        } else {
55
            offset -= iov[i].iov_len;
56
        }
57
    }
58
    assert(offset == 0);
59
    return done;
60
}
61

62
size_t iov_memset(const struct iovec *iov, const unsigned int iov_cnt,
63
                  size_t offset, int fillc, size_t bytes)
64
{
65
    size_t done;
66
    unsigned int i;
67
    for (i = 0, done = 0; (offset || done < bytes) && i < iov_cnt; i++) {
68
        if (offset < iov[i].iov_len) {
69
            size_t len = MIN(iov[i].iov_len - offset, bytes - done);
70
            memset(iov[i].iov_base + offset, fillc, len);
71
            done += len;
72
            offset = 0;
73
        } else {
74
            offset -= iov[i].iov_len;
75
        }
76
    }
77
    assert(offset == 0);
78
    return done;
79
}
80

81
size_t iov_size(const struct iovec *iov, const unsigned int iov_cnt)
82
{
83
    size_t len;
84
    unsigned int i;
85

86
    len = 0;
87
    for (i = 0; i < iov_cnt; i++) {
88
        len += iov[i].iov_len;
89
    }
90
    return len;
91
}
92

93
/* helper function for iov_send_recv() */
94
static ssize_t
95
do_send_recv(int sockfd, struct iovec *iov, unsigned iov_cnt, bool do_send)
96
{
97
#ifdef CONFIG_POSIX
98
    ssize_t ret;
99
    struct msghdr msg;
100
    memset(&msg, 0, sizeof(msg));
101
    msg.msg_iov = iov;
102
    msg.msg_iovlen = iov_cnt;
103
    do {
104
        ret = do_send
105
            ? sendmsg(sockfd, &msg, 0)
106
            : recvmsg(sockfd, &msg, 0);
107
    } while (ret < 0 && errno == EINTR);
108
    return ret;
109
#else
110
    /* else send piece-by-piece */
111
    /*XXX Note: windows has WSASend() and WSARecv() */
112
    unsigned i = 0;
113
    ssize_t ret = 0;
114
    ssize_t off = 0;
115
    while (i < iov_cnt) {
116
        ssize_t r = do_send
117
            ? send(sockfd, iov[i].iov_base + off, iov[i].iov_len - off, 0)
118
            : recv(sockfd, iov[i].iov_base + off, iov[i].iov_len - off, 0);
119
        if (r > 0) {
120
            ret += r;
121
            off += r;
122
            if (off < iov[i].iov_len) {
123
                continue;
124
            }
125
        } else if (!r) {
126
            break;
127
        } else if (errno == EINTR) {
128
            continue;
129
        } else {
130
            /* else it is some "other" error,
131
             * only return if there was no data processed. */
132
            if (ret == 0) {
133
                ret = -1;
134
            }
135
            break;
136
        }
137
        off = 0;
138
        i++;
139
    }
140
    return ret;
141
#endif
142
}
143

144
ssize_t iov_send_recv(int sockfd, const struct iovec *_iov, unsigned iov_cnt,
145
                      size_t offset, size_t bytes,
146
                      bool do_send)
147
{
148
    ssize_t total = 0;
149
    ssize_t ret;
150
    size_t orig_len, tail;
151
    unsigned niov;
152
    struct iovec *local_iov, *iov;
153

154
    if (bytes <= 0) {
155
        return 0;
156
    }
157

158
    local_iov = g_new0(struct iovec, iov_cnt);
159
    iov_copy(local_iov, iov_cnt, _iov, iov_cnt, offset, bytes);
160
    offset = 0;
161
    iov = local_iov;
162

163
    while (bytes > 0) {
164
        /* Find the start position, skipping `offset' bytes:
165
         * first, skip all full-sized vector elements, */
166
        for (niov = 0; niov < iov_cnt && offset >= iov[niov].iov_len; ++niov) {
167
            offset -= iov[niov].iov_len;
168
        }
169

170
        /* niov == iov_cnt would only be valid if bytes == 0, which
171
         * we already ruled out in the loop condition.  */
172
        assert(niov < iov_cnt);
173
        iov += niov;
174
        iov_cnt -= niov;
175

176
        if (offset) {
177
            /* second, skip `offset' bytes from the (now) first element,
178
             * undo it on exit */
179
            iov[0].iov_base += offset;
180
            iov[0].iov_len -= offset;
181
        }
182
        /* Find the end position skipping `bytes' bytes: */
183
        /* first, skip all full-sized elements */
184
        tail = bytes;
185
        for (niov = 0; niov < iov_cnt && iov[niov].iov_len <= tail; ++niov) {
186
            tail -= iov[niov].iov_len;
187
        }
188
        if (tail) {
189
            /* second, fixup the last element, and remember the original
190
             * length */
191
            assert(niov < iov_cnt);
192
            assert(iov[niov].iov_len > tail);
193
            orig_len = iov[niov].iov_len;
194
            iov[niov++].iov_len = tail;
195
            ret = do_send_recv(sockfd, iov, niov, do_send);
196
            /* Undo the changes above before checking for errors */
197
            iov[niov-1].iov_len = orig_len;
198
        } else {
199
            ret = do_send_recv(sockfd, iov, niov, do_send);
200
        }
201
        if (offset) {
202
            iov[0].iov_base -= offset;
203
            iov[0].iov_len += offset;
204
        }
205

206
        if (ret < 0) {
207
            assert(errno != EINTR);
208
            g_free(local_iov);
209
            if (errno == EAGAIN && total > 0) {
210
                return total;
211
            }
212
            return -1;
213
        }
214

215
        if (ret == 0 && !do_send) {
216
            /* recv returns 0 when the peer has performed an orderly
217
             * shutdown. */
218
            break;
219
        }
220

221
        /* Prepare for the next iteration */
222
        offset += ret;
223
        total += ret;
224
        bytes -= ret;
225
    }
226

227
    g_free(local_iov);
228
    return total;
229
}
230

231

232
void iov_hexdump(const struct iovec *iov, const unsigned int iov_cnt,
233
                 FILE *fp, const char *prefix, size_t limit)
234
{
235
    int v;
236
    size_t size = 0;
237
    char *buf;
238

239
    for (v = 0; v < iov_cnt; v++) {
240
        size += iov[v].iov_len;
241
    }
242
    size = size > limit ? limit : size;
243
    buf = g_malloc(size);
244
    iov_to_buf(iov, iov_cnt, 0, buf, size);
245
    qemu_hexdump(fp, prefix, buf, size);
246
    g_free(buf);
247
}
248

249
unsigned iov_copy(struct iovec *dst_iov, unsigned int dst_iov_cnt,
250
                 const struct iovec *iov, unsigned int iov_cnt,
251
                 size_t offset, size_t bytes)
252
{
253
    size_t len;
254
    unsigned int i, j;
255
    for (i = 0, j = 0;
256
         i < iov_cnt && j < dst_iov_cnt && (offset || bytes); i++) {
257
        if (offset >= iov[i].iov_len) {
258
            offset -= iov[i].iov_len;
259
            continue;
260
        }
261
        len = MIN(bytes, iov[i].iov_len - offset);
262

263
        dst_iov[j].iov_base = iov[i].iov_base + offset;
264
        dst_iov[j].iov_len = len;
265
        j++;
266
        bytes -= len;
267
        offset = 0;
268
    }
269
    assert(offset == 0);
270
    return j;
271
}
272

273
/* io vectors */
274

275
void qemu_iovec_init(QEMUIOVector *qiov, int alloc_hint)
276
{
277
    qiov->iov = g_new(struct iovec, alloc_hint);
278
    qiov->niov = 0;
279
    qiov->nalloc = alloc_hint;
280
    qiov->size = 0;
281
}
282

283
void qemu_iovec_init_external(QEMUIOVector *qiov, struct iovec *iov, int niov)
284
{
285
    int i;
286

287
    qiov->iov = iov;
288
    qiov->niov = niov;
289
    qiov->nalloc = -1;
290
    qiov->size = 0;
291
    for (i = 0; i < niov; i++)
292
        qiov->size += iov[i].iov_len;
293
}
294

295
void qemu_iovec_add(QEMUIOVector *qiov, void *base, size_t len)
296
{
297
    assert(qiov->nalloc != -1);
298

299
    if (qiov->niov == qiov->nalloc) {
300
        qiov->nalloc = 2 * qiov->nalloc + 1;
301
        qiov->iov = g_renew(struct iovec, qiov->iov, qiov->nalloc);
302
    }
303
    qiov->iov[qiov->niov].iov_base = base;
304
    qiov->iov[qiov->niov].iov_len = len;
305
    qiov->size += len;
306
    ++qiov->niov;
307
}
308

309
/*
310
 * Concatenates (partial) iovecs from src_iov to the end of dst.
311
 * It starts copying after skipping `soffset' bytes at the
312
 * beginning of src and adds individual vectors from src to
313
 * dst copies up to `sbytes' bytes total, or up to the end
314
 * of src_iov if it comes first.  This way, it is okay to specify
315
 * very large value for `sbytes' to indicate "up to the end
316
 * of src".
317
 * Only vector pointers are processed, not the actual data buffers.
318
 */
319
size_t qemu_iovec_concat_iov(QEMUIOVector *dst,
320
                             struct iovec *src_iov, unsigned int src_cnt,
321
                             size_t soffset, size_t sbytes)
322
{
323
    int i;
324
    size_t done;
325

326
    if (!sbytes) {
327
        return 0;
328
    }
329
    assert(dst->nalloc != -1);
330
    for (i = 0, done = 0; done < sbytes && i < src_cnt; i++) {
331
        if (soffset < src_iov[i].iov_len) {
332
            size_t len = MIN(src_iov[i].iov_len - soffset, sbytes - done);
333
            qemu_iovec_add(dst, src_iov[i].iov_base + soffset, len);
334
            done += len;
335
            soffset = 0;
336
        } else {
337
            soffset -= src_iov[i].iov_len;
338
        }
339
    }
340
    assert(soffset == 0); /* offset beyond end of src */
341

342
    return done;
343
}
344

345
/*
346
 * Concatenates (partial) iovecs from src to the end of dst.
347
 * It starts copying after skipping `soffset' bytes at the
348
 * beginning of src and adds individual vectors from src to
349
 * dst copies up to `sbytes' bytes total, or up to the end
350
 * of src if it comes first.  This way, it is okay to specify
351
 * very large value for `sbytes' to indicate "up to the end
352
 * of src".
353
 * Only vector pointers are processed, not the actual data buffers.
354
 */
355
void qemu_iovec_concat(QEMUIOVector *dst,
356
                       QEMUIOVector *src, size_t soffset, size_t sbytes)
357
{
358
    qemu_iovec_concat_iov(dst, src->iov, src->niov, soffset, sbytes);
359
}
360

361
/*
362
 * qiov_find_iov
363
 *
364
 * Return pointer to iovec structure, where byte at @offset in original vector
365
 * @iov exactly is.
366
 * Set @remaining_offset to be offset inside that iovec to the same byte.
367
 */
368
static struct iovec *iov_skip_offset(struct iovec *iov, size_t offset,
369
                                     size_t *remaining_offset)
370
{
371
    while (offset > 0 && offset >= iov->iov_len) {
372
        offset -= iov->iov_len;
373
        iov++;
374
    }
375
    *remaining_offset = offset;
376

377
    return iov;
378
}
379

380
/*
381
 * qemu_iovec_slice
382
 *
383
 * Find subarray of iovec's, containing requested range. @head would
384
 * be offset in first iov (returned by the function), @tail would be
385
 * count of extra bytes in last iovec (returned iov + @niov - 1).
386
 */
387
struct iovec *qemu_iovec_slice(QEMUIOVector *qiov,
388
                               size_t offset, size_t len,
389
                               size_t *head, size_t *tail, int *niov)
390
{
391
    struct iovec *iov, *end_iov;
392

393
    assert(offset + len <= qiov->size);
394

395
    iov = iov_skip_offset(qiov->iov, offset, head);
396
    end_iov = iov_skip_offset(iov, *head + len, tail);
397

398
    if (*tail > 0) {
399
        assert(*tail < end_iov->iov_len);
400
        *tail = end_iov->iov_len - *tail;
401
        end_iov++;
402
    }
403

404
    *niov = end_iov - iov;
405

406
    return iov;
407
}
408

409
int qemu_iovec_subvec_niov(QEMUIOVector *qiov, size_t offset, size_t len)
410
{
411
    size_t head, tail;
412
    int niov;
413

414
    qemu_iovec_slice(qiov, offset, len, &head, &tail, &niov);
415

416
    return niov;
417
}
418

419
/*
420
 * Check if the contents of subrange of qiov data is all zeroes.
421
 */
422
bool qemu_iovec_is_zero(QEMUIOVector *qiov, size_t offset, size_t bytes)
423
{
424
    struct iovec *iov;
425
    size_t current_offset;
426

427
    assert(offset + bytes <= qiov->size);
428

429
    iov = iov_skip_offset(qiov->iov, offset, &current_offset);
430

431
    while (bytes) {
432
        uint8_t *base = (uint8_t *)iov->iov_base + current_offset;
433
        size_t len = MIN(iov->iov_len - current_offset, bytes);
434

435
        if (!buffer_is_zero(base, len)) {
436
            return false;
437
        }
438

439
        current_offset = 0;
440
        bytes -= len;
441
        iov++;
442
    }
443

444
    return true;
445
}
446

447
void qemu_iovec_init_slice(QEMUIOVector *qiov, QEMUIOVector *source,
448
                           size_t offset, size_t len)
449
{
450
    struct iovec *slice_iov;
451
    int slice_niov;
452
    size_t slice_head, slice_tail;
453

454
    assert(source->size >= len);
455
    assert(source->size - len >= offset);
456

457
    slice_iov = qemu_iovec_slice(source, offset, len,
458
                                 &slice_head, &slice_tail, &slice_niov);
459
    if (slice_niov == 1) {
460
        qemu_iovec_init_buf(qiov, slice_iov[0].iov_base + slice_head, len);
461
    } else {
462
        qemu_iovec_init(qiov, slice_niov);
463
        qemu_iovec_concat_iov(qiov, slice_iov, slice_niov, slice_head, len);
464
    }
465
}
466

467
void qemu_iovec_destroy(QEMUIOVector *qiov)
468
{
469
    if (qiov->nalloc != -1) {
470
        g_free(qiov->iov);
471
    }
472

473
    memset(qiov, 0, sizeof(*qiov));
474
}
475

476
void qemu_iovec_reset(QEMUIOVector *qiov)
477
{
478
    assert(qiov->nalloc != -1);
479

480
    qiov->niov = 0;
481
    qiov->size = 0;
482
}
483

484
size_t qemu_iovec_to_buf(QEMUIOVector *qiov, size_t offset,
485
                         void *buf, size_t bytes)
486
{
487
    return iov_to_buf(qiov->iov, qiov->niov, offset, buf, bytes);
488
}
489

490
size_t qemu_iovec_from_buf(QEMUIOVector *qiov, size_t offset,
491
                           const void *buf, size_t bytes)
492
{
493
    return iov_from_buf(qiov->iov, qiov->niov, offset, buf, bytes);
494
}
495

496
size_t qemu_iovec_memset(QEMUIOVector *qiov, size_t offset,
497
                         int fillc, size_t bytes)
498
{
499
    return iov_memset(qiov->iov, qiov->niov, offset, fillc, bytes);
500
}
501

502
/**
503
 * Check that I/O vector contents are identical
504
 *
505
 * The IO vectors must have the same structure (same length of all parts).
506
 * A typical usage is to compare vectors created with qemu_iovec_clone().
507
 *
508
 * @a:          I/O vector
509
 * @b:          I/O vector
510
 * @ret:        Offset to first mismatching byte or -1 if match
511
 */
512
ssize_t qemu_iovec_compare(QEMUIOVector *a, QEMUIOVector *b)
513
{
514
    int i;
515
    ssize_t offset = 0;
516

517
    assert(a->niov == b->niov);
518
    for (i = 0; i < a->niov; i++) {
519
        size_t len = 0;
520
        uint8_t *p = (uint8_t *)a->iov[i].iov_base;
521
        uint8_t *q = (uint8_t *)b->iov[i].iov_base;
522

523
        assert(a->iov[i].iov_len == b->iov[i].iov_len);
524
        while (len < a->iov[i].iov_len && *p++ == *q++) {
525
            len++;
526
        }
527

528
        offset += len;
529

530
        if (len != a->iov[i].iov_len) {
531
            return offset;
532
        }
533
    }
534
    return -1;
535
}
536

537
typedef struct {
538
    int src_index;
539
    struct iovec *src_iov;
540
    void *dest_base;
541
} IOVectorSortElem;
542

543
static int sortelem_cmp_src_base(const void *a, const void *b)
544
{
545
    const IOVectorSortElem *elem_a = a;
546
    const IOVectorSortElem *elem_b = b;
547

548
    /* Don't overflow */
549
    if (elem_a->src_iov->iov_base < elem_b->src_iov->iov_base) {
550
        return -1;
551
    } else if (elem_a->src_iov->iov_base > elem_b->src_iov->iov_base) {
552
        return 1;
553
    } else {
554
        return 0;
555
    }
556
}
557

558
static int sortelem_cmp_src_index(const void *a, const void *b)
559
{
560
    const IOVectorSortElem *elem_a = a;
561
    const IOVectorSortElem *elem_b = b;
562

563
    return elem_a->src_index - elem_b->src_index;
564
}
565

566
/**
567
 * Copy contents of I/O vector
568
 *
569
 * The relative relationships of overlapping iovecs are preserved.  This is
570
 * necessary to ensure identical semantics in the cloned I/O vector.
571
 */
572
void qemu_iovec_clone(QEMUIOVector *dest, const QEMUIOVector *src, void *buf)
573
{
574
    g_autofree IOVectorSortElem *sortelems = g_new(IOVectorSortElem, src->niov);
575
    void *last_end;
576
    int i;
577

578
    /* Sort by source iovecs by base address */
579
    for (i = 0; i < src->niov; i++) {
580
        sortelems[i].src_index = i;
581
        sortelems[i].src_iov = &src->iov[i];
582
    }
583
    qsort(sortelems, src->niov, sizeof(sortelems[0]), sortelem_cmp_src_base);
584

585
    /* Allocate buffer space taking into account overlapping iovecs */
586
    last_end = NULL;
587
    for (i = 0; i < src->niov; i++) {
588
        struct iovec *cur = sortelems[i].src_iov;
589
        ptrdiff_t rewind = 0;
590

591
        /* Detect overlap */
592
        if (last_end && last_end > cur->iov_base) {
593
            rewind = last_end - cur->iov_base;
594
        }
595

596
        sortelems[i].dest_base = buf - rewind;
597
        buf += cur->iov_len - MIN(rewind, cur->iov_len);
598
        last_end = MAX(cur->iov_base + cur->iov_len, last_end);
599
    }
600

601
    /* Sort by source iovec index and build destination iovec */
602
    qsort(sortelems, src->niov, sizeof(sortelems[0]), sortelem_cmp_src_index);
603
    for (i = 0; i < src->niov; i++) {
604
        qemu_iovec_add(dest, sortelems[i].dest_base, src->iov[i].iov_len);
605
    }
606
}
607

608
void iov_discard_undo(IOVDiscardUndo *undo)
609
{
610
    /* Restore original iovec if it was modified */
611
    if (undo->modified_iov) {
612
        *undo->modified_iov = undo->orig;
613
    }
614
}
615

616
size_t iov_discard_front_undoable(struct iovec **iov,
617
                                  unsigned int *iov_cnt,
618
                                  size_t bytes,
619
                                  IOVDiscardUndo *undo)
620
{
621
    size_t total = 0;
622
    struct iovec *cur;
623

624
    if (undo) {
625
        undo->modified_iov = NULL;
626
    }
627

628
    for (cur = *iov; *iov_cnt > 0; cur++) {
629
        if (cur->iov_len > bytes) {
630
            if (undo) {
631
                undo->modified_iov = cur;
632
                undo->orig = *cur;
633
            }
634

635
            cur->iov_base += bytes;
636
            cur->iov_len -= bytes;
637
            total += bytes;
638
            break;
639
        }
640

641
        bytes -= cur->iov_len;
642
        total += cur->iov_len;
643
        *iov_cnt -= 1;
644
    }
645

646
    *iov = cur;
647
    return total;
648
}
649

650
size_t iov_discard_front(struct iovec **iov, unsigned int *iov_cnt,
651
                         size_t bytes)
652
{
653
    return iov_discard_front_undoable(iov, iov_cnt, bytes, NULL);
654
}
655

656
size_t iov_discard_back_undoable(struct iovec *iov,
657
                                 unsigned int *iov_cnt,
658
                                 size_t bytes,
659
                                 IOVDiscardUndo *undo)
660
{
661
    size_t total = 0;
662
    struct iovec *cur;
663

664
    if (undo) {
665
        undo->modified_iov = NULL;
666
    }
667

668
    if (*iov_cnt == 0) {
669
        return 0;
670
    }
671

672
    cur = iov + (*iov_cnt - 1);
673

674
    while (*iov_cnt > 0) {
675
        if (cur->iov_len > bytes) {
676
            if (undo) {
677
                undo->modified_iov = cur;
678
                undo->orig = *cur;
679
            }
680

681
            cur->iov_len -= bytes;
682
            total += bytes;
683
            break;
684
        }
685

686
        bytes -= cur->iov_len;
687
        total += cur->iov_len;
688
        cur--;
689
        *iov_cnt -= 1;
690
    }
691

692
    return total;
693
}
694

695
size_t iov_discard_back(struct iovec *iov, unsigned int *iov_cnt,
696
                        size_t bytes)
697
{
698
    return iov_discard_back_undoable(iov, iov_cnt, bytes, NULL);
699
}
700

701
void qemu_iovec_discard_back(QEMUIOVector *qiov, size_t bytes)
702
{
703
    size_t total;
704
    unsigned int niov = qiov->niov;
705

706
    assert(qiov->size >= bytes);
707
    total = iov_discard_back(qiov->iov, &niov, bytes);
708
    assert(total == bytes);
709

710
    qiov->niov = niov;
711
    qiov->size -= bytes;
712
}
713

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.