qemu/migration/qemu-file-buf.c
<<
>>
Prefs
   1/*
   2 * QEMU System Emulator
   3 *
   4 * Copyright (c) 2003-2008 Fabrice Bellard
   5 *
   6 * Permission is hereby granted, free of charge, to any person obtaining a copy
   7 * of this software and associated documentation files (the "Software"), to deal
   8 * in the Software without restriction, including without limitation the rights
   9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  10 * copies of the Software, and to permit persons to whom the Software is
  11 * furnished to do so, subject to the following conditions:
  12 *
  13 * The above copyright notice and this permission notice shall be included in
  14 * all copies or substantial portions of the Software.
  15 *
  16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
  19 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  22 * THE SOFTWARE.
  23 */
  24#include "qemu-common.h"
  25#include "qemu/iov.h"
  26#include "qemu/sockets.h"
  27#include "block/coroutine.h"
  28#include "migration/migration.h"
  29#include "migration/qemu-file.h"
  30#include "migration/qemu-file-internal.h"
  31#include "trace.h"
  32
  33#define QSB_CHUNK_SIZE      (1 << 10)
  34#define QSB_MAX_CHUNK_SIZE  (16 * QSB_CHUNK_SIZE)
  35
  36/**
  37 * Create a QEMUSizedBuffer
  38 * This type of buffer uses scatter-gather lists internally and
  39 * can grow to any size. Any data array in the scatter-gather list
  40 * can hold different amount of bytes.
  41 *
  42 * @buffer: Optional buffer to copy into the QSB
  43 * @len: size of initial buffer; if @buffer is given, buffer must
  44 *       hold at least len bytes
  45 *
  46 * Returns a pointer to a QEMUSizedBuffer or NULL on allocation failure
  47 */
  48QEMUSizedBuffer *qsb_create(const uint8_t *buffer, size_t len)
  49{
  50    QEMUSizedBuffer *qsb;
  51    size_t alloc_len, num_chunks, i, to_copy;
  52    size_t chunk_size = (len > QSB_MAX_CHUNK_SIZE)
  53                        ? QSB_MAX_CHUNK_SIZE
  54                        : QSB_CHUNK_SIZE;
  55
  56    num_chunks = DIV_ROUND_UP(len ? len : QSB_CHUNK_SIZE, chunk_size);
  57    alloc_len = num_chunks * chunk_size;
  58
  59    qsb = g_try_new0(QEMUSizedBuffer, 1);
  60    if (!qsb) {
  61        return NULL;
  62    }
  63
  64    qsb->iov = g_try_new0(struct iovec, num_chunks);
  65    if (!qsb->iov) {
  66        g_free(qsb);
  67        return NULL;
  68    }
  69
  70    qsb->n_iov = num_chunks;
  71
  72    for (i = 0; i < num_chunks; i++) {
  73        qsb->iov[i].iov_base = g_try_malloc0(chunk_size);
  74        if (!qsb->iov[i].iov_base) {
  75            /* qsb_free is safe since g_free can cope with NULL */
  76            qsb_free(qsb);
  77            return NULL;
  78        }
  79
  80        qsb->iov[i].iov_len = chunk_size;
  81        if (buffer) {
  82            to_copy = (len - qsb->used) > chunk_size
  83                      ? chunk_size : (len - qsb->used);
  84            memcpy(qsb->iov[i].iov_base, &buffer[qsb->used], to_copy);
  85            qsb->used += to_copy;
  86        }
  87    }
  88
  89    qsb->size = alloc_len;
  90
  91    return qsb;
  92}
  93
  94/**
  95 * Free the QEMUSizedBuffer
  96 *
  97 * @qsb: The QEMUSizedBuffer to free
  98 */
  99void qsb_free(QEMUSizedBuffer *qsb)
 100{
 101    size_t i;
 102
 103    if (!qsb) {
 104        return;
 105    }
 106
 107    for (i = 0; i < qsb->n_iov; i++) {
 108        g_free(qsb->iov[i].iov_base);
 109    }
 110    g_free(qsb->iov);
 111    g_free(qsb);
 112}
 113
 114/**
 115 * Get the number of used bytes in the QEMUSizedBuffer
 116 *
 117 * @qsb: A QEMUSizedBuffer
 118 *
 119 * Returns the number of bytes currently used in this buffer
 120 */
 121size_t qsb_get_length(const QEMUSizedBuffer *qsb)
 122{
 123    return qsb->used;
 124}
 125
 126/**
 127 * Set the length of the buffer; the primary usage of this
 128 * function is to truncate the number of used bytes in the buffer.
 129 * The size will not be extended beyond the current number of
 130 * allocated bytes in the QEMUSizedBuffer.
 131 *
 132 * @qsb: A QEMUSizedBuffer
 133 * @new_len: The new length of bytes in the buffer
 134 *
 135 * Returns the number of bytes the buffer was truncated or extended
 136 * to.
 137 */
 138size_t qsb_set_length(QEMUSizedBuffer *qsb, size_t new_len)
 139{
 140    if (new_len <= qsb->size) {
 141        qsb->used = new_len;
 142    } else {
 143        qsb->used = qsb->size;
 144    }
 145    return qsb->used;
 146}
 147
 148/**
 149 * Get the iovec that holds the data for a given position @pos.
 150 *
 151 * @qsb: A QEMUSizedBuffer
 152 * @pos: The index of a byte in the buffer
 153 * @d_off: Pointer to an offset that this function will indicate
 154 *         at what position within the returned iovec the byte
 155 *         is to be found
 156 *
 157 * Returns the index of the iovec that holds the byte at the given
 158 * index @pos in the byte stream; a negative number if the iovec
 159 * for the given position @pos does not exist.
 160 */
 161static ssize_t qsb_get_iovec(const QEMUSizedBuffer *qsb,
 162                             off_t pos, off_t *d_off)
 163{
 164    ssize_t i;
 165    off_t curr = 0;
 166
 167    if (pos > qsb->used) {
 168        return -1;
 169    }
 170
 171    for (i = 0; i < qsb->n_iov; i++) {
 172        if (curr + qsb->iov[i].iov_len > pos) {
 173            *d_off = pos - curr;
 174            return i;
 175        }
 176        curr += qsb->iov[i].iov_len;
 177    }
 178    return -1;
 179}
 180
 181/*
 182 * Convert the QEMUSizedBuffer into a flat buffer.
 183 *
 184 * Note: If at all possible, try to avoid this function since it
 185 *       may unnecessarily copy memory around.
 186 *
 187 * @qsb: pointer to QEMUSizedBuffer
 188 * @start: offset to start at
 189 * @count: number of bytes to copy
 190 * @buf: a pointer to a buffer to write into (at least @count bytes)
 191 *
 192 * Returns the number of bytes copied into the output buffer
 193 */
 194ssize_t qsb_get_buffer(const QEMUSizedBuffer *qsb, off_t start,
 195                       size_t count, uint8_t *buffer)
 196{
 197    const struct iovec *iov;
 198    size_t to_copy, all_copy;
 199    ssize_t index;
 200    off_t s_off;
 201    off_t d_off = 0;
 202    char *s;
 203
 204    if (start > qsb->used) {
 205        return 0;
 206    }
 207
 208    all_copy = qsb->used - start;
 209    if (all_copy > count) {
 210        all_copy = count;
 211    } else {
 212        count = all_copy;
 213    }
 214
 215    index = qsb_get_iovec(qsb, start, &s_off);
 216    if (index < 0) {
 217        return 0;
 218    }
 219
 220    while (all_copy > 0) {
 221        iov = &qsb->iov[index];
 222
 223        s = iov->iov_base;
 224
 225        to_copy = iov->iov_len - s_off;
 226        if (to_copy > all_copy) {
 227            to_copy = all_copy;
 228        }
 229        memcpy(&buffer[d_off], &s[s_off], to_copy);
 230
 231        d_off += to_copy;
 232        all_copy -= to_copy;
 233
 234        s_off = 0;
 235        index++;
 236    }
 237
 238    return count;
 239}
 240
 241/**
 242 * Grow the QEMUSizedBuffer to the given size and allocate
 243 * memory for it.
 244 *
 245 * @qsb: A QEMUSizedBuffer
 246 * @new_size: The new size of the buffer
 247 *
 248 * Return:
 249 *    a negative error code in case of memory allocation failure
 250 * or
 251 *    the new size of the buffer. The returned size may be greater or equal
 252 *    to @new_size.
 253 */
 254static ssize_t qsb_grow(QEMUSizedBuffer *qsb, size_t new_size)
 255{
 256    size_t needed_chunks, i;
 257
 258    if (qsb->size < new_size) {
 259        struct iovec *new_iov;
 260        size_t size_diff = new_size - qsb->size;
 261        size_t chunk_size = (size_diff > QSB_MAX_CHUNK_SIZE)
 262                             ? QSB_MAX_CHUNK_SIZE : QSB_CHUNK_SIZE;
 263
 264        needed_chunks = DIV_ROUND_UP(size_diff, chunk_size);
 265
 266        new_iov = g_try_new(struct iovec, qsb->n_iov + needed_chunks);
 267        if (new_iov == NULL) {
 268            return -ENOMEM;
 269        }
 270
 271        /* Allocate new chunks as needed into new_iov */
 272        for (i = qsb->n_iov; i < qsb->n_iov + needed_chunks; i++) {
 273            new_iov[i].iov_base = g_try_malloc0(chunk_size);
 274            new_iov[i].iov_len = chunk_size;
 275            if (!new_iov[i].iov_base) {
 276                size_t j;
 277
 278                /* Free previously allocated new chunks */
 279                for (j = qsb->n_iov; j < i; j++) {
 280                    g_free(new_iov[j].iov_base);
 281                }
 282                g_free(new_iov);
 283
 284                return -ENOMEM;
 285            }
 286        }
 287
 288        /*
 289         * Now we can't get any allocation errors, copy over to new iov
 290         * and switch.
 291         */
 292        for (i = 0; i < qsb->n_iov; i++) {
 293            new_iov[i] = qsb->iov[i];
 294        }
 295
 296        qsb->n_iov += needed_chunks;
 297        g_free(qsb->iov);
 298        qsb->iov = new_iov;
 299        qsb->size += (needed_chunks * chunk_size);
 300    }
 301
 302    return qsb->size;
 303}
 304
 305/**
 306 * Write into the QEMUSizedBuffer at a given position and a given
 307 * number of bytes. This function will automatically grow the
 308 * QEMUSizedBuffer.
 309 *
 310 * @qsb: A QEMUSizedBuffer
 311 * @source: A byte array to copy data from
 312 * @pos: The position within the @qsb to write data to
 313 * @size: The number of bytes to copy into the @qsb
 314 *
 315 * Returns @size or a negative error code in case of memory allocation failure,
 316 *           or with an invalid 'pos'
 317 */
 318ssize_t qsb_write_at(QEMUSizedBuffer *qsb, const uint8_t *source,
 319                     off_t pos, size_t count)
 320{
 321    ssize_t rc = qsb_grow(qsb, pos + count);
 322    size_t to_copy;
 323    size_t all_copy = count;
 324    const struct iovec *iov;
 325    ssize_t index;
 326    char *dest;
 327    off_t d_off, s_off = 0;
 328
 329    if (rc < 0) {
 330        return rc;
 331    }
 332
 333    if (pos + count > qsb->used) {
 334        qsb->used = pos + count;
 335    }
 336
 337    index = qsb_get_iovec(qsb, pos, &d_off);
 338    if (index < 0) {
 339        return -EINVAL;
 340    }
 341
 342    while (all_copy > 0) {
 343        iov = &qsb->iov[index];
 344
 345        dest = iov->iov_base;
 346
 347        to_copy = iov->iov_len - d_off;
 348        if (to_copy > all_copy) {
 349            to_copy = all_copy;
 350        }
 351
 352        memcpy(&dest[d_off], &source[s_off], to_copy);
 353
 354        s_off += to_copy;
 355        all_copy -= to_copy;
 356
 357        d_off = 0;
 358        index++;
 359    }
 360
 361    return count;
 362}
 363
 364/**
 365 * Create a deep copy of the given QEMUSizedBuffer.
 366 *
 367 * @qsb: A QEMUSizedBuffer
 368 *
 369 * Returns a clone of @qsb or NULL on allocation failure
 370 */
 371QEMUSizedBuffer *qsb_clone(const QEMUSizedBuffer *qsb)
 372{
 373    QEMUSizedBuffer *out = qsb_create(NULL, qsb_get_length(qsb));
 374    size_t i;
 375    ssize_t res;
 376    off_t pos = 0;
 377
 378    if (!out) {
 379        return NULL;
 380    }
 381
 382    for (i = 0; i < qsb->n_iov; i++) {
 383        res =  qsb_write_at(out, qsb->iov[i].iov_base,
 384                            pos, qsb->iov[i].iov_len);
 385        if (res < 0) {
 386            qsb_free(out);
 387            return NULL;
 388        }
 389        pos += res;
 390    }
 391
 392    return out;
 393}
 394
 395typedef struct QEMUBuffer {
 396    QEMUSizedBuffer *qsb;
 397    QEMUFile *file;
 398} QEMUBuffer;
 399
 400static int buf_get_buffer(void *opaque, uint8_t *buf, int64_t pos, int size)
 401{
 402    QEMUBuffer *s = opaque;
 403    ssize_t len = qsb_get_length(s->qsb) - pos;
 404
 405    if (len <= 0) {
 406        return 0;
 407    }
 408
 409    if (len > size) {
 410        len = size;
 411    }
 412    return qsb_get_buffer(s->qsb, pos, len, buf);
 413}
 414
 415static int buf_put_buffer(void *opaque, const uint8_t *buf,
 416                          int64_t pos, int size)
 417{
 418    QEMUBuffer *s = opaque;
 419
 420    return qsb_write_at(s->qsb, buf, pos, size);
 421}
 422
 423static int buf_close(void *opaque)
 424{
 425    QEMUBuffer *s = opaque;
 426
 427    qsb_free(s->qsb);
 428
 429    g_free(s);
 430
 431    return 0;
 432}
 433
 434const QEMUSizedBuffer *qemu_buf_get(QEMUFile *f)
 435{
 436    QEMUBuffer *p;
 437
 438    qemu_fflush(f);
 439
 440    p = f->opaque;
 441
 442    return p->qsb;
 443}
 444
 445static const QEMUFileOps buf_read_ops = {
 446    .get_buffer = buf_get_buffer,
 447    .close =      buf_close,
 448};
 449
 450static const QEMUFileOps buf_write_ops = {
 451    .put_buffer = buf_put_buffer,
 452    .close =      buf_close,
 453};
 454
 455QEMUFile *qemu_bufopen(const char *mode, QEMUSizedBuffer *input)
 456{
 457    QEMUBuffer *s;
 458
 459    if (mode == NULL || (mode[0] != 'r' && mode[0] != 'w') ||
 460        mode[1] != '\0') {
 461        error_report("qemu_bufopen: Argument validity check failed");
 462        return NULL;
 463    }
 464
 465    s = g_malloc0(sizeof(QEMUBuffer));
 466    if (mode[0] == 'r') {
 467        s->qsb = input;
 468    }
 469
 470    if (s->qsb == NULL) {
 471        s->qsb = qsb_create(NULL, 0);
 472    }
 473    if (!s->qsb) {
 474        g_free(s);
 475        error_report("qemu_bufopen: qsb_create failed");
 476        return NULL;
 477    }
 478
 479
 480    if (mode[0] == 'r') {
 481        s->file = qemu_fopen_ops(s, &buf_read_ops);
 482    } else {
 483        s->file = qemu_fopen_ops(s, &buf_write_ops);
 484    }
 485    return s->file;
 486}
 487