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/block_int.h"
  17#include "block/blockjob.h"
  18#include "qapi/qmp/qerror.h"
  19#include "qemu/ratelimit.h"
  20#include "sysemu/block-backend.h"
  21
  22enum {
  23    /*
  24     * Size of data buffer for populating the image file.  This should be large
  25     * enough to process multiple clusters in a single call, so that populating
  26     * contiguous regions of the image is efficient.
  27     */
  28    COMMIT_BUFFER_SIZE = 512 * 1024, /* in bytes */
  29};
  30
  31#define SLICE_TIME 100000000ULL /* ns */
  32
  33typedef struct CommitBlockJob {
  34    BlockJob common;
  35    RateLimit limit;
  36    BlockDriverState *active;
  37    BlockDriverState *top;
  38    BlockDriverState *base;
  39    BlockdevOnError on_error;
  40    int base_flags;
  41    int orig_overlay_flags;
  42    char *backing_file_str;
  43} CommitBlockJob;
  44
  45static int coroutine_fn commit_populate(BlockDriverState *bs,
  46                                        BlockDriverState *base,
  47                                        int64_t sector_num, int nb_sectors,
  48                                        void *buf)
  49{
  50    int ret = 0;
  51
  52    ret = bdrv_read(bs, sector_num, buf, nb_sectors);
  53    if (ret) {
  54        return ret;
  55    }
  56
  57    ret = bdrv_write(base, sector_num, buf, nb_sectors);
  58    if (ret) {
  59        return ret;
  60    }
  61
  62    return 0;
  63}
  64
  65typedef struct {
  66    int ret;
  67} CommitCompleteData;
  68
  69static void commit_complete(BlockJob *job, void *opaque)
  70{
  71    CommitBlockJob *s = container_of(job, CommitBlockJob, common);
  72    CommitCompleteData *data = opaque;
  73    BlockDriverState *active = s->active;
  74    BlockDriverState *top = s->top;
  75    BlockDriverState *base = s->base;
  76    BlockDriverState *overlay_bs;
  77    int ret = data->ret;
  78
  79    if (!block_job_is_cancelled(&s->common) && ret == 0) {
  80        /* success */
  81        ret = bdrv_drop_intermediate(active, top, base, s->backing_file_str);
  82    }
  83
  84    /* restore base open flags here if appropriate (e.g., change the base back
  85     * to r/o). These reopens do not need to be atomic, since we won't abort
  86     * even on failure here */
  87    if (s->base_flags != bdrv_get_flags(base)) {
  88        bdrv_reopen(base, s->base_flags, NULL);
  89    }
  90    overlay_bs = bdrv_find_overlay(active, top);
  91    if (overlay_bs && s->orig_overlay_flags != bdrv_get_flags(overlay_bs)) {
  92        bdrv_reopen(overlay_bs, s->orig_overlay_flags, NULL);
  93    }
  94    g_free(s->backing_file_str);
  95    block_job_completed(&s->common, ret);
  96    g_free(data);
  97}
  98
  99static void coroutine_fn commit_run(void *opaque)
 100{
 101    CommitBlockJob *s = opaque;
 102    CommitCompleteData *data;
 103    BlockDriverState *top = s->top;
 104    BlockDriverState *base = s->base;
 105    int64_t sector_num, end;
 106    int ret = 0;
 107    int n = 0;
 108    void *buf = NULL;
 109    int bytes_written = 0;
 110    int64_t base_len;
 111
 112    ret = s->common.len = bdrv_getlength(top);
 113
 114
 115    if (s->common.len < 0) {
 116        goto out;
 117    }
 118
 119    ret = base_len = bdrv_getlength(base);
 120    if (base_len < 0) {
 121        goto out;
 122    }
 123
 124    if (base_len < s->common.len) {
 125        ret = bdrv_truncate(base, s->common.len);
 126        if (ret) {
 127            goto out;
 128        }
 129    }
 130
 131    end = s->common.len >> BDRV_SECTOR_BITS;
 132    buf = qemu_blockalign(top, COMMIT_BUFFER_SIZE);
 133
 134    for (sector_num = 0; sector_num < end; sector_num += n) {
 135        uint64_t delay_ns = 0;
 136        bool copy;
 137
 138wait:
 139        /* Note that even when no rate limit is applied we need to yield
 140         * with no pending I/O here so that bdrv_drain_all() returns.
 141         */
 142        block_job_sleep_ns(&s->common, QEMU_CLOCK_REALTIME, delay_ns);
 143        if (block_job_is_cancelled(&s->common)) {
 144            break;
 145        }
 146        /* Copy if allocated above the base */
 147        ret = bdrv_is_allocated_above(top, base, sector_num,
 148                                      COMMIT_BUFFER_SIZE / BDRV_SECTOR_SIZE,
 149                                      &n);
 150        copy = (ret == 1);
 151        trace_commit_one_iteration(s, sector_num, n, ret);
 152        if (copy) {
 153            if (s->common.speed) {
 154                delay_ns = ratelimit_calculate_delay(&s->limit, n);
 155                if (delay_ns > 0) {
 156                    goto wait;
 157                }
 158            }
 159            ret = commit_populate(top, base, sector_num, n, buf);
 160            bytes_written += n * BDRV_SECTOR_SIZE;
 161        }
 162        if (ret < 0) {
 163            if (s->on_error == BLOCKDEV_ON_ERROR_STOP ||
 164                s->on_error == BLOCKDEV_ON_ERROR_REPORT||
 165                (s->on_error == BLOCKDEV_ON_ERROR_ENOSPC && ret == -ENOSPC)) {
 166                goto out;
 167            } else {
 168                n = 0;
 169                continue;
 170            }
 171        }
 172        /* Publish progress */
 173        s->common.offset += n * BDRV_SECTOR_SIZE;
 174    }
 175
 176    ret = 0;
 177
 178out:
 179    qemu_vfree(buf);
 180
 181    data = g_malloc(sizeof(*data));
 182    data->ret = ret;
 183    block_job_defer_to_main_loop(&s->common, commit_complete, data);
 184}
 185
 186static void commit_set_speed(BlockJob *job, int64_t speed, Error **errp)
 187{
 188    CommitBlockJob *s = container_of(job, CommitBlockJob, common);
 189
 190    if (speed < 0) {
 191        error_setg(errp, QERR_INVALID_PARAMETER, "speed");
 192        return;
 193    }
 194    ratelimit_set_speed(&s->limit, speed / BDRV_SECTOR_SIZE, SLICE_TIME);
 195}
 196
 197static const BlockJobDriver commit_job_driver = {
 198    .instance_size = sizeof(CommitBlockJob),
 199    .job_type      = BLOCK_JOB_TYPE_COMMIT,
 200    .set_speed     = commit_set_speed,
 201};
 202
 203void commit_start(BlockDriverState *bs, BlockDriverState *base,
 204                  BlockDriverState *top, int64_t speed,
 205                  BlockdevOnError on_error, BlockCompletionFunc *cb,
 206                  void *opaque, const char *backing_file_str, Error **errp)
 207{
 208    CommitBlockJob *s;
 209    BlockReopenQueue *reopen_queue = NULL;
 210    int orig_overlay_flags;
 211    int orig_base_flags;
 212    BlockDriverState *overlay_bs;
 213    Error *local_err = NULL;
 214
 215    if ((on_error == BLOCKDEV_ON_ERROR_STOP ||
 216         on_error == BLOCKDEV_ON_ERROR_ENOSPC) &&
 217        (!bs->blk || !blk_iostatus_is_enabled(bs->blk))) {
 218        error_setg(errp, "Invalid parameter combination");
 219        return;
 220    }
 221
 222    assert(top != bs);
 223    if (top == base) {
 224        error_setg(errp, "Invalid files for merge: top and base are the same");
 225        return;
 226    }
 227
 228    overlay_bs = bdrv_find_overlay(bs, top);
 229
 230    if (overlay_bs == NULL) {
 231        error_setg(errp, "Could not find overlay image for %s:", top->filename);
 232        return;
 233    }
 234
 235    orig_base_flags    = bdrv_get_flags(base);
 236    orig_overlay_flags = bdrv_get_flags(overlay_bs);
 237
 238    /* convert base & overlay_bs to r/w, if necessary */
 239    if (!(orig_overlay_flags & BDRV_O_RDWR)) {
 240        reopen_queue = bdrv_reopen_queue(reopen_queue, overlay_bs, NULL,
 241                                         orig_overlay_flags | BDRV_O_RDWR);
 242    }
 243    if (!(orig_base_flags & BDRV_O_RDWR)) {
 244        reopen_queue = bdrv_reopen_queue(reopen_queue, base, NULL,
 245                                         orig_base_flags | BDRV_O_RDWR);
 246    }
 247    if (reopen_queue) {
 248        bdrv_reopen_multiple(reopen_queue, &local_err);
 249        if (local_err != NULL) {
 250            error_propagate(errp, local_err);
 251            return;
 252        }
 253    }
 254
 255
 256    s = block_job_create(&commit_job_driver, bs, speed, cb, opaque, errp);
 257    if (!s) {
 258        return;
 259    }
 260
 261    s->base   = base;
 262    s->top    = top;
 263    s->active = bs;
 264
 265    s->base_flags          = orig_base_flags;
 266    s->orig_overlay_flags  = orig_overlay_flags;
 267
 268    s->backing_file_str = g_strdup(backing_file_str);
 269
 270    s->on_error = on_error;
 271    s->common.co = qemu_coroutine_create(commit_run);
 272
 273    trace_commit_start(bs, base, top, s, s->common.co, opaque);
 274    qemu_coroutine_enter(s->common.co, s);
 275}
 276