diff --git a/arch_init.c b/arch_init.c index a6c69c75a..a411fdf26 100644 --- a/arch_init.c +++ b/arch_init.c @@ -256,6 +256,7 @@ int ram_save_live(Monitor *mon, QEMUFile *f, int stage, void *opaque) uint64_t bytes_transferred_last; double bwidth = 0; uint64_t expected_time = 0; + int ret; if (stage < 0) { cpu_physical_memory_set_dirty_tracking(0); @@ -263,8 +264,8 @@ int ram_save_live(Monitor *mon, QEMUFile *f, int stage, void *opaque) } if (cpu_physical_sync_dirty_bitmap(0, TARGET_PHYS_ADDR_MAX) != 0) { - qemu_file_set_error(f); - return 0; + qemu_file_set_error(f, -EINVAL); + return -EINVAL; } if (stage == 1) { @@ -300,7 +301,7 @@ int ram_save_live(Monitor *mon, QEMUFile *f, int stage, void *opaque) bytes_transferred_last = bytes_transferred; bwidth = qemu_get_clock_ns(rt_clock); - while (!qemu_file_rate_limit(f)) { + while ((ret = qemu_file_rate_limit(f)) == 0) { int bytes_sent; bytes_sent = ram_save_block(f); @@ -310,6 +311,10 @@ int ram_save_live(Monitor *mon, QEMUFile *f, int stage, void *opaque) } } + if (ret < 0) { + return ret; + } + bwidth = qemu_get_clock_ns(rt_clock) - bwidth; bwidth = (bytes_transferred - bytes_transferred_last) / bwidth; @@ -371,6 +376,7 @@ int ram_load(QEMUFile *f, void *opaque, int version_id) { ram_addr_t addr; int flags; + int error; if (version_id < 3 || version_id > 4) { return -EINVAL; @@ -451,8 +457,9 @@ int ram_load(QEMUFile *f, void *opaque, int version_id) qemu_get_buffer(f, host, TARGET_PAGE_SIZE); } - if (qemu_file_has_error(f)) { - return -EIO; + error = qemu_file_get_error(f); + if (error) { + return error; } } while (!(flags & RAM_SAVE_FLAG_EOS)); diff --git a/block-migration.c b/block-migration.c index e2775ee50..0bff07538 100644 --- a/block-migration.c +++ b/block-migration.c @@ -263,7 +263,7 @@ static int mig_save_device_bulk(Monitor *mon, QEMUFile *f, error: monitor_printf(mon, "Error reading sector %" PRId64 "\n", cur_sector); - qemu_file_set_error(f); + qemu_file_set_error(f, -EIO); g_free(blk->buf); g_free(blk); return 0; @@ -383,6 +383,7 @@ static int mig_save_device_dirty(Monitor *mon, QEMUFile *f, int64_t total_sectors = bmds->total_sectors; int64_t sector; int nr_sectors; + int ret = -EIO; for (sector = bmds->cur_dirty; sector < bmds->total_sectors;) { if (bmds_aio_inflight(bmds, sector)) { @@ -418,8 +419,8 @@ static int mig_save_device_dirty(Monitor *mon, QEMUFile *f, block_mig_state.submitted++; bmds_set_aio_inflight(bmds, sector, nr_sectors, 1); } else { - if (bdrv_read(bmds->bs, sector, blk->buf, - nr_sectors) < 0) { + ret = bdrv_read(bmds->bs, sector, blk->buf, nr_sectors); + if (ret < 0) { goto error; } blk_send(f, blk); @@ -439,7 +440,7 @@ static int mig_save_device_dirty(Monitor *mon, QEMUFile *f, error: monitor_printf(mon, "Error reading sector %" PRId64 "\n", sector); - qemu_file_set_error(f); + qemu_file_set_error(f, ret); g_free(blk->buf); g_free(blk); return 0; @@ -473,7 +474,7 @@ static void flush_blks(QEMUFile* f) break; } if (blk->ret < 0) { - qemu_file_set_error(f); + qemu_file_set_error(f, blk->ret); break; } blk_send(f, blk); @@ -556,6 +557,8 @@ static void blk_mig_cleanup(Monitor *mon) static int block_save_live(Monitor *mon, QEMUFile *f, int stage, void *opaque) { + int ret; + DPRINTF("Enter save live stage %d submitted %d transferred %d\n", stage, block_mig_state.submitted, block_mig_state.transferred); @@ -579,9 +582,10 @@ static int block_save_live(Monitor *mon, QEMUFile *f, int stage, void *opaque) flush_blks(f); - if (qemu_file_has_error(f)) { + ret = qemu_file_get_error(f); + if (ret) { blk_mig_cleanup(mon); - return 0; + return ret; } blk_mig_reset_dirty_cursor(); @@ -607,9 +611,10 @@ static int block_save_live(Monitor *mon, QEMUFile *f, int stage, void *opaque) flush_blks(f); - if (qemu_file_has_error(f)) { + ret = qemu_file_get_error(f); + if (ret) { blk_mig_cleanup(mon); - return 0; + return ret; } } @@ -624,8 +629,9 @@ static int block_save_live(Monitor *mon, QEMUFile *f, int stage, void *opaque) /* report completion */ qemu_put_be64(f, (100 << BDRV_SECTOR_BITS) | BLK_MIG_FLAG_PROGRESS); - if (qemu_file_has_error(f)) { - return 0; + ret = qemu_file_get_error(f); + if (ret) { + return ret; } monitor_printf(mon, "Block migration completed\n"); @@ -646,6 +652,7 @@ static int block_load(QEMUFile *f, void *opaque, int version_id) uint8_t *buf; int64_t total_sectors = 0; int nr_sectors; + int ret; do { addr = qemu_get_be64(f); @@ -654,7 +661,6 @@ static int block_load(QEMUFile *f, void *opaque, int version_id) addr >>= BDRV_SECTOR_BITS; if (flags & BLK_MIG_FLAG_DEVICE_BLOCK) { - int ret; /* get device name */ len = qemu_get_byte(f); qemu_get_buffer(f, (uint8_t *)device_name, len); @@ -704,8 +710,9 @@ static int block_load(QEMUFile *f, void *opaque, int version_id) fprintf(stderr, "Unknown flags\n"); return -EINVAL; } - if (qemu_file_has_error(f)) { - return -EIO; + ret = qemu_file_get_error(f); + if (ret != 0) { + return ret; } } while (!(flags & BLK_MIG_FLAG_EOS)); diff --git a/buffered_file.c b/buffered_file.c index 486af575d..fed9a227b 100644 --- a/buffered_file.c +++ b/buffered_file.c @@ -27,7 +27,6 @@ typedef struct QEMUFileBuffered BufferedCloseFunc *close; void *opaque; QEMUFile *file; - int has_error; int freeze_output; size_t bytes_xfer; size_t xfer_limit; @@ -72,9 +71,11 @@ static void buffered_append(QEMUFileBuffered *s, static void buffered_flush(QEMUFileBuffered *s) { size_t offset = 0; + int error; - if (s->has_error) { - DPRINTF("flush when error, bailing\n"); + error = qemu_file_get_error(s->file); + if (error != 0) { + DPRINTF("flush when error, bailing: %s\n", strerror(-error)); return; } @@ -93,7 +94,7 @@ static void buffered_flush(QEMUFileBuffered *s) if (ret <= 0) { DPRINTF("error flushing data, %zd\n", ret); - s->has_error = 1; + qemu_file_set_error(s->file, ret); break; } else { DPRINTF("flushed %zd byte(s)\n", ret); @@ -109,14 +110,15 @@ static void buffered_flush(QEMUFileBuffered *s) static int buffered_put_buffer(void *opaque, const uint8_t *buf, int64_t pos, int size) { QEMUFileBuffered *s = opaque; - int offset = 0; + int offset = 0, error; ssize_t ret; DPRINTF("putting %d bytes at %" PRId64 "\n", size, pos); - if (s->has_error) { - DPRINTF("flush when error, bailing\n"); - return -EINVAL; + error = qemu_file_get_error(s->file); + if (error) { + DPRINTF("flush when error, bailing: %s\n", strerror(-error)); + return error; } DPRINTF("unfreezing output\n"); @@ -139,7 +141,7 @@ static int buffered_put_buffer(void *opaque, const uint8_t *buf, int64_t pos, in if (ret <= 0) { DPRINTF("error putting\n"); - s->has_error = 1; + qemu_file_set_error(s->file, ret); offset = -EINVAL; break; } @@ -173,10 +175,10 @@ static int buffered_close(void *opaque) DPRINTF("closing\n"); - while (!s->has_error && s->buffer_size) { + while (!qemu_file_get_error(s->file) && s->buffer_size) { buffered_flush(s); if (s->freeze_output) - s->wait_for_unfreeze(s); + s->wait_for_unfreeze(s->opaque); } ret = s->close(s->opaque); @@ -189,13 +191,21 @@ static int buffered_close(void *opaque) return ret; } +/* + * The meaning of the return values is: + * 0: We can continue sending + * 1: Time to stop + * negative: There has been an error + */ static int buffered_rate_limit(void *opaque) { QEMUFileBuffered *s = opaque; + int ret; - if (s->has_error) - return 0; - + ret = qemu_file_get_error(s->file); + if (ret) { + return ret; + } if (s->freeze_output) return 1; @@ -208,9 +218,9 @@ static int buffered_rate_limit(void *opaque) static int64_t buffered_set_rate_limit(void *opaque, int64_t new_rate) { QEMUFileBuffered *s = opaque; - if (s->has_error) + if (qemu_file_get_error(s->file)) { goto out; - + } if (new_rate > SIZE_MAX) { new_rate = SIZE_MAX; } @@ -232,7 +242,7 @@ static void buffered_rate_tick(void *opaque) { QEMUFileBuffered *s = opaque; - if (s->has_error) { + if (qemu_file_get_error(s->file)) { buffered_close(s); return; } diff --git a/hw/ds1225y.c b/hw/ds1225y.c index 9875c445f..6852a61d0 100644 --- a/hw/ds1225y.c +++ b/hw/ds1225y.c @@ -29,7 +29,7 @@ typedef struct { DeviceState qdev; uint32_t chip_size; char *filename; - QEMUFile *file; + FILE *file; uint8_t *contents; } NvRamState; @@ -70,9 +70,9 @@ static void nvram_writeb (void *opaque, target_phys_addr_t addr, uint32_t val) s->contents[addr] = val; if (s->file) { - qemu_fseek(s->file, addr, SEEK_SET); - qemu_put_byte(s->file, (int)val); - qemu_fflush(s->file); + fseek(s->file, addr, SEEK_SET); + fputc(val, s->file); + fflush(s->file); } } @@ -108,15 +108,17 @@ static int nvram_post_load(void *opaque, int version_id) /* Close file, as filename may has changed in load/store process */ if (s->file) { - qemu_fclose(s->file); + fclose(s->file); } /* Write back nvram contents */ - s->file = qemu_fopen(s->filename, "wb"); + s->file = fopen(s->filename, "wb"); if (s->file) { /* Write back contents, as 'wb' mode cleaned the file */ - qemu_put_buffer(s->file, s->contents, s->chip_size); - qemu_fflush(s->file); + if (fwrite(s->contents, s->chip_size, 1, s->file) != 1) { + printf("nvram_post_load: short write\n"); + } + fflush(s->file); } return 0; @@ -143,7 +145,7 @@ typedef struct { static int nvram_sysbus_initfn(SysBusDevice *dev) { NvRamState *s = &FROM_SYSBUS(SysBusNvRamState, dev)->nvram; - QEMUFile *file; + FILE *file; int s_io; s->contents = g_malloc0(s->chip_size); @@ -153,11 +155,13 @@ static int nvram_sysbus_initfn(SysBusDevice *dev) sysbus_init_mmio(dev, s->chip_size, s_io); /* Read current file */ - file = qemu_fopen(s->filename, "rb"); + file = fopen(s->filename, "rb"); if (file) { /* Read nvram contents */ - qemu_get_buffer(file, s->contents, s->chip_size); - qemu_fclose(file); + if (fread(s->contents, s->chip_size, 1, file) != 1) { + printf("nvram_sysbus_initfn: short read\n"); + } + fclose(file); } nvram_post_load(s, 0); diff --git a/hw/hw.h b/hw/hw.h index a124da9c2..ed20f5a27 100644 --- a/hw/hw.h +++ b/hw/hw.h @@ -85,8 +85,8 @@ uint64_t qemu_get_be64(QEMUFile *f); int qemu_file_rate_limit(QEMUFile *f); int64_t qemu_file_set_rate_limit(QEMUFile *f, int64_t new_rate); int64_t qemu_file_get_rate_limit(QEMUFile *f); -int qemu_file_has_error(QEMUFile *f); -void qemu_file_set_error(QEMUFile *f); +int qemu_file_get_error(QEMUFile *f); +void qemu_file_set_error(QEMUFile *f, int error); /* Try to send any outstanding data. This function is useful when output is * halted due to rate limiting or EAGAIN errors occur as it can be used to diff --git a/migration-exec.c b/migration-exec.c index 2cfb6f232..b7b1055e8 100644 --- a/migration-exec.c +++ b/migration-exec.c @@ -32,17 +32,17 @@ do { } while (0) #endif -static int file_errno(FdMigrationState *s) +static int file_errno(MigrationState *s) { return errno; } -static int file_write(FdMigrationState *s, const void * buf, size_t size) +static int file_write(MigrationState *s, const void * buf, size_t size) { return write(s->fd, buf, size); } -static int exec_close(FdMigrationState *s) +static int exec_close(MigrationState *s) { int ret = 0; DPRINTF("exec_close\n"); @@ -61,22 +61,14 @@ static int exec_close(FdMigrationState *s) return ret; } -MigrationState *exec_start_outgoing_migration(Monitor *mon, - const char *command, - int64_t bandwidth_limit, - int detach, - int blk, - int inc) +int exec_start_outgoing_migration(MigrationState *s, const char *command) { - FdMigrationState *s; FILE *f; - s = g_malloc0(sizeof(*s)); - f = popen(command, "w"); if (f == NULL) { DPRINTF("Unable to popen exec target\n"); - goto err_after_alloc; + goto err_after_popen; } s->fd = fileno(f); @@ -92,29 +84,14 @@ MigrationState *exec_start_outgoing_migration(Monitor *mon, s->close = exec_close; s->get_error = file_errno; s->write = file_write; - s->mig_state.cancel = migrate_fd_cancel; - s->mig_state.get_status = migrate_fd_get_status; - s->mig_state.release = migrate_fd_release; - - s->mig_state.blk = blk; - s->mig_state.shared = inc; - - s->state = MIG_STATE_ACTIVE; - s->mon = NULL; - s->bandwidth_limit = bandwidth_limit; - - if (!detach) { - migrate_fd_monitor_suspend(s, mon); - } migrate_fd_connect(s); - return &s->mig_state; + return 0; err_after_open: pclose(f); -err_after_alloc: - g_free(s); - return NULL; +err_after_popen: + return -1; } static void exec_accept_incoming_migration(void *opaque) diff --git a/migration-fd.c b/migration-fd.c index aee690aae..d0aec89e8 100644 --- a/migration-fd.c +++ b/migration-fd.c @@ -30,17 +30,17 @@ do { } while (0) #endif -static int fd_errno(FdMigrationState *s) +static int fd_errno(MigrationState *s) { return errno; } -static int fd_write(FdMigrationState *s, const void * buf, size_t size) +static int fd_write(MigrationState *s, const void * buf, size_t size) { return write(s->fd, buf, size); } -static int fd_close(FdMigrationState *s) +static int fd_close(MigrationState *s) { DPRINTF("fd_close\n"); if (s->fd != -1) { @@ -50,21 +50,12 @@ static int fd_close(FdMigrationState *s) return 0; } -MigrationState *fd_start_outgoing_migration(Monitor *mon, - const char *fdname, - int64_t bandwidth_limit, - int detach, - int blk, - int inc) +int fd_start_outgoing_migration(MigrationState *s, const char *fdname) { - FdMigrationState *s; - - s = g_malloc0(sizeof(*s)); - - s->fd = monitor_get_fd(mon, fdname); + s->fd = monitor_get_fd(s->mon, fdname); if (s->fd == -1) { DPRINTF("fd_migration: invalid file descriptor identifier\n"); - goto err_after_alloc; + goto err_after_get_fd; } if (fcntl(s->fd, F_SETFL, O_NONBLOCK) == -1) { @@ -75,29 +66,14 @@ MigrationState *fd_start_outgoing_migration(Monitor *mon, s->get_error = fd_errno; s->write = fd_write; s->close = fd_close; - s->mig_state.cancel = migrate_fd_cancel; - s->mig_state.get_status = migrate_fd_get_status; - s->mig_state.release = migrate_fd_release; - - s->mig_state.blk = blk; - s->mig_state.shared = inc; - - s->state = MIG_STATE_ACTIVE; - s->mon = NULL; - s->bandwidth_limit = bandwidth_limit; - - if (!detach) { - migrate_fd_monitor_suspend(s, mon); - } migrate_fd_connect(s); - return &s->mig_state; + return 0; err_after_open: close(s->fd); -err_after_alloc: - g_free(s); - return NULL; +err_after_get_fd: + return -1; } static void fd_accept_incoming_migration(void *opaque) diff --git a/migration-tcp.c b/migration-tcp.c index c431e03a4..5aa742c34 100644 --- a/migration-tcp.c +++ b/migration-tcp.c @@ -28,17 +28,17 @@ do { } while (0) #endif -static int socket_errno(FdMigrationState *s) +static int socket_errno(MigrationState *s) { return socket_error(); } -static int socket_write(FdMigrationState *s, const void * buf, size_t size) +static int socket_write(MigrationState *s, const void * buf, size_t size) { return send(s->fd, buf, size, 0); } -static int tcp_close(FdMigrationState *s) +static int tcp_close(MigrationState *s) { DPRINTF("tcp_close\n"); if (s->fd != -1) { @@ -48,17 +48,16 @@ static int tcp_close(FdMigrationState *s) return 0; } - static void tcp_wait_for_connect(void *opaque) { - FdMigrationState *s = opaque; + MigrationState *s = opaque; int val, ret; socklen_t valsize = sizeof(val); DPRINTF("connect completed\n"); do { ret = getsockopt(s->fd, SOL_SOCKET, SO_ERROR, (void *) &val, &valsize); - } while (ret == -1 && (s->get_error(s)) == EINTR); + } while (ret == -1 && (socket_error()) == EINTR); if (ret < 0) { migrate_fd_error(s); @@ -75,63 +74,46 @@ static void tcp_wait_for_connect(void *opaque) } } -MigrationState *tcp_start_outgoing_migration(Monitor *mon, - const char *host_port, - int64_t bandwidth_limit, - int detach, - int blk, - int inc) +int tcp_start_outgoing_migration(MigrationState *s, const char *host_port) { struct sockaddr_in addr; - FdMigrationState *s; int ret; - if (parse_host_port(&addr, host_port) < 0) - return NULL; - - s = g_malloc0(sizeof(*s)); + ret = parse_host_port(&addr, host_port); + if (ret < 0) { + return ret; + } s->get_error = socket_errno; s->write = socket_write; s->close = tcp_close; - s->mig_state.cancel = migrate_fd_cancel; - s->mig_state.get_status = migrate_fd_get_status; - s->mig_state.release = migrate_fd_release; - s->mig_state.blk = blk; - s->mig_state.shared = inc; - - s->state = MIG_STATE_ACTIVE; - s->mon = NULL; - s->bandwidth_limit = bandwidth_limit; s->fd = qemu_socket(PF_INET, SOCK_STREAM, 0); if (s->fd == -1) { - g_free(s); - return NULL; + DPRINTF("Unable to open socket"); + return -socket_error(); } socket_set_nonblock(s->fd); - if (!detach) { - migrate_fd_monitor_suspend(s, mon); - } - do { ret = connect(s->fd, (struct sockaddr *)&addr, sizeof(addr)); - if (ret == -1) - ret = -(s->get_error(s)); - - if (ret == -EINPROGRESS || ret == -EWOULDBLOCK) + if (ret == -1) { + ret = -socket_error(); + } + if (ret == -EINPROGRESS || ret == -EWOULDBLOCK) { qemu_set_fd_handler2(s->fd, NULL, NULL, tcp_wait_for_connect, s); + return 0; + } } while (ret == -EINTR); - if (ret < 0 && ret != -EINPROGRESS && ret != -EWOULDBLOCK) { + if (ret < 0) { DPRINTF("connect failed\n"); migrate_fd_error(s); - } else if (ret >= 0) - migrate_fd_connect(s); - - return &s->mig_state; + return ret; + } + migrate_fd_connect(s); + return 0; } static void tcp_accept_incoming_migration(void *opaque) @@ -174,23 +156,27 @@ int tcp_start_incoming_migration(const char *host_port) int val; int s; + DPRINTF("Attempting to start an incoming migration\n"); + if (parse_host_port(&addr, host_port) < 0) { fprintf(stderr, "invalid host/port combination: %s\n", host_port); return -EINVAL; } s = qemu_socket(PF_INET, SOCK_STREAM, 0); - if (s == -1) + if (s == -1) { return -socket_error(); + } val = 1; setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (const char *)&val, sizeof(val)); - if (bind(s, (struct sockaddr *)&addr, sizeof(addr)) == -1) + if (bind(s, (struct sockaddr *)&addr, sizeof(addr)) == -1) { goto err; - - if (listen(s, 1) == -1) + } + if (listen(s, 1) == -1) { goto err; + } qemu_set_fd_handler2(s, NULL, tcp_accept_incoming_migration, NULL, (void *)(intptr_t)s); diff --git a/migration-unix.c b/migration-unix.c index 6dc985d42..8596353d7 100644 --- a/migration-unix.c +++ b/migration-unix.c @@ -28,17 +28,17 @@ do { } while (0) #endif -static int unix_errno(FdMigrationState *s) +static int unix_errno(MigrationState *s) { return errno; } -static int unix_write(FdMigrationState *s, const void * buf, size_t size) +static int unix_write(MigrationState *s, const void * buf, size_t size) { return write(s->fd, buf, size); } -static int unix_close(FdMigrationState *s) +static int unix_close(MigrationState *s) { DPRINTF("unix_close\n"); if (s->fd != -1) { @@ -50,14 +50,14 @@ static int unix_close(FdMigrationState *s) static void unix_wait_for_connect(void *opaque) { - FdMigrationState *s = opaque; + MigrationState *s = opaque; int val, ret; socklen_t valsize = sizeof(val); DPRINTF("connect completed\n"); do { ret = getsockopt(s->fd, SOL_SOCKET, SO_ERROR, (void *) &val, &valsize); - } while (ret == -1 && (s->get_error(s)) == EINTR); + } while (ret == -1 && errno == EINTR); if (ret < 0) { migrate_fd_error(s); @@ -74,72 +74,43 @@ static void unix_wait_for_connect(void *opaque) } } -MigrationState *unix_start_outgoing_migration(Monitor *mon, - const char *path, - int64_t bandwidth_limit, - int detach, - int blk, - int inc) +int unix_start_outgoing_migration(MigrationState *s, const char *path) { - FdMigrationState *s; struct sockaddr_un addr; int ret; addr.sun_family = AF_UNIX; snprintf(addr.sun_path, sizeof(addr.sun_path), "%s", path); - - s = g_malloc0(sizeof(*s)); - s->get_error = unix_errno; s->write = unix_write; s->close = unix_close; - s->mig_state.cancel = migrate_fd_cancel; - s->mig_state.get_status = migrate_fd_get_status; - s->mig_state.release = migrate_fd_release; - s->mig_state.blk = blk; - s->mig_state.shared = inc; - - s->state = MIG_STATE_ACTIVE; - s->mon = NULL; - s->bandwidth_limit = bandwidth_limit; s->fd = qemu_socket(PF_UNIX, SOCK_STREAM, 0); - if (s->fd < 0) { + if (s->fd == -1) { DPRINTF("Unable to open socket"); - goto err_after_alloc; + return -errno; } socket_set_nonblock(s->fd); do { ret = connect(s->fd, (struct sockaddr *)&addr, sizeof(addr)); - if (ret == -1) - ret = -(s->get_error(s)); - - if (ret == -EINPROGRESS || ret == -EWOULDBLOCK) + if (ret == -1) { + ret = -errno; + } + if (ret == -EINPROGRESS || ret == -EWOULDBLOCK) { qemu_set_fd_handler2(s->fd, NULL, NULL, unix_wait_for_connect, s); + return 0; + } } while (ret == -EINTR); - if (ret < 0 && ret != -EINPROGRESS && ret != -EWOULDBLOCK) { + if (ret < 0) { DPRINTF("connect failed\n"); - goto err_after_open; + migrate_fd_error(s); + return ret; } - - if (!detach) { - migrate_fd_monitor_suspend(s, mon); - } - - if (ret >= 0) - migrate_fd_connect(s); - - return &s->mig_state; - -err_after_open: - close(s->fd); - -err_after_alloc: - g_free(s); - return NULL; + migrate_fd_connect(s); + return 0; } static void unix_accept_incoming_migration(void *opaque) @@ -152,13 +123,13 @@ static void unix_accept_incoming_migration(void *opaque) do { c = qemu_accept(s, (struct sockaddr *)&addr, &addrlen); - } while (c == -1 && socket_error() == EINTR); + } while (c == -1 && errno == EINTR); DPRINTF("accepted migration\n"); if (c == -1) { fprintf(stderr, "could not accept migration connection\n"); - return; + goto out2; } f = qemu_fopen_socket(c); @@ -170,45 +141,49 @@ static void unix_accept_incoming_migration(void *opaque) process_incoming_migration(f); qemu_fclose(f); out: + close(c); +out2: qemu_set_fd_handler2(s, NULL, NULL, NULL, NULL); close(s); - close(c); } int unix_start_incoming_migration(const char *path) { - struct sockaddr_un un; - int sock; + struct sockaddr_un addr; + int s; + int ret; DPRINTF("Attempting to start an incoming migration\n"); - sock = qemu_socket(PF_UNIX, SOCK_STREAM, 0); - if (sock < 0) { + s = qemu_socket(PF_UNIX, SOCK_STREAM, 0); + if (s == -1) { fprintf(stderr, "Could not open unix socket: %s\n", strerror(errno)); - return -EINVAL; + return -errno; } - memset(&un, 0, sizeof(un)); - un.sun_family = AF_UNIX; - snprintf(un.sun_path, sizeof(un.sun_path), "%s", path); + memset(&addr, 0, sizeof(addr)); + addr.sun_family = AF_UNIX; + snprintf(addr.sun_path, sizeof(addr.sun_path), "%s", path); - unlink(un.sun_path); - if (bind(sock, (struct sockaddr*) &un, sizeof(un)) < 0) { - fprintf(stderr, "bind(unix:%s): %s\n", un.sun_path, strerror(errno)); + unlink(addr.sun_path); + if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) { + ret = -errno; + fprintf(stderr, "bind(unix:%s): %s\n", addr.sun_path, strerror(errno)); goto err; } - if (listen(sock, 1) < 0) { - fprintf(stderr, "listen(unix:%s): %s\n", un.sun_path, strerror(errno)); + if (listen(s, 1) == -1) { + fprintf(stderr, "listen(unix:%s): %s\n", addr.sun_path, + strerror(errno)); + ret = -errno; goto err; } - qemu_set_fd_handler2(sock, NULL, unix_accept_incoming_migration, NULL, - (void *)(intptr_t)sock); + qemu_set_fd_handler2(s, NULL, unix_accept_incoming_migration, NULL, + (void *)(intptr_t)s); return 0; err: - close(sock); - - return -EINVAL; + close(s); + return ret; } diff --git a/migration.c b/migration.c index 62b74a6e2..bdca72e00 100644 --- a/migration.c +++ b/migration.c @@ -31,14 +31,33 @@ do { } while (0) #endif -/* Migration speed throttling */ -static int64_t max_throttle = (32 << 20); +enum { + MIG_STATE_ERROR, + MIG_STATE_SETUP, + MIG_STATE_CANCELLED, + MIG_STATE_ACTIVE, + MIG_STATE_COMPLETED, +}; -static MigrationState *current_migration; +#define MAX_THROTTLE (32 << 20) /* Migration speed throttling */ static NotifierList migration_state_notifiers = NOTIFIER_LIST_INITIALIZER(migration_state_notifiers); +/* When we add fault tolerance, we could have several + migrations at once. For now we don't need to add + dynamic creation of migration */ + +static MigrationState *migrate_get_current(void) +{ + static MigrationState current_migration = { + .state = MIG_STATE_SETUP, + .bandwidth_limit = MAX_THROTTLE, + }; + + return ¤t_migration; +} + int qemu_start_incoming_migration(const char *uri) { const char *p; @@ -77,87 +96,6 @@ void process_incoming_migration(QEMUFile *f) } } -int do_migrate(Monitor *mon, const QDict *qdict, QObject **ret_data) -{ - MigrationState *s = NULL; - const char *p; - int detach = qdict_get_try_bool(qdict, "detach", 0); - int blk = qdict_get_try_bool(qdict, "blk", 0); - int inc = qdict_get_try_bool(qdict, "inc", 0); - const char *uri = qdict_get_str(qdict, "uri"); - - if (current_migration && - current_migration->get_status(current_migration) == MIG_STATE_ACTIVE) { - monitor_printf(mon, "migration already in progress\n"); - return -1; - } - - if (qemu_savevm_state_blocked(mon)) { - return -1; - } - - if (strstart(uri, "tcp:", &p)) { - s = tcp_start_outgoing_migration(mon, p, max_throttle, detach, - blk, inc); -#if !defined(WIN32) - } else if (strstart(uri, "exec:", &p)) { - s = exec_start_outgoing_migration(mon, p, max_throttle, detach, - blk, inc); - } else if (strstart(uri, "unix:", &p)) { - s = unix_start_outgoing_migration(mon, p, max_throttle, detach, - blk, inc); - } else if (strstart(uri, "fd:", &p)) { - s = fd_start_outgoing_migration(mon, p, max_throttle, detach, - blk, inc); -#endif - } else { - monitor_printf(mon, "unknown migration protocol: %s\n", uri); - return -1; - } - - if (s == NULL) { - monitor_printf(mon, "migration failed\n"); - return -1; - } - - if (current_migration) { - current_migration->release(current_migration); - } - - current_migration = s; - notifier_list_notify(&migration_state_notifiers, NULL); - return 0; -} - -int do_migrate_cancel(Monitor *mon, const QDict *qdict, QObject **ret_data) -{ - MigrationState *s = current_migration; - - if (s) - s->cancel(s); - - return 0; -} - -int do_migrate_set_speed(Monitor *mon, const QDict *qdict, QObject **ret_data) -{ - int64_t d; - FdMigrationState *s; - - d = qdict_get_int(qdict, "value"); - if (d < 0) { - d = 0; - } - max_throttle = d; - - s = migrate_to_fms(current_migration); - if (s && s->file) { - qemu_file_set_rate_limit(s->file, max_throttle); - } - - return 0; -} - /* amount of nanoseconds we are willing to wait for migration to be down. * the choice of nanoseconds is because it is the maximum resolution that * get_clock() can achieve. It is an internal measure. All user-visible @@ -169,18 +107,6 @@ uint64_t migrate_max_downtime(void) return max_downtime; } -int do_migrate_set_downtime(Monitor *mon, const QDict *qdict, - QObject **ret_data) -{ - double d; - - d = qdict_get_double(qdict, "value") * 1e9; - d = MAX(0, MIN(UINT64_MAX, d)); - max_downtime = (uint64_t)d; - - return 0; -} - static void migrate_print_status(Monitor *mon, const char *name, const QDict *status_dict) { @@ -228,41 +154,42 @@ static void migrate_put_status(QDict *qdict, const char *name, void do_info_migrate(Monitor *mon, QObject **ret_data) { QDict *qdict; - MigrationState *s = current_migration; + MigrationState *s = migrate_get_current(); - if (s) { - switch (s->get_status(s)) { - case MIG_STATE_ACTIVE: - qdict = qdict_new(); - qdict_put(qdict, "status", qstring_from_str("active")); + switch (s->state) { + case MIG_STATE_SETUP: + /* no migration has happened ever */ + break; + case MIG_STATE_ACTIVE: + qdict = qdict_new(); + qdict_put(qdict, "status", qstring_from_str("active")); - migrate_put_status(qdict, "ram", ram_bytes_transferred(), - ram_bytes_remaining(), ram_bytes_total()); + migrate_put_status(qdict, "ram", ram_bytes_transferred(), + ram_bytes_remaining(), ram_bytes_total()); - if (blk_mig_active()) { - migrate_put_status(qdict, "disk", blk_mig_bytes_transferred(), - blk_mig_bytes_remaining(), - blk_mig_bytes_total()); - } - - *ret_data = QOBJECT(qdict); - break; - case MIG_STATE_COMPLETED: - *ret_data = qobject_from_jsonf("{ 'status': 'completed' }"); - break; - case MIG_STATE_ERROR: - *ret_data = qobject_from_jsonf("{ 'status': 'failed' }"); - break; - case MIG_STATE_CANCELLED: - *ret_data = qobject_from_jsonf("{ 'status': 'cancelled' }"); - break; + if (blk_mig_active()) { + migrate_put_status(qdict, "disk", blk_mig_bytes_transferred(), + blk_mig_bytes_remaining(), + blk_mig_bytes_total()); } + + *ret_data = QOBJECT(qdict); + break; + case MIG_STATE_COMPLETED: + *ret_data = qobject_from_jsonf("{ 'status': 'completed' }"); + break; + case MIG_STATE_ERROR: + *ret_data = qobject_from_jsonf("{ 'status': 'failed' }"); + break; + case MIG_STATE_CANCELLED: + *ret_data = qobject_from_jsonf("{ 'status': 'cancelled' }"); + break; } } /* shared migration helpers */ -void migrate_fd_monitor_suspend(FdMigrationState *s, Monitor *mon) +static void migrate_fd_monitor_suspend(MigrationState *s, Monitor *mon) { s->mon = mon; if (monitor_suspend(mon) == 0) { @@ -273,15 +200,7 @@ void migrate_fd_monitor_suspend(FdMigrationState *s, Monitor *mon) } } -void migrate_fd_error(FdMigrationState *s) -{ - DPRINTF("setting error state\n"); - s->state = MIG_STATE_ERROR; - notifier_list_notify(&migration_state_notifiers, NULL); - migrate_fd_cleanup(s); -} - -int migrate_fd_cleanup(FdMigrationState *s) +static int migrate_fd_cleanup(MigrationState *s) { int ret = 0; @@ -307,19 +226,47 @@ int migrate_fd_cleanup(FdMigrationState *s) return ret; } -void migrate_fd_put_notify(void *opaque) +void migrate_fd_error(MigrationState *s) { - FdMigrationState *s = opaque; + DPRINTF("setting error state\n"); + s->state = MIG_STATE_ERROR; + notifier_list_notify(&migration_state_notifiers, s); + migrate_fd_cleanup(s); +} + +static void migrate_fd_completed(MigrationState *s) +{ + DPRINTF("setting completed state\n"); + if (migrate_fd_cleanup(s) < 0) { + s->state = MIG_STATE_ERROR; + } else { + s->state = MIG_STATE_COMPLETED; + runstate_set(RUN_STATE_POSTMIGRATE); + } + notifier_list_notify(&migration_state_notifiers, s); +} + +static void migrate_fd_put_notify(void *opaque) +{ + MigrationState *s = opaque; qemu_set_fd_handler2(s->fd, NULL, NULL, NULL, NULL); qemu_file_put_notify(s->file); + if (qemu_file_get_error(s->file)) { + migrate_fd_error(s); + } } -ssize_t migrate_fd_put_buffer(void *opaque, const void *data, size_t size) +static ssize_t migrate_fd_put_buffer(void *opaque, const void *data, + size_t size) { - FdMigrationState *s = opaque; + MigrationState *s = opaque; ssize_t ret; + if (s->state != MIG_STATE_ACTIVE) { + return -EIO; + } + do { ret = s->write(s, data, size); } while (ret == -1 && ((s->get_error(s)) == EINTR)); @@ -329,115 +276,61 @@ ssize_t migrate_fd_put_buffer(void *opaque, const void *data, size_t size) if (ret == -EAGAIN) { qemu_set_fd_handler2(s->fd, NULL, NULL, migrate_fd_put_notify, s); - } else if (ret < 0) { - s->state = MIG_STATE_ERROR; - notifier_list_notify(&migration_state_notifiers, NULL); } return ret; } -void migrate_fd_connect(FdMigrationState *s) +static void migrate_fd_put_ready(void *opaque) { + MigrationState *s = opaque; int ret; - s->file = qemu_fopen_ops_buffered(s, - s->bandwidth_limit, - migrate_fd_put_buffer, - migrate_fd_put_ready, - migrate_fd_wait_for_unfreeze, - migrate_fd_close); - - DPRINTF("beginning savevm\n"); - ret = qemu_savevm_state_begin(s->mon, s->file, s->mig_state.blk, - s->mig_state.shared); - if (ret < 0) { - DPRINTF("failed, %d\n", ret); - migrate_fd_error(s); - return; - } - - migrate_fd_put_ready(s); -} - -void migrate_fd_put_ready(void *opaque) -{ - FdMigrationState *s = opaque; - if (s->state != MIG_STATE_ACTIVE) { DPRINTF("put_ready returning because of non-active state\n"); return; } DPRINTF("iterate\n"); - if (qemu_savevm_state_iterate(s->mon, s->file) == 1) { - int state; + ret = qemu_savevm_state_iterate(s->mon, s->file); + if (ret < 0) { + migrate_fd_error(s); + } else if (ret == 1) { int old_vm_running = runstate_is_running(); DPRINTF("done iterating\n"); vm_stop_force_state(RUN_STATE_FINISH_MIGRATE); - if ((qemu_savevm_state_complete(s->mon, s->file)) < 0) { - if (old_vm_running) { - vm_start(); - } - state = MIG_STATE_ERROR; + if (qemu_savevm_state_complete(s->mon, s->file) < 0) { + migrate_fd_error(s); } else { - state = MIG_STATE_COMPLETED; + migrate_fd_completed(s); } - if (migrate_fd_cleanup(s) < 0) { + if (s->state != MIG_STATE_COMPLETED) { if (old_vm_running) { vm_start(); } - state = MIG_STATE_ERROR; } - if (state == MIG_STATE_COMPLETED) { - runstate_set(RUN_STATE_POSTMIGRATE); - } - s->state = state; - notifier_list_notify(&migration_state_notifiers, NULL); } } -int migrate_fd_get_status(MigrationState *mig_state) +static void migrate_fd_cancel(MigrationState *s) { - FdMigrationState *s = migrate_to_fms(mig_state); - return s->state; -} - -void migrate_fd_cancel(MigrationState *mig_state) -{ - FdMigrationState *s = migrate_to_fms(mig_state); - if (s->state != MIG_STATE_ACTIVE) return; DPRINTF("cancelling migration\n"); s->state = MIG_STATE_CANCELLED; - notifier_list_notify(&migration_state_notifiers, NULL); + notifier_list_notify(&migration_state_notifiers, s); qemu_savevm_state_cancel(s->mon, s->file); migrate_fd_cleanup(s); } -void migrate_fd_release(MigrationState *mig_state) +static void migrate_fd_wait_for_unfreeze(void *opaque) { - FdMigrationState *s = migrate_to_fms(mig_state); - - DPRINTF("releasing state\n"); - - if (s->state == MIG_STATE_ACTIVE) { - s->state = MIG_STATE_CANCELLED; - notifier_list_notify(&migration_state_notifiers, NULL); - migrate_fd_cleanup(s); - } - g_free(s); -} - -void migrate_fd_wait_for_unfreeze(void *opaque) -{ - FdMigrationState *s = opaque; + MigrationState *s = opaque; int ret; DPRINTF("wait for unfreeze\n"); @@ -452,11 +345,15 @@ void migrate_fd_wait_for_unfreeze(void *opaque) ret = select(s->fd + 1, NULL, &wfds, NULL, NULL); } while (ret == -1 && (s->get_error(s)) == EINTR); + + if (ret == -1) { + qemu_file_set_error(s->file, -s->get_error(s)); + } } -int migrate_fd_close(void *opaque) +static int migrate_fd_close(void *opaque) { - FdMigrationState *s = opaque; + MigrationState *s = opaque; if (s->mon) { monitor_resume(s->mon); @@ -475,11 +372,129 @@ void remove_migration_state_change_notifier(Notifier *notify) notifier_list_remove(&migration_state_notifiers, notify); } -int get_migration_state(void) +bool migration_has_finished(MigrationState *s) { - if (current_migration) { - return migrate_fd_get_status(current_migration); - } else { - return MIG_STATE_ERROR; - } + return s->state == MIG_STATE_COMPLETED; +} + +void migrate_fd_connect(MigrationState *s) +{ + int ret; + + s->state = MIG_STATE_ACTIVE; + s->file = qemu_fopen_ops_buffered(s, + s->bandwidth_limit, + migrate_fd_put_buffer, + migrate_fd_put_ready, + migrate_fd_wait_for_unfreeze, + migrate_fd_close); + + DPRINTF("beginning savevm\n"); + ret = qemu_savevm_state_begin(s->mon, s->file, s->blk, s->shared); + if (ret < 0) { + DPRINTF("failed, %d\n", ret); + migrate_fd_error(s); + return; + } + migrate_fd_put_ready(s); +} + +static MigrationState *migrate_init(Monitor *mon, int detach, int blk, int inc) +{ + MigrationState *s = migrate_get_current(); + int64_t bandwidth_limit = s->bandwidth_limit; + + memset(s, 0, sizeof(*s)); + s->bandwidth_limit = bandwidth_limit; + s->blk = blk; + s->shared = inc; + s->mon = NULL; + s->bandwidth_limit = bandwidth_limit; + s->state = MIG_STATE_SETUP; + + if (!detach) { + migrate_fd_monitor_suspend(s, mon); + } + + return s; +} + +int do_migrate(Monitor *mon, const QDict *qdict, QObject **ret_data) +{ + MigrationState *s = migrate_get_current(); + const char *p; + int detach = qdict_get_try_bool(qdict, "detach", 0); + int blk = qdict_get_try_bool(qdict, "blk", 0); + int inc = qdict_get_try_bool(qdict, "inc", 0); + const char *uri = qdict_get_str(qdict, "uri"); + int ret; + + if (s->state == MIG_STATE_ACTIVE) { + monitor_printf(mon, "migration already in progress\n"); + return -1; + } + + if (qemu_savevm_state_blocked(mon)) { + return -1; + } + + s = migrate_init(mon, detach, blk, inc); + + if (strstart(uri, "tcp:", &p)) { + ret = tcp_start_outgoing_migration(s, p); +#if !defined(WIN32) + } else if (strstart(uri, "exec:", &p)) { + ret = exec_start_outgoing_migration(s, p); + } else if (strstart(uri, "unix:", &p)) { + ret = unix_start_outgoing_migration(s, p); + } else if (strstart(uri, "fd:", &p)) { + ret = fd_start_outgoing_migration(s, p); +#endif + } else { + monitor_printf(mon, "unknown migration protocol: %s\n", uri); + ret = -EINVAL; + } + + if (ret < 0) { + monitor_printf(mon, "migration failed: %s\n", strerror(-ret)); + return ret; + } + + notifier_list_notify(&migration_state_notifiers, s); + return 0; +} + +int do_migrate_cancel(Monitor *mon, const QDict *qdict, QObject **ret_data) +{ + migrate_fd_cancel(migrate_get_current()); + return 0; +} + +int do_migrate_set_speed(Monitor *mon, const QDict *qdict, QObject **ret_data) +{ + int64_t d; + MigrationState *s; + + d = qdict_get_int(qdict, "value"); + if (d < 0) { + d = 0; + } + + s = migrate_get_current(); + s->bandwidth_limit = d; + qemu_file_set_rate_limit(s->file, s->bandwidth_limit); + + return 0; +} + +int do_migrate_set_downtime(Monitor *mon, const QDict *qdict, + QObject **ret_data) +{ + double d; + + d = qdict_get_double(qdict, "value") * 1e9; + d = MAX(0, MIN(UINT64_MAX, d)); + max_downtime = (uint64_t)d; + + return 0; } diff --git a/migration.h b/migration.h index 050c56c5a..a1f80d072 100644 --- a/migration.h +++ b/migration.h @@ -18,37 +18,21 @@ #include "qemu-common.h" #include "notify.h" -#define MIG_STATE_ERROR -1 -#define MIG_STATE_COMPLETED 0 -#define MIG_STATE_CANCELLED 1 -#define MIG_STATE_ACTIVE 2 - typedef struct MigrationState MigrationState; struct MigrationState { - /* FIXME: add more accessors to print migration info */ - void (*cancel)(MigrationState *s); - int (*get_status)(MigrationState *s); - void (*release)(MigrationState *s); - int blk; - int shared; -}; - -typedef struct FdMigrationState FdMigrationState; - -struct FdMigrationState -{ - MigrationState mig_state; int64_t bandwidth_limit; QEMUFile *file; int fd; Monitor *mon; int state; - int (*get_error)(struct FdMigrationState*); - int (*close)(struct FdMigrationState*); - int (*write)(struct FdMigrationState*, const void *, size_t); + int (*get_error)(MigrationState *s); + int (*close)(MigrationState *s); + int (*write)(MigrationState *s, const void *buff, size_t size); void *opaque; + int blk; + int shared; }; void process_incoming_migration(QEMUFile *f); @@ -72,72 +56,27 @@ void do_info_migrate(Monitor *mon, QObject **ret_data); int exec_start_incoming_migration(const char *host_port); -MigrationState *exec_start_outgoing_migration(Monitor *mon, - const char *host_port, - int64_t bandwidth_limit, - int detach, - int blk, - int inc); +int exec_start_outgoing_migration(MigrationState *s, const char *host_port); int tcp_start_incoming_migration(const char *host_port); -MigrationState *tcp_start_outgoing_migration(Monitor *mon, - const char *host_port, - int64_t bandwidth_limit, - int detach, - int blk, - int inc); +int tcp_start_outgoing_migration(MigrationState *s, const char *host_port); int unix_start_incoming_migration(const char *path); -MigrationState *unix_start_outgoing_migration(Monitor *mon, - const char *path, - int64_t bandwidth_limit, - int detach, - int blk, - int inc); +int unix_start_outgoing_migration(MigrationState *s, const char *path); int fd_start_incoming_migration(const char *path); -MigrationState *fd_start_outgoing_migration(Monitor *mon, - const char *fdname, - int64_t bandwidth_limit, - int detach, - int blk, - int inc); +int fd_start_outgoing_migration(MigrationState *s, const char *fdname); -void migrate_fd_monitor_suspend(FdMigrationState *s, Monitor *mon); +void migrate_fd_error(MigrationState *s); -void migrate_fd_error(FdMigrationState *s); - -int migrate_fd_cleanup(FdMigrationState *s); - -void migrate_fd_put_notify(void *opaque); - -ssize_t migrate_fd_put_buffer(void *opaque, const void *data, size_t size); - -void migrate_fd_connect(FdMigrationState *s); - -void migrate_fd_put_ready(void *opaque); - -int migrate_fd_get_status(MigrationState *mig_state); - -void migrate_fd_cancel(MigrationState *mig_state); - -void migrate_fd_release(MigrationState *mig_state); - -void migrate_fd_wait_for_unfreeze(void *opaque); - -int migrate_fd_close(void *opaque); - -static inline FdMigrationState *migrate_to_fms(MigrationState *mig_state) -{ - return container_of(mig_state, FdMigrationState, mig_state); -} +void migrate_fd_connect(MigrationState *s); void add_migration_state_change_notifier(Notifier *notify); void remove_migration_state_change_notifier(Notifier *notify); -int get_migration_state(void); +bool migration_has_finished(MigrationState *); uint64_t ram_bytes_remaining(void); uint64_t ram_bytes_transferred(void); diff --git a/savevm.c b/savevm.c index abb4a6064..cf79a5687 100644 --- a/savevm.c +++ b/savevm.c @@ -173,7 +173,7 @@ struct QEMUFile { int buf_size; /* 0 when writing */ uint8_t buf[IO_BUF_SIZE]; - int has_error; + int last_error; }; typedef struct QEMUFileStdio @@ -425,14 +425,14 @@ QEMUFile *qemu_fopen_ops(void *opaque, QEMUFilePutBufferFunc *put_buffer, return f; } -int qemu_file_has_error(QEMUFile *f) +int qemu_file_get_error(QEMUFile *f) { - return f->has_error; + return f->last_error; } -void qemu_file_set_error(QEMUFile *f) +void qemu_file_set_error(QEMUFile *f, int ret) { - f->has_error = 1; + f->last_error = ret; } void qemu_fflush(QEMUFile *f) @@ -447,7 +447,7 @@ void qemu_fflush(QEMUFile *f) if (len > 0) f->buf_offset += f->buf_index; else - f->has_error = 1; + f->last_error = -EINVAL; f->buf_index = 0; } } @@ -455,6 +455,7 @@ void qemu_fflush(QEMUFile *f) static void qemu_fill_buffer(QEMUFile *f) { int len; + int pending; if (!f->get_buffer) return; @@ -462,13 +463,20 @@ static void qemu_fill_buffer(QEMUFile *f) if (f->is_write) abort(); - len = f->get_buffer(f->opaque, f->buf, f->buf_offset, IO_BUF_SIZE); + pending = f->buf_size - f->buf_index; + if (pending > 0) { + memmove(f->buf, f->buf + f->buf_index, pending); + } + f->buf_index = 0; + f->buf_size = pending; + + len = f->get_buffer(f->opaque, f->buf + pending, f->buf_offset, + IO_BUF_SIZE - pending); if (len > 0) { - f->buf_index = 0; - f->buf_size = len; + f->buf_size += len; f->buf_offset += len; } else if (len != -EAGAIN) - f->has_error = 1; + f->last_error = len; } int qemu_fclose(QEMUFile *f) @@ -490,13 +498,13 @@ void qemu_put_buffer(QEMUFile *f, const uint8_t *buf, int size) { int l; - if (!f->has_error && f->is_write == 0 && f->buf_index > 0) { + if (!f->last_error && f->is_write == 0 && f->buf_index > 0) { fprintf(stderr, "Attempted to write to buffer while read buffer is not empty\n"); abort(); } - while (!f->has_error && size > 0) { + while (!f->last_error && size > 0) { l = IO_BUF_SIZE - f->buf_index; if (l > size) l = size; @@ -512,7 +520,7 @@ void qemu_put_buffer(QEMUFile *f, const uint8_t *buf, int size) void qemu_put_byte(QEMUFile *f, int v) { - if (!f->has_error && f->is_write == 0 && f->buf_index > 0) { + if (!f->last_error && f->is_write == 0 && f->buf_index > 0) { fprintf(stderr, "Attempted to write to buffer while read buffer is not empty\n"); abort(); @@ -524,56 +532,86 @@ void qemu_put_byte(QEMUFile *f, int v) qemu_fflush(f); } -int qemu_get_buffer(QEMUFile *f, uint8_t *buf, int size1) +static void qemu_file_skip(QEMUFile *f, int size) { - int size, l; - - if (f->is_write) - abort(); - - size = size1; - while (size > 0) { - l = f->buf_size - f->buf_index; - if (l == 0) { - qemu_fill_buffer(f); - l = f->buf_size - f->buf_index; - if (l == 0) - break; - } - if (l > size) - l = size; - memcpy(buf, f->buf + f->buf_index, l); - f->buf_index += l; - buf += l; - size -= l; + if (f->buf_index + size <= f->buf_size) { + f->buf_index += size; } - return size1 - size; } -static int qemu_peek_byte(QEMUFile *f) +static int qemu_peek_buffer(QEMUFile *f, uint8_t *buf, int size, size_t offset) { - if (f->is_write) - abort(); + int pending; + int index; - if (f->buf_index >= f->buf_size) { - qemu_fill_buffer(f); - if (f->buf_index >= f->buf_size) - return 0; + if (f->is_write) { + abort(); } - return f->buf[f->buf_index]; + + index = f->buf_index + offset; + pending = f->buf_size - index; + if (pending < size) { + qemu_fill_buffer(f); + index = f->buf_index + offset; + pending = f->buf_size - index; + } + + if (pending <= 0) { + return 0; + } + if (size > pending) { + size = pending; + } + + memcpy(buf, f->buf + index, size); + return size; +} + +int qemu_get_buffer(QEMUFile *f, uint8_t *buf, int size) +{ + int pending = size; + int done = 0; + + while (pending > 0) { + int res; + + res = qemu_peek_buffer(f, buf, pending, 0); + if (res == 0) { + return done; + } + qemu_file_skip(f, res); + buf += res; + pending -= res; + done += res; + } + return done; +} + +static int qemu_peek_byte(QEMUFile *f, int offset) +{ + int index = f->buf_index + offset; + + if (f->is_write) { + abort(); + } + + if (index >= f->buf_size) { + qemu_fill_buffer(f); + index = f->buf_index + offset; + if (index >= f->buf_size) { + return 0; + } + } + return f->buf[index]; } int qemu_get_byte(QEMUFile *f) { - if (f->is_write) - abort(); + int result; - if (f->buf_index >= f->buf_size) { - qemu_fill_buffer(f); - if (f->buf_index >= f->buf_size) - return 0; - } - return f->buf[f->buf_index++]; + result = qemu_peek_byte(f, 0); + qemu_file_skip(f, 1); + return result; } int64_t qemu_ftell(QEMUFile *f) @@ -1466,6 +1504,7 @@ int qemu_savevm_state_begin(Monitor *mon, QEMUFile *f, int blk_enable, int shared) { SaveStateEntry *se; + int ret; QTAILQ_FOREACH(se, &savevm_handlers, entry) { if(se->set_params == NULL) { @@ -1495,17 +1534,27 @@ int qemu_savevm_state_begin(Monitor *mon, QEMUFile *f, int blk_enable, qemu_put_be32(f, se->instance_id); qemu_put_be32(f, se->version_id); - se->save_live_state(mon, f, QEMU_VM_SECTION_START, se->opaque); + ret = se->save_live_state(mon, f, QEMU_VM_SECTION_START, se->opaque); + if (ret < 0) { + qemu_savevm_state_cancel(mon, f); + return ret; + } } - - if (qemu_file_has_error(f)) { + ret = qemu_file_get_error(f); + if (ret != 0) { qemu_savevm_state_cancel(mon, f); - return -EIO; } - return 0; + return ret; + } +/* + * this funtion has three return values: + * negative: there was one error, and we have -errno. + * 0 : We haven't finished, caller have to go again + * 1 : We have finished, we can go to complete phase + */ int qemu_savevm_state_iterate(Monitor *mon, QEMUFile *f) { SaveStateEntry *se; @@ -1520,7 +1569,7 @@ int qemu_savevm_state_iterate(Monitor *mon, QEMUFile *f) qemu_put_be32(f, se->section_id); ret = se->save_live_state(mon, f, QEMU_VM_SECTION_PART, se->opaque); - if (!ret) { + if (ret <= 0) { /* Do not proceed to the next vmstate before this one reported completion of the current stage. This serializes the migration and reduces the probability that a faster changing state is @@ -1528,21 +1577,20 @@ int qemu_savevm_state_iterate(Monitor *mon, QEMUFile *f) break; } } - - if (ret) - return 1; - - if (qemu_file_has_error(f)) { - qemu_savevm_state_cancel(mon, f); - return -EIO; + if (ret != 0) { + return ret; } - - return 0; + ret = qemu_file_get_error(f); + if (ret != 0) { + qemu_savevm_state_cancel(mon, f); + } + return ret; } int qemu_savevm_state_complete(Monitor *mon, QEMUFile *f) { SaveStateEntry *se; + int ret; cpu_synchronize_all_states(); @@ -1554,7 +1602,10 @@ int qemu_savevm_state_complete(Monitor *mon, QEMUFile *f) qemu_put_byte(f, QEMU_VM_SECTION_END); qemu_put_be32(f, se->section_id); - se->save_live_state(mon, f, QEMU_VM_SECTION_END, se->opaque); + ret = se->save_live_state(mon, f, QEMU_VM_SECTION_END, se->opaque); + if (ret < 0) { + return ret; + } } QTAILQ_FOREACH(se, &savevm_handlers, entry) { @@ -1580,10 +1631,7 @@ int qemu_savevm_state_complete(Monitor *mon, QEMUFile *f) qemu_put_byte(f, QEMU_VM_EOF); - if (qemu_file_has_error(f)) - return -EIO; - - return 0; + return qemu_file_get_error(f); } void qemu_savevm_state_cancel(Monitor *mon, QEMUFile *f) @@ -1619,8 +1667,9 @@ static int qemu_savevm_state(Monitor *mon, QEMUFile *f) ret = qemu_savevm_state_complete(mon, f); out: - if (qemu_file_has_error(f)) - ret = -EIO; + if (ret == 0) { + ret = qemu_file_get_error(f); + } return ret; } @@ -1659,29 +1708,36 @@ static const VMStateDescription *vmstate_get_subsection(const VMStateSubsection static int vmstate_subsection_load(QEMUFile *f, const VMStateDescription *vmsd, void *opaque) { - const VMStateSubsection *sub = vmsd->subsections; - - if (!sub || !sub->needed) { - return 0; - } - - while (qemu_peek_byte(f) == QEMU_VM_SUBSECTION) { + while (qemu_peek_byte(f, 0) == QEMU_VM_SUBSECTION) { char idstr[256]; int ret; - uint8_t version_id, len; + uint8_t version_id, len, size; const VMStateDescription *sub_vmsd; - qemu_get_byte(f); /* subsection */ - len = qemu_get_byte(f); - qemu_get_buffer(f, (uint8_t *)idstr, len); - idstr[len] = 0; - version_id = qemu_get_be32(f); + len = qemu_peek_byte(f, 1); + if (len < strlen(vmsd->name) + 1) { + /* subsection name has be be "section_name/a" */ + return 0; + } + size = qemu_peek_buffer(f, (uint8_t *)idstr, len, 2); + if (size != len) { + return 0; + } + idstr[size] = 0; - sub_vmsd = vmstate_get_subsection(sub, idstr); + if (strncmp(vmsd->name, idstr, strlen(vmsd->name)) != 0) { + /* it don't have a valid subsection name */ + return 0; + } + sub_vmsd = vmstate_get_subsection(vmsd->subsections, idstr); if (sub_vmsd == NULL) { return -ENOENT; } - assert(!sub_vmsd->subsections); + qemu_file_skip(f, 1); /* subsection */ + qemu_file_skip(f, 1); /* len */ + qemu_file_skip(f, len); /* idstr */ + version_id = qemu_get_be32(f); + ret = vmstate_load_state(f, sub_vmsd, opaque, version_id); if (ret) { return ret; @@ -1705,7 +1761,6 @@ static void vmstate_subsection_save(QEMUFile *f, const VMStateDescription *vmsd, qemu_put_byte(f, len); qemu_put_buffer(f, (uint8_t *)vmsd->name, len); qemu_put_be32(f, vmsd->version_id); - assert(!vmsd->subsections); vmstate_save_state(f, vmsd, opaque); } sub++; @@ -1831,8 +1886,9 @@ out: g_free(le); } - if (qemu_file_has_error(f)) - ret = -EIO; + if (ret == 0) { + ret = qemu_file_get_error(f); + } return ret; } diff --git a/ui/spice-core.c b/ui/spice-core.c index 3cbc721ee..b33366e5d 100644 --- a/ui/spice-core.c +++ b/ui/spice-core.c @@ -447,9 +447,9 @@ void do_info_spice(Monitor *mon, QObject **ret_data) static void migration_state_notifier(Notifier *notifier, void *data) { - int state = get_migration_state(); + MigrationState *s = data; - if (state == MIG_STATE_COMPLETED) { + if (migration_has_finished(s)) { #if SPICE_SERVER_VERSION >= 0x000701 /* 0.7.1 */ spice_server_migrate_switch(spice_server); #endif