qemu/block/commit.c
<<
>>
Prefs
   1/*
   2 * Live block commit
   3 *
   4 * Copyright Red Hat, Inc. 2012
   5 *
   6 * Authors:
   7 *  Jeff Cody   <jcody@redhat.com>
   8 *  Based on stream.c by Stefan Hajnoczi
   9 *
  10 * This work is licensed under the terms of the GNU LGPL, version 2 or later.
  11 * See the COPYING.LIB file in the top-level directory.
  12 *
  13 */
  14
  15#include "trace.h"
  16#include "block_int.h"
  17#include "blockjob.h"
  18#include "qemu/ratelimit.h"
  19
  20enum {
  21    /*
  22     * Size of data buffer for populating the image file.  This should be large
  23     * enough to process multiple clusters in a single call, so that populating
  24     * contiguous regions of the image is efficient.
  25     */
  26    COMMIT_BUFFER_SIZE = 512 * 1024, /* in bytes */
  27};
  28
  29#define SLICE_TIME 100000000ULL /* ns */
  30
  31typedef struct CommitBlockJob {
  32    BlockJob common;
  33    RateLimit limit;
  34    BlockDriverState *active;
  35    BlockDriverState *top;
  36    BlockDriverState *base;
  37    BlockdevOnError on_error;
  38    int base_flags;
  39    int orig_overlay_flags;
  40} CommitBlockJob;
  41
  42static int coroutine_fn commit_populate(BlockDriverState *bs,
  43                                        BlockDriverState *base,
  44                                        int64_t sector_num, int nb_sectors,
  45                                        void *buf)
  46{
  47    int ret = 0;
  48
  49    ret = bdrv_read(bs, sector_num, buf, nb_sectors);
  50    if (ret) {
  51        return ret;
  52    }
  53
  54    ret = bdrv_write(base, sector_num, buf, nb_sectors);
  55    if (ret) {
  56        return ret;
  57    }
  58
  59    return 0;
  60}
  61
  62static void coroutine_fn commit_run(void *opaque)
  63{
  64    CommitBlockJob *s = opaque;
  65    BlockDriverState *active = s->active;
  66    BlockDriverState *top = s->top;
  67    BlockDriverState *base = s->base;
  68    BlockDriverState *overlay_bs = NULL;
  69    int64_t sector_num, end;
  70    int ret = 0;
  71    int n = 0;
  72    void *buf;
  73    int bytes_written = 0;
  74    int64_t base_len;
  75
  76    ret = s->common.len = bdrv_getlength(top);
  77
  78
  79    if (s->common.len < 0) {
  80        goto exit_restore_reopen;
  81    }
  82
  83    ret = base_len = bdrv_getlength(base);
  84    if (base_len < 0) {
  85        goto exit_restore_reopen;
  86    }
  87
  88    if (base_len < s->common.len) {
  89        ret = bdrv_truncate(base, s->common.len);
  90        if (ret) {
  91            goto exit_restore_reopen;
  92        }
  93    }
  94
  95    overlay_bs = bdrv_find_overlay(active, top);
  96
  97    end = s->common.len >> BDRV_SECTOR_BITS;
  98    buf = qemu_blockalign(top, COMMIT_BUFFER_SIZE);
  99
 100    for (sector_num = 0; sector_num < end; sector_num += n) {
 101        uint64_t delay_ns = 0;
 102        bool copy;
 103
 104wait:
 105        /* Note that even when no rate limit is applied we need to yield
 106         * with no pending I/O here so that qemu_aio_flush() returns.
 107         */
 108        block_job_sleep_ns(&s->common, rt_clock, delay_ns);
 109        if (block_job_is_cancelled(&s->common)) {
 110            break;
 111        }
 112        /* Copy if allocated above the base */
 113        ret = bdrv_co_is_allocated_above(top, base, sector_num,
 114                                         COMMIT_BUFFER_SIZE / BDRV_SECTOR_SIZE,
 115                                         &n);
 116        copy = (ret == 1);
 117        trace_commit_one_iteration(s, sector_num, n, ret);
 118        if (copy) {
 119            if (s->common.speed) {
 120                delay_ns = ratelimit_calculate_delay(&s->limit, n);
 121                if (delay_ns > 0) {
 122                    goto wait;
 123                }
 124            }
 125            ret = commit_populate(top, base, sector_num, n, buf);
 126            bytes_written += n * BDRV_SECTOR_SIZE;
 127        }
 128        if (ret < 0) {
 129            if (s->on_error == BLOCKDEV_ON_ERROR_STOP ||
 130                s->on_error == BLOCKDEV_ON_ERROR_REPORT||
 131                (s->on_error == BLOCKDEV_ON_ERROR_ENOSPC && ret == -ENOSPC)) {
 132                goto exit_free_buf;
 133            } else {
 134                n = 0;
 135                continue;
 136            }
 137        }
 138        /* Publish progress */
 139        s->common.offset += n * BDRV_SECTOR_SIZE;
 140    }
 141
 142    ret = 0;
 143
 144    if (!block_job_is_cancelled(&s->common) && sector_num == end) {
 145        /* success */
 146        ret = bdrv_drop_intermediate(active, top, base);
 147    }
 148
 149exit_free_buf:
 150    qemu_vfree(buf);
 151
 152exit_restore_reopen:
 153    /* restore base open flags here if appropriate (e.g., change the base back
 154     * to r/o). These reopens do not need to be atomic, since we won't abort
 155     * even on failure here */
 156    if (s->base_flags != bdrv_get_flags(base)) {
 157        bdrv_reopen(base, s->base_flags, NULL);
 158    }
 159    if (s->orig_overlay_flags != bdrv_get_flags(overlay_bs)) {
 160        bdrv_reopen(overlay_bs, s->orig_overlay_flags, NULL);
 161    }
 162
 163    block_job_completed(&s->common, ret);
 164}
 165
 166static void commit_set_speed(BlockJob *job, int64_t speed, Error **errp)
 167{
 168    CommitBlockJob *s = container_of(job, CommitBlockJob, common);
 169
 170    if (speed < 0) {
 171        error_set(errp, QERR_INVALID_PARAMETER, "speed");
 172        return;
 173    }
 174    ratelimit_set_speed(&s->limit, speed / BDRV_SECTOR_SIZE, SLICE_TIME);
 175}
 176
 177static BlockJobType commit_job_type = {
 178    .instance_size = sizeof(CommitBlockJob),
 179    .job_type      = "commit",
 180    .set_speed     = commit_set_speed,
 181};
 182
 183void commit_start(BlockDriverState *bs, BlockDriverState *base,
 184                  BlockDriverState *top, int64_t speed,
 185                  BlockdevOnError on_error, BlockDriverCompletionFunc *cb,
 186                  void *opaque, Error **errp)
 187{
 188    CommitBlockJob *s;
 189    BlockReopenQueue *reopen_queue = NULL;
 190    int orig_overlay_flags;
 191    int orig_base_flags;
 192    BlockDriverState *overlay_bs;
 193    Error *local_err = NULL;
 194
 195    if ((on_error == BLOCKDEV_ON_ERROR_STOP ||
 196         on_error == BLOCKDEV_ON_ERROR_ENOSPC) &&
 197        !bdrv_iostatus_is_enabled(bs)) {
 198        error_set(errp, QERR_INVALID_PARAMETER_COMBINATION);
 199        return;
 200    }
 201
 202    /* Once we support top == active layer, remove this check */
 203    if (top == bs) {
 204        error_setg(errp,
 205                   "Top image as the active layer is currently unsupported");
 206        return;
 207    }
 208
 209    if (top == base) {
 210        error_setg(errp, "Invalid files for merge: top and base are the same");
 211        return;
 212    }
 213
 214    overlay_bs = bdrv_find_overlay(bs, top);
 215
 216    if (overlay_bs == NULL) {
 217        error_setg(errp, "Could not find overlay image for %s:", top->filename);
 218        return;
 219    }
 220
 221    orig_base_flags    = bdrv_get_flags(base);
 222    orig_overlay_flags = bdrv_get_flags(overlay_bs);
 223
 224    /* convert base & overlay_bs to r/w, if necessary */
 225    if (!(orig_base_flags & BDRV_O_RDWR)) {
 226        reopen_queue = bdrv_reopen_queue(reopen_queue, base,
 227                                         orig_base_flags | BDRV_O_RDWR);
 228    }
 229    if (!(orig_overlay_flags & BDRV_O_RDWR)) {
 230        reopen_queue = bdrv_reopen_queue(reopen_queue, overlay_bs,
 231                                         orig_overlay_flags | BDRV_O_RDWR);
 232    }
 233    if (reopen_queue) {
 234        bdrv_reopen_multiple(reopen_queue, &local_err);
 235        if (local_err != NULL) {
 236            error_propagate(errp, local_err);
 237            return;
 238        }
 239    }
 240
 241
 242    s = block_job_create(&commit_job_type, bs, speed, cb, opaque, errp);
 243    if (!s) {
 244        return;
 245    }
 246
 247    s->base   = base;
 248    s->top    = top;
 249    s->active = bs;
 250
 251    s->base_flags          = orig_base_flags;
 252    s->orig_overlay_flags  = orig_overlay_flags;
 253
 254    s->on_error = on_error;
 255    s->common.co = qemu_coroutine_create(commit_run);
 256
 257    trace_commit_start(bs, base, top, s, s->common.co, opaque);
 258    qemu_coroutine_enter(s->common.co, s);
 259}
 260