diff options
-rw-r--r-- | Makefile.target | 2 | ||||
-rw-r--r-- | arch_init.c | 2 | ||||
-rw-r--r-- | block-migration.c | 58 | ||||
-rw-r--r-- | default-configs/pci.mak | 1 | ||||
-rw-r--r-- | default-configs/s390x-softmmu.mak | 1 | ||||
-rw-r--r-- | error.c | 13 | ||||
-rw-r--r-- | error.h | 5 | ||||
-rw-r--r-- | exec.c | 18 | ||||
-rw-r--r-- | hmp-commands.hx | 3 | ||||
-rw-r--r-- | hmp.c | 74 | ||||
-rw-r--r-- | hmp.h | 1 | ||||
-rw-r--r-- | hw/pci.c | 1 | ||||
-rw-r--r-- | hw/scsi-bus.c | 18 | ||||
-rw-r--r-- | hw/scsi-disk.c | 49 | ||||
-rw-r--r-- | hw/virtio-scsi.c | 2 | ||||
-rw-r--r-- | migration-fd.c | 2 | ||||
-rw-r--r-- | migration.c | 74 | ||||
-rw-r--r-- | migration.h | 5 | ||||
-rw-r--r-- | pc-bios/bios.bin | bin | 131072 -> 131072 bytes | |||
-rw-r--r-- | qapi-schema.json | 21 | ||||
-rw-r--r-- | qerror.c | 8 | ||||
-rw-r--r-- | qerror.h | 6 | ||||
-rw-r--r-- | qmp-commands.hx | 9 | ||||
-rw-r--r-- | roms/Makefile | 10 | ||||
-rw-r--r-- | roms/config.seabios | 1 | ||||
-rwxr-xr-x | roms/configure-seabios.sh | 5 | ||||
m--------- | roms/seabios | 0 | ||||
-rw-r--r-- | savevm.c | 42 | ||||
-rw-r--r-- | sysemu.h | 11 | ||||
-rw-r--r-- | vmstate.h | 3 |
30 files changed, 287 insertions, 158 deletions
diff --git a/Makefile.target b/Makefile.target index 37fb7edd6d..63cf769dc9 100644 --- a/Makefile.target +++ b/Makefile.target @@ -209,7 +209,7 @@ obj-y = arch_init.o cpus.o monitor.o machine.o gdbstub.o balloon.o ioport.o # need to fix this properly obj-$(CONFIG_NO_PCI) += pci-stub.o obj-$(CONFIG_VIRTIO) += virtio.o virtio-blk.o virtio-balloon.o virtio-net.o virtio-serial-bus.o -obj-$(CONFIG_VIRTIO_SCSI) += virtio-scsi.o +obj-$(CONFIG_VIRTIO) += virtio-scsi.o obj-y += vhost_net.o obj-$(CONFIG_VHOST_NET) += vhost.o obj-$(CONFIG_REALLY_VIRTFS) += 9pfs/virtio-9p-device.o diff --git a/arch_init.c b/arch_init.c index a95ef495fe..595badf50f 100644 --- a/arch_init.c +++ b/arch_init.c @@ -260,7 +260,7 @@ static void sort_ram_list(void) g_free(blocks); } -int ram_save_live(Monitor *mon, QEMUFile *f, int stage, void *opaque) +int ram_save_live(QEMUFile *f, int stage, void *opaque) { ram_addr_t addr; uint64_t bytes_transferred_last; diff --git a/block-migration.c b/block-migration.c index 4467468506..fd2ffff0d5 100644 --- a/block-migration.c +++ b/block-migration.c @@ -18,7 +18,6 @@ #include "hw/hw.h" #include "qemu-queue.h" #include "qemu-timer.h" -#include "monitor.h" #include "block-migration.h" #include "migration.h" #include "blockdev.h" @@ -204,8 +203,7 @@ static void blk_mig_read_cb(void *opaque, int ret) assert(block_mig_state.submitted >= 0); } -static int mig_save_device_bulk(Monitor *mon, QEMUFile *f, - BlkMigDevState *bmds) +static int mig_save_device_bulk(QEMUFile *f, BlkMigDevState *bmds) { int64_t total_sectors = bmds->total_sectors; int64_t cur_sector = bmds->cur_sector; @@ -272,7 +270,6 @@ static void set_dirty_tracking(int enable) static void init_blk_migration_it(void *opaque, BlockDriverState *bs) { - Monitor *mon = opaque; BlkMigDevState *bmds; int64_t sectors; @@ -295,19 +292,17 @@ static void init_blk_migration_it(void *opaque, BlockDriverState *bs) block_mig_state.total_sector_sum += sectors; if (bmds->shared_base) { - monitor_printf(mon, "Start migration for %s with shared base " - "image\n", - bs->device_name); + DPRINTF("Start migration for %s with shared base image\n", + bs->device_name); } else { - monitor_printf(mon, "Start full migration for %s\n", - bs->device_name); + DPRINTF("Start full migration for %s\n", bs->device_name); } QSIMPLEQ_INSERT_TAIL(&block_mig_state.bmds_list, bmds, entry); } } -static void init_blk_migration(Monitor *mon, QEMUFile *f) +static void init_blk_migration(QEMUFile *f) { block_mig_state.submitted = 0; block_mig_state.read_done = 0; @@ -318,10 +313,10 @@ static void init_blk_migration(Monitor *mon, QEMUFile *f) block_mig_state.total_time = 0; block_mig_state.reads = 0; - bdrv_iterate(init_blk_migration_it, mon); + bdrv_iterate(init_blk_migration_it, NULL); } -static int blk_mig_save_bulked_block(Monitor *mon, QEMUFile *f) +static int blk_mig_save_bulked_block(QEMUFile *f) { int64_t completed_sector_sum = 0; BlkMigDevState *bmds; @@ -330,7 +325,7 @@ static int blk_mig_save_bulked_block(Monitor *mon, QEMUFile *f) QSIMPLEQ_FOREACH(bmds, &block_mig_state.bmds_list, entry) { if (bmds->bulk_completed == 0) { - if (mig_save_device_bulk(mon, f, bmds) == 1) { + if (mig_save_device_bulk(f, bmds) == 1) { /* completed bulk section for this device */ bmds->bulk_completed = 1; } @@ -352,8 +347,7 @@ static int blk_mig_save_bulked_block(Monitor *mon, QEMUFile *f) block_mig_state.prev_progress = progress; qemu_put_be64(f, (progress << BDRV_SECTOR_BITS) | BLK_MIG_FLAG_PROGRESS); - monitor_printf(mon, "Completed %d %%\r", progress); - monitor_flush(mon); + DPRINTF("Completed %d %%\r", progress); } return ret; @@ -368,8 +362,8 @@ static void blk_mig_reset_dirty_cursor(void) } } -static int mig_save_device_dirty(Monitor *mon, QEMUFile *f, - BlkMigDevState *bmds, int is_async) +static int mig_save_device_dirty(QEMUFile *f, BlkMigDevState *bmds, + int is_async) { BlkMigBlock *blk; int64_t total_sectors = bmds->total_sectors; @@ -428,20 +422,20 @@ static int mig_save_device_dirty(Monitor *mon, QEMUFile *f, return (bmds->cur_dirty >= bmds->total_sectors); error: - monitor_printf(mon, "Error reading sector %" PRId64 "\n", sector); + DPRINTF("Error reading sector %" PRId64 "\n", sector); qemu_file_set_error(f, ret); g_free(blk->buf); g_free(blk); return 0; } -static int blk_mig_save_dirty_block(Monitor *mon, QEMUFile *f, int is_async) +static int blk_mig_save_dirty_block(QEMUFile *f, int is_async) { BlkMigDevState *bmds; int ret = 0; QSIMPLEQ_FOREACH(bmds, &block_mig_state.bmds_list, entry) { - if (mig_save_device_dirty(mon, f, bmds, is_async) == 0) { + if (mig_save_device_dirty(f, bmds, is_async) == 0) { ret = 1; break; } @@ -520,7 +514,7 @@ static int is_stage2_completed(void) return 0; } -static void blk_mig_cleanup(Monitor *mon) +static void blk_mig_cleanup(void) { BlkMigDevState *bmds; BlkMigBlock *blk; @@ -540,11 +534,9 @@ static void blk_mig_cleanup(Monitor *mon) g_free(blk->buf); g_free(blk); } - - monitor_printf(mon, "\n"); } -static int block_save_live(Monitor *mon, QEMUFile *f, int stage, void *opaque) +static int block_save_live(QEMUFile *f, int stage, void *opaque) { int ret; @@ -552,7 +544,7 @@ static int block_save_live(Monitor *mon, QEMUFile *f, int stage, void *opaque) stage, block_mig_state.submitted, block_mig_state.transferred); if (stage < 0) { - blk_mig_cleanup(mon); + blk_mig_cleanup(); return 0; } @@ -563,7 +555,7 @@ static int block_save_live(Monitor *mon, QEMUFile *f, int stage, void *opaque) } if (stage == 1) { - init_blk_migration(mon, f); + init_blk_migration(f); /* start track dirty blocks */ set_dirty_tracking(1); @@ -573,7 +565,7 @@ static int block_save_live(Monitor *mon, QEMUFile *f, int stage, void *opaque) ret = qemu_file_get_error(f); if (ret) { - blk_mig_cleanup(mon); + blk_mig_cleanup(); return ret; } @@ -586,12 +578,12 @@ static int block_save_live(Monitor *mon, QEMUFile *f, int stage, void *opaque) qemu_file_get_rate_limit(f)) { if (block_mig_state.bulk_completed == 0) { /* first finish the bulk phase */ - if (blk_mig_save_bulked_block(mon, f) == 0) { + 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(mon, f, 1) == 0) { + if (blk_mig_save_dirty_block(f, 1) == 0) { /* no more dirty blocks */ break; } @@ -602,7 +594,7 @@ static int block_save_live(Monitor *mon, QEMUFile *f, int stage, void *opaque) ret = qemu_file_get_error(f); if (ret) { - blk_mig_cleanup(mon); + blk_mig_cleanup(); return ret; } } @@ -612,8 +604,8 @@ static int block_save_live(Monitor *mon, QEMUFile *f, int stage, void *opaque) all async read completed */ assert(block_mig_state.submitted == 0); - while (blk_mig_save_dirty_block(mon, f, 0) != 0); - blk_mig_cleanup(mon); + while (blk_mig_save_dirty_block(f, 0) != 0); + blk_mig_cleanup(); /* report completion */ qemu_put_be64(f, (100 << BDRV_SECTOR_BITS) | BLK_MIG_FLAG_PROGRESS); @@ -623,7 +615,7 @@ static int block_save_live(Monitor *mon, QEMUFile *f, int stage, void *opaque) return ret; } - monitor_printf(mon, "Block migration completed\n"); + DPRINTF("Block migration completed\n"); } qemu_put_be64(f, BLK_MIG_FLAG_EOS); diff --git a/default-configs/pci.mak b/default-configs/pci.mak index 21e4ccfb00..9d3e1dbda1 100644 --- a/default-configs/pci.mak +++ b/default-configs/pci.mak @@ -1,6 +1,5 @@ CONFIG_PCI=y CONFIG_VIRTIO_PCI=y -CONFIG_VIRTIO_SCSI=y CONFIG_VIRTIO=y CONFIG_USB_UHCI=y CONFIG_USB_OHCI=y diff --git a/default-configs/s390x-softmmu.mak b/default-configs/s390x-softmmu.mak index e5888037cf..3005729204 100644 --- a/default-configs/s390x-softmmu.mak +++ b/default-configs/s390x-softmmu.mak @@ -1,2 +1 @@ CONFIG_VIRTIO=y -CONFIG_VIRTIO_SCSI=y @@ -43,6 +43,19 @@ void error_set(Error **errp, const char *fmt, ...) *errp = err; } +Error *error_copy(const Error *err) +{ + Error *err_new; + + err_new = g_malloc0(sizeof(*err)); + err_new->msg = g_strdup(err->msg); + err_new->fmt = err->fmt; + err_new->obj = err->obj; + QINCREF(err_new->obj); + + return err_new; +} + bool error_is_set(Error **errp) { return (errp && *errp); @@ -35,6 +35,11 @@ void error_set(Error **err, const char *fmt, ...) GCC_FMT_ATTR(2, 3); bool error_is_set(Error **err); /** + * Returns an exact copy of the error passed as an argument. + */ +Error *error_copy(const Error *err); + +/** * Get a human readable representation of an error object. */ const char *error_get_pretty(Error *err); @@ -2031,14 +2031,19 @@ static void tlb_unprotect_code_phys(CPUArchState *env, ram_addr_t ram_addr, cpu_physical_memory_set_dirty_flags(ram_addr, CODE_DIRTY_FLAG); } +static bool tlb_is_dirty_ram(CPUTLBEntry *tlbe) +{ + return (tlbe->addr_write & (TLB_INVALID_MASK|TLB_MMIO|TLB_NOTDIRTY)) == 0; +} + static inline void tlb_reset_dirty_range(CPUTLBEntry *tlb_entry, unsigned long start, unsigned long length) { unsigned long addr; - if ((tlb_entry->addr_write & ~TARGET_PAGE_MASK) == io_mem_ram.ram_addr) { + if (tlb_is_dirty_ram(tlb_entry)) { addr = (tlb_entry->addr_write & TARGET_PAGE_MASK) + tlb_entry->addend; if ((addr - start) < length) { - tlb_entry->addr_write = (tlb_entry->addr_write & TARGET_PAGE_MASK) | TLB_NOTDIRTY; + tlb_entry->addr_write |= TLB_NOTDIRTY; } } } @@ -2091,7 +2096,7 @@ static inline void tlb_update_dirty(CPUTLBEntry *tlb_entry) ram_addr_t ram_addr; void *p; - if ((tlb_entry->addr_write & ~TARGET_PAGE_MASK) == io_mem_ram.ram_addr) { + if (tlb_is_dirty_ram(tlb_entry)) { p = (void *)(unsigned long)((tlb_entry->addr_write & TARGET_PAGE_MASK) + tlb_entry->addend); ram_addr = qemu_ram_addr_from_host_nofail(p); @@ -3851,8 +3856,8 @@ void cpu_physical_memory_rw(target_phys_addr_t addr, uint8_t *buf, } } else { /* RAM case */ - ptr = qemu_get_ram_ptr(section->mr->ram_addr) - + section_addr(section, addr); + ptr = qemu_get_ram_ptr(section->mr->ram_addr + + section_addr(section, addr)); memcpy(buf, ptr, l); qemu_put_ram_ptr(ptr); } @@ -4604,7 +4609,8 @@ tb_page_addr_t get_page_addr_code(CPUArchState *env1, target_ulong addr) pd = env1->iotlb[mmu_idx][page_index] & ~TARGET_PAGE_MASK; mr = iotlb_to_region(pd); if (mr != &io_mem_ram && mr != &io_mem_rom - && mr != &io_mem_notdirty && !mr->rom_device) { + && mr != &io_mem_notdirty && !mr->rom_device + && mr != &io_mem_watch) { #if defined(TARGET_ALPHA) || defined(TARGET_MIPS) || defined(TARGET_SPARC) cpu_unassigned_access(env1, addr, 0, 1, 0, 4); #else diff --git a/hmp-commands.hx b/hmp-commands.hx index 6980214a1a..bd35a3eb08 100644 --- a/hmp-commands.hx +++ b/hmp-commands.hx @@ -806,8 +806,7 @@ ETEXI " full copy of disk\n\t\t\t -i for migration without " "shared storage with incremental copy of disk " "(base image shared between src and destination)", - .user_print = monitor_user_noop, - .mhandler.cmd_new = do_migrate, + .mhandler.cmd = hmp_migrate, }, @@ -14,6 +14,7 @@ */ #include "hmp.h" +#include "qemu-timer.h" #include "qmp-commands.h" static void hmp_handle_error(Monitor *mon, Error **errp) @@ -860,3 +861,76 @@ void hmp_block_job_cancel(Monitor *mon, const QDict *qdict) hmp_handle_error(mon, &error); } + +typedef struct MigrationStatus +{ + QEMUTimer *timer; + Monitor *mon; + bool is_block_migration; +} MigrationStatus; + +static void hmp_migrate_status_cb(void *opaque) +{ + MigrationStatus *status = opaque; + MigrationInfo *info; + + info = qmp_query_migrate(NULL); + if (!info->has_status || strcmp(info->status, "active") == 0) { + if (info->has_disk) { + int progress; + + if (info->disk->remaining) { + progress = info->disk->transferred * 100 / info->disk->total; + } else { + progress = 100; + } + + monitor_printf(status->mon, "Completed %d %%\r", progress); + monitor_flush(status->mon); + } + + qemu_mod_timer(status->timer, qemu_get_clock_ms(rt_clock) + 1000); + } else { + if (status->is_block_migration) { + monitor_printf(status->mon, "\n"); + } + monitor_resume(status->mon); + qemu_del_timer(status->timer); + g_free(status); + } + + qapi_free_MigrationInfo(info); +} + +void hmp_migrate(Monitor *mon, const QDict *qdict) +{ + 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"); + Error *err = NULL; + + qmp_migrate(uri, !!blk, blk, !!inc, inc, false, false, &err); + if (err) { + monitor_printf(mon, "migrate: %s\n", error_get_pretty(err)); + error_free(err); + return; + } + + if (!detach) { + MigrationStatus *status; + + if (monitor_suspend(mon) < 0) { + monitor_printf(mon, "terminal does not allow synchronous " + "migration, continuing detached\n"); + return; + } + + status = g_malloc0(sizeof(*status)); + status->mon = mon; + status->is_block_migration = blk || inc; + status->timer = qemu_new_timer_ms(rt_clock, hmp_migrate_status_cb, + status); + qemu_mod_timer(status->timer, qemu_get_clock_ms(rt_clock)); + } +} @@ -59,5 +59,6 @@ void hmp_block_set_io_throttle(Monitor *mon, const QDict *qdict); void hmp_block_stream(Monitor *mon, const QDict *qdict); void hmp_block_job_set_speed(Monitor *mon, const QDict *qdict); void hmp_block_job_cancel(Monitor *mon, const QDict *qdict); +void hmp_migrate(Monitor *mon, const QDict *qdict); #endif @@ -841,7 +841,6 @@ static int pci_unregister_device(DeviceState *dev) pci_unregister_io_regions(pci_dev); pci_del_option_rom(pci_dev); - g_free(pci_dev->romfile); do_pci_unregister_device(pci_dev); return 0; } diff --git a/hw/scsi-bus.c b/hw/scsi-bus.c index 2cb5a18da2..8e76c5d32c 100644 --- a/hw/scsi-bus.c +++ b/hw/scsi-bus.c @@ -7,6 +7,7 @@ #include "trace.h" #include "dma.h" +static char *scsibus_get_dev_path(DeviceState *dev); static char *scsibus_get_fw_dev_path(DeviceState *dev); static int scsi_req_parse(SCSICommand *cmd, SCSIDevice *dev, uint8_t *buf); static void scsi_req_dequeue(SCSIRequest *req); @@ -14,6 +15,7 @@ static void scsi_req_dequeue(SCSIRequest *req); static struct BusInfo scsi_bus_info = { .name = "SCSI", .size = sizeof(SCSIBus), + .get_dev_path = scsibus_get_dev_path, .get_fw_dev_path = scsibus_get_fw_dev_path, .props = (Property[]) { DEFINE_PROP_UINT32("channel", SCSIDevice, channel, 0), @@ -1423,6 +1425,22 @@ void scsi_device_purge_requests(SCSIDevice *sdev, SCSISense sense) sdev->unit_attention = sense; } +static char *scsibus_get_dev_path(DeviceState *dev) +{ + SCSIDevice *d = DO_UPCAST(SCSIDevice, qdev, dev); + DeviceState *hba = dev->parent_bus->parent; + char *id = NULL; + + if (hba && hba->parent_bus && hba->parent_bus->info->get_dev_path) { + id = hba->parent_bus->info->get_dev_path(hba); + } + if (id) { + return g_strdup_printf("%s/%d:%d:%d", id, d->channel, d->id, d->lun); + } else { + return g_strdup_printf("%d:%d:%d", d->channel, d->id, d->lun); + } +} + static char *scsibus_get_fw_dev_path(DeviceState *dev) { SCSIDevice *d = SCSI_DEVICE(dev); diff --git a/hw/scsi-disk.c b/hw/scsi-disk.c index add399e97b..9949786e52 100644 --- a/hw/scsi-disk.c +++ b/hw/scsi-disk.c @@ -471,8 +471,9 @@ static int scsi_disk_emulate_inquiry(SCSIRequest *req, uint8_t *outbuf) case 0x83: /* Device identification page, mandatory */ { - int max_len = 255 - 8; - int id_len = strlen(bdrv_get_device_name(s->qdev.conf.bs)); + const char *str = s->serial ?: bdrv_get_device_name(s->qdev.conf.bs); + int max_len = s->serial ? 20 : 255 - 8; + int id_len = strlen(str); if (id_len > max_len) { id_len = max_len; @@ -486,7 +487,7 @@ static int scsi_disk_emulate_inquiry(SCSIRequest *req, uint8_t *outbuf) outbuf[buflen++] = 0; // reserved outbuf[buflen++] = id_len; // length of data following - memcpy(outbuf+buflen, bdrv_get_device_name(s->qdev.conf.bs), id_len); + memcpy(outbuf+buflen, str, id_len); buflen += id_len; break; } @@ -1152,9 +1153,7 @@ static int scsi_disk_emulate_command(SCSIDiskReq *r) outbuf = r->iov.iov_base; switch (req->cmd.buf[0]) { case TEST_UNIT_READY: - if (s->tray_open || !bdrv_is_inserted(s->qdev.conf.bs)) { - goto not_ready; - } + assert(!s->tray_open && bdrv_is_inserted(s->qdev.conf.bs)); break; case INQUIRY: buflen = scsi_disk_emulate_inquiry(req, outbuf); @@ -1209,7 +1208,8 @@ static int scsi_disk_emulate_command(SCSIDiskReq *r) memset(outbuf, 0, 8); bdrv_get_geometry(s->qdev.conf.bs, &nb_sectors); if (!nb_sectors) { - goto not_ready; + scsi_check_condition(r, SENSE_CODE(LUN_NOT_READY)); + return -1; } if ((req->cmd.buf[8] & 1) == 0 && req->cmd.lba) { goto illegal_request; @@ -1269,7 +1269,8 @@ static int scsi_disk_emulate_command(SCSIDiskReq *r) memset(outbuf, 0, req->cmd.xfer); bdrv_get_geometry(s->qdev.conf.bs, &nb_sectors); if (!nb_sectors) { - goto not_ready; + scsi_check_condition(r, SENSE_CODE(LUN_NOT_READY)); + return -1; } if ((req->cmd.buf[14] & 1) == 0 && req->cmd.lba) { goto illegal_request; @@ -1314,14 +1315,6 @@ static int scsi_disk_emulate_command(SCSIDiskReq *r) buflen = MIN(buflen, req->cmd.xfer); return buflen; -not_ready: - if (s->tray_open || !bdrv_is_inserted(s->qdev.conf.bs)) { - scsi_check_condition(r, SENSE_CODE(NO_MEDIUM)); - } else { - scsi_check_condition(r, SENSE_CODE(LUN_NOT_READY)); - } - return -1; - illegal_request: if (r->req.status == -1) { scsi_check_condition(r, SENSE_CODE(INVALID_FIELD)); @@ -1356,6 +1349,30 @@ static int32_t scsi_send_command(SCSIRequest *req, uint8_t *buf) #endif switch (command) { + case INQUIRY: + case MODE_SENSE: + case MODE_SENSE_10: + case RESERVE: + case RESERVE_10: + case RELEASE: + case RELEASE_10: + case START_STOP: + case ALLOW_MEDIUM_REMOVAL: + case GET_CONFIGURATION: + case GET_EVENT_STATUS_NOTIFICATION: + case MECHANISM_STATUS: + case REQUEST_SENSE: + break; + + default: + if (s->tray_open || !bdrv_is_inserted(s->qdev.conf.bs)) { + scsi_check_condition(r, SENSE_CODE(NO_MEDIUM)); + return 0; + } + break; + } + + switch (command) { case TEST_UNIT_READY: case INQUIRY: case MODE_SENSE: diff --git a/hw/virtio-scsi.c b/hw/virtio-scsi.c index e607edc915..45d54faeb5 100644 --- a/hw/virtio-scsi.c +++ b/hw/virtio-scsi.c @@ -613,5 +613,7 @@ VirtIODevice *virtio_scsi_init(DeviceState *dev, VirtIOSCSIConf *proxyconf) void virtio_scsi_exit(VirtIODevice *vdev) { + VirtIOSCSI *s = (VirtIOSCSI *)vdev; + unregister_savevm(s->qdev, "virtio-scsi", s); virtio_cleanup(vdev); } diff --git a/migration-fd.c b/migration-fd.c index 5a068c632a..50138edb34 100644 --- a/migration-fd.c +++ b/migration-fd.c @@ -75,7 +75,7 @@ static int fd_close(MigrationState *s) int fd_start_outgoing_migration(MigrationState *s, const char *fdname) { - s->fd = monitor_get_fd(s->mon, fdname); + s->fd = monitor_get_fd(cur_mon, fdname); if (s->fd == -1) { DPRINTF("fd_migration: invalid file descriptor identifier\n"); goto err_after_get_fd; diff --git a/migration.c b/migration.c index 00fa1e3f72..8c119ba8ff 100644 --- a/migration.c +++ b/migration.c @@ -158,16 +158,6 @@ MigrationInfo *qmp_query_migrate(Error **errp) /* shared migration helpers */ -static void migrate_fd_monitor_suspend(MigrationState *s, Monitor *mon) -{ - if (monitor_suspend(mon) == 0) { - DPRINTF("suspending monitor\n"); - } else { - monitor_printf(mon, "terminal does not allow synchronous " - "migration, continuing detached\n"); - } -} - static int migrate_fd_cleanup(MigrationState *s) { int ret = 0; @@ -178,10 +168,6 @@ static int migrate_fd_cleanup(MigrationState *s) DPRINTF("closing file\n"); ret = qemu_fclose(s->file); s->file = NULL; - } else { - if (s->mon) { - monitor_resume(s->mon); - } } if (s->fd != -1) { @@ -258,7 +244,7 @@ static void migrate_fd_put_ready(void *opaque) } DPRINTF("iterate\n"); - ret = qemu_savevm_state_iterate(s->mon, s->file); + ret = qemu_savevm_state_iterate(s->file); if (ret < 0) { migrate_fd_error(s); } else if (ret == 1) { @@ -267,7 +253,7 @@ static void migrate_fd_put_ready(void *opaque) DPRINTF("done iterating\n"); vm_stop_force_state(RUN_STATE_FINISH_MIGRATE); - if (qemu_savevm_state_complete(s->mon, s->file) < 0) { + if (qemu_savevm_state_complete(s->file) < 0) { migrate_fd_error(s); } else { migrate_fd_completed(s); @@ -289,7 +275,7 @@ static void migrate_fd_cancel(MigrationState *s) s->state = MIG_STATE_CANCELLED; notifier_list_notify(&migration_state_notifiers, s); - qemu_savevm_state_cancel(s->mon, s->file); + qemu_savevm_state_cancel(s->file); migrate_fd_cleanup(s); } @@ -321,9 +307,6 @@ static int migrate_fd_close(void *opaque) { MigrationState *s = opaque; - if (s->mon) { - monitor_resume(s->mon); - } qemu_set_fd_handler2(s->fd, NULL, NULL, NULL, NULL); return s->close(s); } @@ -367,7 +350,7 @@ void migrate_fd_connect(MigrationState *s) migrate_fd_close); DPRINTF("beginning savevm\n"); - ret = qemu_savevm_state_begin(s->mon, s->file, s->blk, s->shared); + ret = qemu_savevm_state_begin(s->file, s->blk, s->shared); if (ret < 0) { DPRINTF("failed, %d\n", ret); migrate_fd_error(s); @@ -376,7 +359,7 @@ void migrate_fd_connect(MigrationState *s) migrate_fd_put_ready(s); } -static MigrationState *migrate_init(Monitor *mon, int detach, int blk, int inc) +static MigrationState *migrate_init(int blk, int inc) { MigrationState *s = migrate_get_current(); int64_t bandwidth_limit = s->bandwidth_limit; @@ -386,18 +369,9 @@ static MigrationState *migrate_init(Monitor *mon, int detach, int blk, int inc) s->blk = blk; s->shared = inc; - /* s->mon is used for two things: - - pass fd in fd migration - - suspend/resume monitor for not detached migration - */ - s->mon = mon; s->bandwidth_limit = bandwidth_limit; s->state = MIG_STATE_SETUP; - if (!detach) { - migrate_fd_monitor_suspend(s, mon); - } - return s; } @@ -413,32 +387,29 @@ void migrate_del_blocker(Error *reason) migration_blockers = g_slist_remove(migration_blockers, reason); } -int do_migrate(Monitor *mon, const QDict *qdict, QObject **ret_data) +void qmp_migrate(const char *uri, bool has_blk, bool blk, + bool has_inc, bool inc, bool has_detach, bool detach, + Error **errp) { 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; + error_set(errp, QERR_MIGRATION_ACTIVE); + return; } - if (qemu_savevm_state_blocked(mon)) { - return -1; + if (qemu_savevm_state_blocked(errp)) { + return; } if (migration_blockers) { - Error *err = migration_blockers->data; - qerror_report_err(err); - return -1; + *errp = error_copy(migration_blockers->data); + return; } - s = migrate_init(mon, detach, blk, inc); + s = migrate_init(blk, inc); if (strstart(uri, "tcp:", &p)) { ret = tcp_start_outgoing_migration(s, p); @@ -451,21 +422,18 @@ int do_migrate(Monitor *mon, const QDict *qdict, QObject **ret_data) ret = fd_start_outgoing_migration(s, p); #endif } else { - monitor_printf(mon, "unknown migration protocol: %s\n", uri); - ret = -EINVAL; + error_set(errp, QERR_INVALID_PARAMETER_VALUE, "uri", "a valid migration protocol"); + return; } if (ret < 0) { - monitor_printf(mon, "migration failed: %s\n", strerror(-ret)); - return ret; - } - - if (detach) { - s->mon = NULL; + DPRINTF("migration failed: %s\n", strerror(-ret)); + /* FIXME: we should return meaningful errors */ + error_set(errp, QERR_UNDEFINED_ERROR); + return; } notifier_list_notify(&migration_state_notifiers, s); - return 0; } void qmp_migrate_cancel(Error **errp) diff --git a/migration.h b/migration.h index 372b066b48..691b367389 100644 --- a/migration.h +++ b/migration.h @@ -26,7 +26,6 @@ struct MigrationState int64_t bandwidth_limit; QEMUFile *file; int fd; - Monitor *mon; int state; int (*get_error)(MigrationState *s); int (*close)(MigrationState *s); @@ -40,8 +39,6 @@ void process_incoming_migration(QEMUFile *f); int qemu_start_incoming_migration(const char *uri); -int do_migrate(Monitor *mon, const QDict *qdict, QObject **ret_data); - uint64_t migrate_max_downtime(void); void do_info_migrate_print(Monitor *mon, const QObject *data); @@ -78,7 +75,7 @@ uint64_t ram_bytes_remaining(void); uint64_t ram_bytes_transferred(void); uint64_t ram_bytes_total(void); -int ram_save_live(Monitor *mon, QEMUFile *f, int stage, void *opaque); +int ram_save_live(QEMUFile *f, int stage, void *opaque); int ram_load(QEMUFile *f, void *opaque, int version_id); /** diff --git a/pc-bios/bios.bin b/pc-bios/bios.bin Binary files differindex 41e2b38ea2..e1f3923681 100644 --- a/pc-bios/bios.bin +++ b/pc-bios/bios.bin diff --git a/qapi-schema.json b/qapi-schema.json index 04fa84fbde..3a962c1b40 100644 --- a/qapi-schema.json +++ b/qapi-schema.json @@ -1663,3 +1663,24 @@ { 'command': 'qom-list-types', 'data': { '*implements': 'str', '*abstract': 'bool' }, 'returns': [ 'ObjectTypeInfo' ] } + +## +# @migrate +# +# Migrates the current running guest to another Virtual Machine. +# +# @uri: the Uniform Resource Identifier of the destination VM +# +# @blk: #optional do block migration (full disk copy) +# +# @inc: #optional incremental disk copy migration +# +# @detach: this argument exists only for compatibility reasons and +# is ignored by QEMU +# +# Returns: nothing on success +# +# Since: 0.14.0 +## +{ 'command': 'migrate', + 'data': {'uri': 'str', '*blk': 'bool', '*inc': 'bool', '*detach': 'bool' } } @@ -193,6 +193,14 @@ static const QErrorStringTable qerror_table[] = { .desc = "Using KVM without %(capability), %(feature) unavailable", }, { + .error_fmt = QERR_MIGRATION_ACTIVE, + .desc = "There's a migration process in progress", + }, + { + .error_fmt = QERR_MIGRATION_NOT_SUPPORTED, + .desc = "State blocked by non-migratable device '%(device)'", + }, + { .error_fmt = QERR_MIGRATION_EXPECTED, .desc = "An incoming migration is expected before this command can be executed", }, @@ -166,6 +166,12 @@ QError *qobject_to_qerror(const QObject *obj); #define QERR_KVM_MISSING_CAP \ "{ 'class': 'KVMMissingCap', 'data': { 'capability': %s, 'feature': %s } }" +#define QERR_MIGRATION_ACTIVE \ + "{ 'class': 'MigrationActive', 'data': {} }" + +#define QERR_MIGRATION_NOT_SUPPORTED \ + "{ 'class': 'MigrationNotSupported', 'data': {'device': %s} }" + #define QERR_MIGRATION_EXPECTED \ "{ 'class': 'MigrationExpected', 'data': {} }" diff --git a/qmp-commands.hx b/qmp-commands.hx index dfe8a5b40b..8b820382bc 100644 --- a/qmp-commands.hx +++ b/qmp-commands.hx @@ -446,14 +446,7 @@ EQMP { .name = "migrate", .args_type = "detach:-d,blk:-b,inc:-i,uri:s", - .params = "[-d] [-b] [-i] uri", - .help = "migrate to URI (using -d to not wait for completion)" - "\n\t\t\t -b for migration without shared storage with" - " full copy of disk\n\t\t\t -i for migration without " - "shared storage with incremental copy of disk " - "(base image shared between src and destination)", - .user_print = monitor_user_noop, - .mhandler.cmd_new = do_migrate, + .mhandler.cmd_new = qmp_marshal_input_migrate, }, SQMP diff --git a/roms/Makefile b/roms/Makefile new file mode 100644 index 0000000000..0114e6f33f --- /dev/null +++ b/roms/Makefile @@ -0,0 +1,10 @@ + +default: + @echo "nothing is build by default" + @echo "available build targets:" + @echo " bios -- update bios.bin (seabios)" + +bios: config.seabios + sh configure-seabios.sh $< + make -C seabios out/bios.bin + cp seabios/out/bios.bin ../pc-bios/bios.bin diff --git a/roms/config.seabios b/roms/config.seabios new file mode 100644 index 0000000000..c373b87a98 --- /dev/null +++ b/roms/config.seabios @@ -0,0 +1 @@ +# empty, default config works for us diff --git a/roms/configure-seabios.sh b/roms/configure-seabios.sh new file mode 100755 index 0000000000..98f59a24ba --- /dev/null +++ b/roms/configure-seabios.sh @@ -0,0 +1,5 @@ +#!/bin/sh +config="$1" +make -C seabios clean distclean +cp "$config" seabios/.config +make -C seabios oldnoconfig diff --git a/roms/seabios b/roms/seabios -Subproject 80d11e8577bf03e98f2eb1b0cb3a281ab2879c9 +Subproject 2e8bd611ce4e1e36b5a80c9ca6e256e23802f09 @@ -1542,22 +1542,20 @@ static void vmstate_save(QEMUFile *f, SaveStateEntry *se) #define QEMU_VM_SECTION_FULL 0x04 #define QEMU_VM_SUBSECTION 0x05 -bool qemu_savevm_state_blocked(Monitor *mon) +bool qemu_savevm_state_blocked(Error **errp) { SaveStateEntry *se; QTAILQ_FOREACH(se, &savevm_handlers, entry) { if (se->no_migrate) { - monitor_printf(mon, "state blocked by non-migratable device '%s'\n", - se->idstr); + error_set(errp, QERR_MIGRATION_NOT_SUPPORTED, se->idstr); return true; } } return false; } -int qemu_savevm_state_begin(Monitor *mon, QEMUFile *f, int blk_enable, - int shared) +int qemu_savevm_state_begin(QEMUFile *f, int blk_enable, int shared) { SaveStateEntry *se; int ret; @@ -1590,15 +1588,15 @@ 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); - ret = se->save_live_state(mon, f, QEMU_VM_SECTION_START, se->opaque); + ret = se->save_live_state(f, QEMU_VM_SECTION_START, se->opaque); if (ret < 0) { - qemu_savevm_state_cancel(mon, f); + qemu_savevm_state_cancel(f); return ret; } } ret = qemu_file_get_error(f); if (ret != 0) { - qemu_savevm_state_cancel(mon, f); + qemu_savevm_state_cancel(f); } return ret; @@ -1611,7 +1609,7 @@ int qemu_savevm_state_begin(Monitor *mon, QEMUFile *f, int blk_enable, * 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) +int qemu_savevm_state_iterate(QEMUFile *f) { SaveStateEntry *se; int ret = 1; @@ -1624,7 +1622,7 @@ int qemu_savevm_state_iterate(Monitor *mon, QEMUFile *f) qemu_put_byte(f, QEMU_VM_SECTION_PART); qemu_put_be32(f, se->section_id); - ret = se->save_live_state(mon, f, QEMU_VM_SECTION_PART, se->opaque); + ret = se->save_live_state(f, QEMU_VM_SECTION_PART, se->opaque); if (ret <= 0) { /* Do not proceed to the next vmstate before this one reported completion of the current stage. This serializes the migration @@ -1638,12 +1636,12 @@ int qemu_savevm_state_iterate(Monitor *mon, QEMUFile *f) } ret = qemu_file_get_error(f); if (ret != 0) { - qemu_savevm_state_cancel(mon, f); + qemu_savevm_state_cancel(f); } return ret; } -int qemu_savevm_state_complete(Monitor *mon, QEMUFile *f) +int qemu_savevm_state_complete(QEMUFile *f) { SaveStateEntry *se; int ret; @@ -1658,7 +1656,7 @@ int qemu_savevm_state_complete(Monitor *mon, QEMUFile *f) qemu_put_byte(f, QEMU_VM_SECTION_END); qemu_put_be32(f, se->section_id); - ret = se->save_live_state(mon, f, QEMU_VM_SECTION_END, se->opaque); + ret = se->save_live_state(f, QEMU_VM_SECTION_END, se->opaque); if (ret < 0) { return ret; } @@ -1690,37 +1688,37 @@ int qemu_savevm_state_complete(Monitor *mon, QEMUFile *f) return qemu_file_get_error(f); } -void qemu_savevm_state_cancel(Monitor *mon, QEMUFile *f) +void qemu_savevm_state_cancel(QEMUFile *f) { SaveStateEntry *se; QTAILQ_FOREACH(se, &savevm_handlers, entry) { if (se->save_live_state) { - se->save_live_state(mon, f, -1, se->opaque); + se->save_live_state(f, -1, se->opaque); } } } -static int qemu_savevm_state(Monitor *mon, QEMUFile *f) +static int qemu_savevm_state(QEMUFile *f) { int ret; - if (qemu_savevm_state_blocked(mon)) { + if (qemu_savevm_state_blocked(NULL)) { ret = -EINVAL; goto out; } - ret = qemu_savevm_state_begin(mon, f, 0, 0); + ret = qemu_savevm_state_begin(f, 0, 0); if (ret < 0) goto out; do { - ret = qemu_savevm_state_iterate(mon, f); + ret = qemu_savevm_state_iterate(f); if (ret < 0) goto out; } while (ret == 0); - ret = qemu_savevm_state_complete(mon, f); + ret = qemu_savevm_state_complete(f); out: if (ret == 0) { @@ -1839,7 +1837,7 @@ int qemu_loadvm_state(QEMUFile *f) unsigned int v; int ret; - if (qemu_savevm_state_blocked(default_mon)) { + if (qemu_savevm_state_blocked(NULL)) { return -EINVAL; } @@ -2083,7 +2081,7 @@ void do_savevm(Monitor *mon, const QDict *qdict) monitor_printf(mon, "Could not open VM state file\n"); goto the_end; } - ret = qemu_savevm_state(mon, f); + ret = qemu_savevm_state(f); vm_state_size = qemu_ftell(f); qemu_fclose(f); if (ret < 0) { @@ -76,12 +76,11 @@ void do_info_snapshots(Monitor *mon); void qemu_announce_self(void); -bool qemu_savevm_state_blocked(Monitor *mon); -int qemu_savevm_state_begin(Monitor *mon, QEMUFile *f, int blk_enable, - int shared); -int qemu_savevm_state_iterate(Monitor *mon, QEMUFile *f); -int qemu_savevm_state_complete(Monitor *mon, QEMUFile *f); -void qemu_savevm_state_cancel(Monitor *mon, QEMUFile *f); +bool qemu_savevm_state_blocked(Error **errp); +int qemu_savevm_state_begin(QEMUFile *f, int blk_enable, int shared); +int qemu_savevm_state_iterate(QEMUFile *f); +int qemu_savevm_state_complete(QEMUFile *f); +void qemu_savevm_state_cancel(QEMUFile *f); int qemu_loadvm_state(QEMUFile *f); /* SLIRP */ @@ -28,8 +28,7 @@ typedef void SaveSetParamsHandler(int blk_enable, int shared, void * opaque); typedef void SaveStateHandler(QEMUFile *f, void *opaque); -typedef int SaveLiveStateHandler(Monitor *mon, QEMUFile *f, int stage, - void *opaque); +typedef int SaveLiveStateHandler(QEMUFile *f, int stage, void *opaque); typedef int LoadStateHandler(QEMUFile *f, void *opaque, int version_id); int register_savevm(DeviceState *dev, |