X-Git-Url: https://git.proxmox.com/?a=blobdiff_plain;f=block-migration.c;h=43ab2028c0bb544f422c11677e729897e6500256;hb=refs%2Fheads%2Fbackup2;hp=fd2ffff0d5178d41fdfc3ef95460bb2373de597a;hpb=4e1957acc854b2f3f3068c75cef2a429f9b97011;p=qemu.git diff --git a/block-migration.c b/block-migration.c index fd2ffff0d..43ab2028c 100644 --- a/block-migration.c +++ b/block-migration.c @@ -14,16 +14,17 @@ */ #include "qemu-common.h" -#include "block_int.h" +#include "block/block_int.h" #include "hw/hw.h" -#include "qemu-queue.h" -#include "qemu-timer.h" -#include "block-migration.h" -#include "migration.h" -#include "blockdev.h" +#include "qemu/queue.h" +#include "qemu/timer.h" +#include "migration/block.h" +#include "migration/migration.h" +#include "sysemu/blockdev.h" #include -#define BLOCK_SIZE (BDRV_SECTORS_PER_DIRTY_CHUNK << BDRV_SECTOR_BITS) +#define BLOCK_SIZE (1 << 20) +#define BDRV_SECTORS_PER_DIRTY_CHUNK (BLOCK_SIZE >> BDRV_SECTOR_BITS) #define BLK_MIG_FLAG_DEVICE_BLOCK 0x01 #define BLK_MIG_FLAG_EOS 0x02 @@ -77,9 +78,7 @@ typedef struct BlkMigState { int64_t total_sector_sum; int prev_progress; int bulk_completed; - long double total_time; long double prev_time_offset; - int reads; } BlkMigState; static BlkMigState block_mig_state; @@ -132,12 +131,6 @@ uint64_t blk_mig_bytes_total(void) return sum << BDRV_SECTOR_BITS; } -static inline long double compute_read_bwidth(void) -{ - assert(block_mig_state.total_time != 0); - return (block_mig_state.reads / block_mig_state.total_time) * BLOCK_SIZE; -} - static int bmds_aio_inflight(BlkMigDevState *bmds, int64_t sector) { int64_t chunk = sector / (int64_t)BDRV_SECTORS_PER_DIRTY_CHUNK; @@ -191,8 +184,6 @@ static void blk_mig_read_cb(void *opaque, int ret) blk->ret = ret; - block_mig_state.reads++; - block_mig_state.total_time += (curr_time - block_mig_state.prev_time_offset); block_mig_state.prev_time_offset = curr_time; QSIMPLEQ_INSERT_TAIL(&block_mig_state.blk_list, blk, entry); @@ -264,7 +255,7 @@ static void set_dirty_tracking(int enable) BlkMigDevState *bmds; QSIMPLEQ_FOREACH(bmds, &block_mig_state.bmds_list, entry) { - bdrv_set_dirty_tracking(bmds->bs, enable); + bdrv_set_dirty_tracking(bmds->bs, enable ? BLOCK_SIZE : 0); } } @@ -310,8 +301,6 @@ static void init_blk_migration(QEMUFile *f) block_mig_state.total_sector_sum = 0; block_mig_state.prev_progress = -1; block_mig_state.bulk_completed = 0; - block_mig_state.total_time = 0; - block_mig_state.reads = 0; bdrv_iterate(init_blk_migration_it, NULL); } @@ -423,20 +412,23 @@ static int mig_save_device_dirty(QEMUFile *f, BlkMigDevState *bmds, error: DPRINTF("Error reading sector %" PRId64 "\n", sector); - qemu_file_set_error(f, ret); g_free(blk->buf); g_free(blk); - return 0; + return ret; } +/* return value: + * 0: too much data for max_downtime + * 1: few enough data for max_downtime +*/ static int blk_mig_save_dirty_block(QEMUFile *f, int is_async) { BlkMigDevState *bmds; - int ret = 0; + int ret = 1; QSIMPLEQ_FOREACH(bmds, &block_mig_state.bmds_list, entry) { - if (mig_save_device_dirty(f, bmds, is_async) == 0) { - ret = 1; + ret = mig_save_device_dirty(f, bmds, is_async); + if (ret <= 0) { break; } } @@ -444,9 +436,10 @@ static int blk_mig_save_dirty_block(QEMUFile *f, int is_async) return ret; } -static void flush_blks(QEMUFile* f) +static int flush_blks(QEMUFile *f) { BlkMigBlock *blk; + int ret = 0; DPRINTF("%s Enter submitted %d read_done %d transferred %d\n", __FUNCTION__, block_mig_state.submitted, block_mig_state.read_done, @@ -457,7 +450,7 @@ static void flush_blks(QEMUFile* f) break; } if (blk->ret < 0) { - qemu_file_set_error(f, blk->ret); + ret = blk->ret; break; } blk_send(f, blk); @@ -474,6 +467,7 @@ static void flush_blks(QEMUFile* f) DPRINTF("%s Exit submitted %d read_done %d transferred %d\n", __FUNCTION__, block_mig_state.submitted, block_mig_state.read_done, block_mig_state.transferred); + return ret; } static int64_t get_remaining_dirty(void) @@ -485,33 +479,7 @@ static int64_t get_remaining_dirty(void) dirty += bdrv_get_dirty_count(bmds->bs); } - return dirty * BLOCK_SIZE; -} - -static int is_stage2_completed(void) -{ - int64_t remaining_dirty; - long double bwidth; - - if (block_mig_state.bulk_completed == 1) { - - remaining_dirty = get_remaining_dirty(); - if (remaining_dirty == 0) { - return 1; - } - - bwidth = compute_read_bwidth(); - - if ((remaining_dirty / bwidth) <= - migrate_max_downtime()) { - /* finish stage2 because we think that we can finish remaining work - below max_downtime */ - - return 1; - } - } - - return 0; + return dirty << BDRV_SECTOR_BITS; } static void blk_mig_cleanup(void) @@ -519,6 +487,8 @@ static void blk_mig_cleanup(void) BlkMigDevState *bmds; BlkMigBlock *blk; + bdrv_drain_all(); + set_dirty_tracking(0); while ((bmds = QSIMPLEQ_FIRST(&block_mig_state.bmds_list)) != NULL) { @@ -536,34 +506,45 @@ static void blk_mig_cleanup(void) } } -static int block_save_live(QEMUFile *f, int stage, void *opaque) +static void block_migration_cancel(void *opaque) +{ + blk_mig_cleanup(); +} + +static int block_save_setup(QEMUFile *f, void *opaque) { int ret; - DPRINTF("Enter save live stage %d submitted %d transferred %d\n", - stage, block_mig_state.submitted, block_mig_state.transferred); + DPRINTF("Enter save live setup submitted %d transferred %d\n", + block_mig_state.submitted, block_mig_state.transferred); + + init_blk_migration(f); + + /* start track dirty blocks */ + set_dirty_tracking(1); - if (stage < 0) { + ret = flush_blks(f); + if (ret) { blk_mig_cleanup(); - return 0; + return ret; } - if (block_mig_state.blk_enable != 1) { - /* no need to migrate storage */ - qemu_put_be64(f, BLK_MIG_FLAG_EOS); - return 1; - } + blk_mig_reset_dirty_cursor(); - if (stage == 1) { - init_blk_migration(f); + qemu_put_be64(f, BLK_MIG_FLAG_EOS); - /* start track dirty blocks */ - set_dirty_tracking(1); - } + return 0; +} - flush_blks(f); +static int block_save_iterate(QEMUFile *f, void *opaque) +{ + int ret; + int64_t last_ftell = qemu_ftell(f); + + DPRINTF("Enter save live iterate submitted %d transferred %d\n", + block_mig_state.submitted, block_mig_state.transferred); - ret = qemu_file_get_error(f); + ret = flush_blks(f); if (ret) { blk_mig_cleanup(); return ret; @@ -571,56 +552,91 @@ static int block_save_live(QEMUFile *f, int stage, void *opaque) blk_mig_reset_dirty_cursor(); - if (stage == 2) { - /* control the rate of transfer */ - while ((block_mig_state.submitted + - block_mig_state.read_done) * BLOCK_SIZE < - qemu_file_get_rate_limit(f)) { - if (block_mig_state.bulk_completed == 0) { - /* first finish the bulk phase */ - if (blk_mig_save_bulked_block(f) == 0) { - /* finished saving bulk on all devices */ - block_mig_state.bulk_completed = 1; - } - } else { - if (blk_mig_save_dirty_block(f, 1) == 0) { - /* no more dirty blocks */ - break; - } + /* control the rate of transfer */ + while ((block_mig_state.submitted + + block_mig_state.read_done) * BLOCK_SIZE < + qemu_file_get_rate_limit(f)) { + if (block_mig_state.bulk_completed == 0) { + /* first finish the bulk phase */ + if (blk_mig_save_bulked_block(f) == 0) { + /* finished saving bulk on all devices */ + block_mig_state.bulk_completed = 1; + } + } else { + ret = blk_mig_save_dirty_block(f, 1); + if (ret != 0) { + /* no more dirty blocks */ + break; } } + } + if (ret < 0) { + blk_mig_cleanup(); + return ret; + } - flush_blks(f); - - ret = qemu_file_get_error(f); - if (ret) { - blk_mig_cleanup(); - return ret; - } + ret = flush_blks(f); + if (ret) { + blk_mig_cleanup(); + return ret; } - if (stage == 3) { - /* we know for sure that save bulk is completed and - all async read completed */ - assert(block_mig_state.submitted == 0); + qemu_put_be64(f, BLK_MIG_FLAG_EOS); + + return qemu_ftell(f) - last_ftell; +} + +static int block_save_complete(QEMUFile *f, void *opaque) +{ + int ret; + + DPRINTF("Enter save live complete submitted %d transferred %d\n", + block_mig_state.submitted, block_mig_state.transferred); - while (blk_mig_save_dirty_block(f, 0) != 0); + ret = flush_blks(f); + if (ret) { blk_mig_cleanup(); + return ret; + } - /* report completion */ - qemu_put_be64(f, (100 << BDRV_SECTOR_BITS) | BLK_MIG_FLAG_PROGRESS); + blk_mig_reset_dirty_cursor(); - ret = qemu_file_get_error(f); - if (ret) { - return ret; - } + /* we know for sure that save bulk is completed and + all async read completed */ + assert(block_mig_state.submitted == 0); + + do { + ret = blk_mig_save_dirty_block(f, 0); + } while (ret == 0); - DPRINTF("Block migration completed\n"); + blk_mig_cleanup(); + if (ret < 0) { + return ret; } + /* report completion */ + qemu_put_be64(f, (100 << BDRV_SECTOR_BITS) | BLK_MIG_FLAG_PROGRESS); + + DPRINTF("Block migration completed\n"); qemu_put_be64(f, BLK_MIG_FLAG_EOS); - return ((stage == 2) && is_stage2_completed()); + return 0; +} + +static uint64_t block_save_pending(QEMUFile *f, void *opaque, uint64_t max_size) +{ + /* Estimate pending number of bytes to send */ + uint64_t pending = get_remaining_dirty() + + block_mig_state.submitted * BLOCK_SIZE + + block_mig_state.read_done * BLOCK_SIZE; + + /* Report at least one block pending during bulk phase */ + if (pending == 0 && !block_mig_state.bulk_completed) { + pending = BLOCK_SIZE; + } + + DPRINTF("Enter save live pending %" PRIu64 "\n", pending); + return pending; } static int block_load(QEMUFile *f, void *opaque, int version_id) @@ -688,7 +704,7 @@ static int block_load(QEMUFile *f, void *opaque, int version_id) (addr == 100) ? '\n' : '\r'); fflush(stdout); } else if (!(flags & BLK_MIG_FLAG_EOS)) { - fprintf(stderr, "Unknown flags\n"); + fprintf(stderr, "Unknown block migration flags: %#x\n", flags); return -EINVAL; } ret = qemu_file_get_error(f); @@ -700,20 +716,36 @@ static int block_load(QEMUFile *f, void *opaque, int version_id) return 0; } -static void block_set_params(int blk_enable, int shared_base, void *opaque) +static void block_set_params(const MigrationParams *params, void *opaque) { - block_mig_state.blk_enable = blk_enable; - block_mig_state.shared_base = shared_base; + block_mig_state.blk_enable = params->blk; + block_mig_state.shared_base = params->shared; /* shared base means that blk_enable = 1 */ - block_mig_state.blk_enable |= shared_base; + block_mig_state.blk_enable |= params->shared; } +static bool block_is_active(void *opaque) +{ + return block_mig_state.blk_enable == 1; +} + +SaveVMHandlers savevm_block_handlers = { + .set_params = block_set_params, + .save_live_setup = block_save_setup, + .save_live_iterate = block_save_iterate, + .save_live_complete = block_save_complete, + .save_live_pending = block_save_pending, + .load_state = block_load, + .cancel = block_migration_cancel, + .is_active = block_is_active, +}; + void blk_mig_init(void) { QSIMPLEQ_INIT(&block_mig_state.bmds_list); QSIMPLEQ_INIT(&block_mig_state.blk_list); - register_savevm_live(NULL, "block", 0, 1, block_set_params, - block_save_live, NULL, block_load, &block_mig_state); + register_savevm_live(NULL, "block", 0, 1, &savevm_block_handlers, + &block_mig_state); }