diff --git a/usr.sbin/bhyve/atkbdc.h b/usr.sbin/bhyve/atkbdc.h --- a/usr.sbin/bhyve/atkbdc.h +++ b/usr.sbin/bhyve/atkbdc.h @@ -30,14 +30,9 @@ #define _ATKBDC_H_ struct atkbdc_softc; -struct vm_snapshot_meta; struct vmctx; void atkbdc_init(struct vmctx *ctx); void atkbdc_event(struct atkbdc_softc *sc, int iskbd); -#ifdef BHYVE_SNAPSHOT -int atkbdc_snapshot(struct vm_snapshot_meta *meta); -#endif - #endif /* _ATKBDC_H_ */ diff --git a/usr.sbin/bhyve/atkbdc.c b/usr.sbin/bhyve/atkbdc.c --- a/usr.sbin/bhyve/atkbdc.c +++ b/usr.sbin/bhyve/atkbdc.c @@ -56,6 +56,10 @@ #include "ps2kbd.h" #include "ps2mouse.h" +#ifdef BHYVE_SNAPSHOT +#include "snapshot.h" +#endif + #define KBD_DATA_PORT 0x60 #define KBD_STS_CTL_PORT 0x64 @@ -138,10 +142,6 @@ struct aux_dev aux; }; -#ifdef BHYVE_SNAPSHOT -static struct atkbdc_softc *atkbdc_sc = NULL; -#endif - static void atkbdc_assert_kbd_intr(struct atkbdc_softc *sc) { @@ -511,6 +511,14 @@ pthread_mutex_unlock(&sc->mtx); } +#ifdef BHYVE_SNAPSHOT +extern int atkbdc_snapshot(struct vm_snapshot_meta *, void *); + +static struct snapshot_ops atkbdc_snapshot_ops = { + .snapshot_cb = atkbdc_snapshot, +}; +#endif + void atkbdc_init(struct vmctx *ctx) { @@ -555,42 +563,40 @@ sc->ps2mouse_sc = ps2mouse_init(sc); #ifdef BHYVE_SNAPSHOT - assert(atkbdc_sc == NULL); - atkbdc_sc = sc; + register_snapshot_dev("atkbdc", &atkbdc_snapshot_ops, sc); #endif } #ifdef BHYVE_SNAPSHOT int -atkbdc_snapshot(struct vm_snapshot_meta *meta) +atkbdc_snapshot(struct vm_snapshot_meta *meta, void *cbdata) { int ret; - - SNAPSHOT_VAR_OR_LEAVE(atkbdc_sc->status, meta, ret, done); - SNAPSHOT_VAR_OR_LEAVE(atkbdc_sc->outport, meta, ret, done); - SNAPSHOT_BUF_OR_LEAVE(atkbdc_sc->ram, - sizeof(atkbdc_sc->ram), meta, ret, done); - SNAPSHOT_VAR_OR_LEAVE(atkbdc_sc->curcmd, meta, ret, done); - SNAPSHOT_VAR_OR_LEAVE(atkbdc_sc->ctrlbyte, meta, ret, done); - SNAPSHOT_VAR_OR_LEAVE(atkbdc_sc->kbd, meta, ret, done); - - SNAPSHOT_VAR_OR_LEAVE(atkbdc_sc->kbd.irq_active, meta, ret, done); - SNAPSHOT_VAR_OR_LEAVE(atkbdc_sc->kbd.irq, meta, ret, done); - SNAPSHOT_BUF_OR_LEAVE(atkbdc_sc->kbd.buffer, - sizeof(atkbdc_sc->kbd.buffer), meta, ret, done); - SNAPSHOT_VAR_OR_LEAVE(atkbdc_sc->kbd.brd, meta, ret, done); - SNAPSHOT_VAR_OR_LEAVE(atkbdc_sc->kbd.bwr, meta, ret, done); - SNAPSHOT_VAR_OR_LEAVE(atkbdc_sc->kbd.bcnt, meta, ret, done); - - SNAPSHOT_VAR_OR_LEAVE(atkbdc_sc->aux.irq_active, meta, ret, done); - SNAPSHOT_VAR_OR_LEAVE(atkbdc_sc->aux.irq, meta, ret, done); - - ret = ps2kbd_snapshot(atkbdc_sc->ps2kbd_sc, meta); - if (ret != 0) - goto done; - - ret = ps2mouse_snapshot(atkbdc_sc->ps2mouse_sc, meta); - + struct atkbdc_softc *sc = (struct atkbdc_softc *)cbdata; + + assert(sc != NULL); + + SNAPSHOT_VAR_OR_LEAVE(sc->status, meta, ret, done); + SNAPSHOT_VAR_OR_LEAVE(sc->outport, meta, ret, done); + SNAPSHOT_BUF_OR_LEAVE(sc->ram, sizeof (sc->ram), meta, ret, done); + SNAPSHOT_VAR_OR_LEAVE(sc->curcmd, meta, ret, done); + SNAPSHOT_VAR_OR_LEAVE(sc->ctrlbyte, meta, ret, done); + SNAPSHOT_VAR_OR_LEAVE(sc->kbd, meta, ret, done); + + SNAPSHOT_VAR_OR_LEAVE(sc->kbd.irq_active, meta, ret, done); + SNAPSHOT_VAR_OR_LEAVE(sc->kbd.irq, meta, ret, done); + SNAPSHOT_BUF_OR_LEAVE(sc->kbd.buffer, + sizeof (sc->kbd.buffer), meta, ret, done); + SNAPSHOT_VAR_OR_LEAVE(sc->kbd.brd, meta, ret, done); + SNAPSHOT_VAR_OR_LEAVE(sc->kbd.bwr, meta, ret, done); + SNAPSHOT_VAR_OR_LEAVE(sc->kbd.bcnt, meta, ret, done); + + SNAPSHOT_VAR_OR_LEAVE(sc->aux.irq_active, meta, ret, done); + SNAPSHOT_VAR_OR_LEAVE(sc->aux.irq, meta, ret, done); + + ret = ps2kbd_snapshot(sc->ps2kbd_sc, meta); + if (ret == 0) + ret = ps2mouse_snapshot(sc->ps2mouse_sc, meta); done: return (ret); } diff --git a/usr.sbin/bhyve/bhyverun.c b/usr.sbin/bhyve/bhyverun.c --- a/usr.sbin/bhyve/bhyverun.c +++ b/usr.sbin/bhyve/bhyverun.c @@ -1498,7 +1498,7 @@ } fprintf(stdout, "Pausing pci devs...\r\n"); - if (vm_pause_user_devs() != 0) { + if (vm_pause_devices() != 0) { fprintf(stderr, "Failed to pause PCI device state.\n"); exit(1); } @@ -1510,7 +1510,7 @@ } fprintf(stdout, "Restoring pci devs...\r\n"); - if (vm_restore_user_devs(ctx, &rstate) != 0) { + if (vm_restore_devices(ctx, &rstate) != 0) { fprintf(stderr, "Failed to restore PCI device state.\n"); exit(1); } @@ -1522,7 +1522,7 @@ } fprintf(stdout, "Resuming pci devs...\r\n"); - if (vm_resume_user_devs() != 0) { + if (vm_resume_devices() != 0) { fprintf(stderr, "Failed to resume PCI device state.\n"); exit(1); } diff --git a/usr.sbin/bhyve/pci_emul.h b/usr.sbin/bhyve/pci_emul.h --- a/usr.sbin/bhyve/pci_emul.h +++ b/usr.sbin/bhyve/pci_emul.h @@ -80,7 +80,6 @@ int (*pe_snapshot)(struct vm_snapshot_meta *meta); int (*pe_pause)(struct pci_devinst *pi); int (*pe_resume)(struct pci_devinst *pi); - }; #define PCI_EMUL_SET(x) DATA_SET(pci_devemu_set, x); @@ -259,11 +258,6 @@ void pci_write_dsdt(void); uint64_t pci_ecfg_base(void); int pci_bus_configured(int bus); -#ifdef BHYVE_SNAPSHOT -int pci_snapshot(struct vm_snapshot_meta *meta); -int pci_pause(const char *dev_name); -int pci_resume(const char *dev_name); -#endif static __inline void pci_set_cfgdata8(struct pci_devinst *pi, int offset, uint8_t val) diff --git a/usr.sbin/bhyve/pci_emul.c b/usr.sbin/bhyve/pci_emul.c --- a/usr.sbin/bhyve/pci_emul.c +++ b/usr.sbin/bhyve/pci_emul.c @@ -62,6 +62,10 @@ #include "pci_irq.h" #include "pci_lpc.h" +#ifdef BHYVE_SNAPSHOT +#include "snapshot.h" +#endif + #define CONF1_ADDR_PORT 0x0cf8 #define CONF1_DATA_PORT 0x0cfc @@ -985,7 +989,9 @@ pdi->pi_lintr.pirq_pin = 0; pdi->pi_lintr.ioapic_irq = 0; pdi->pi_d = pde; - snprintf(pdi->pi_name, PI_NAMESZ, "%s-pci-%d", pde->pe_emu, slot); + + snprintf(pdi->pi_name, PI_NAMESZ, "%s-pci-%d-%d-%d", pde->pe_emu, bus, + slot, func); /* Disable legacy interrupts */ pci_set_cfgdata8(pdi, PCIR_INTLINE, 255); @@ -1322,6 +1328,18 @@ #define BUSMEM32_ROUNDUP (1024 * 1024) #define BUSMEM64_ROUNDUP (512 * 1024 * 1024) +#ifdef BHYVE_SNAPSHOT +static int pci_snapshot(struct vm_snapshot_meta *, void *); +static int pci_pause(void *); +static int pci_resume(void *); + +static struct snapshot_ops pci_snapshot_ops = { + .snapshot_cb = pci_snapshot, + .pause_cb = pci_pause, + .resume_cb = pci_resume, +}; +#endif + int init_pci(struct vmctx *ctx) { @@ -1400,6 +1418,10 @@ func, fi); if (error) return (error); +#ifdef BHYVE_SNAPSHOT + register_snapshot_dev(fi->fi_devi->pi_name, + &pci_snapshot_ops, fi->fi_devi); +#endif } } @@ -2321,58 +2343,15 @@ } static int -pci_find_slotted_dev(const char *dev_name, struct pci_devemu **pde, - struct pci_devinst **pdi) -{ - struct businfo *bi; - struct slotinfo *si; - struct funcinfo *fi; - int bus, slot, func; - - assert(dev_name != NULL); - assert(pde != NULL); - assert(pdi != NULL); - - for (bus = 0; bus < MAXBUSES; bus++) { - if ((bi = pci_businfo[bus]) == NULL) - continue; - - for (slot = 0; slot < MAXSLOTS; slot++) { - si = &bi->slotinfo[slot]; - for (func = 0; func < MAXFUNCS; func++) { - fi = &si->si_funcs[func]; - if (fi->fi_pde == NULL) - continue; - if (strcmp(dev_name, fi->fi_pde->pe_emu) != 0) - continue; - - *pde = fi->fi_pde; - *pdi = fi->fi_devi; - return (0); - } - } - } - - return (EINVAL); -} - -int -pci_snapshot(struct vm_snapshot_meta *meta) +pci_snapshot(struct vm_snapshot_meta *meta, void *cbdata) { struct pci_devemu *pde; - struct pci_devinst *pdi; + struct pci_devinst *pdi = (struct pci_devinst *)cbdata; int ret; - assert(meta->dev_name != NULL); - - ret = pci_find_slotted_dev(meta->dev_name, &pde, &pdi); - if (ret != 0) { - fprintf(stderr, "%s: no such name: %s\r\n", - __func__, meta->dev_name); - memset(meta->buffer.buf_start, 0, meta->buffer.buf_size); - return (0); - } + assert(pdi != NULL); + pde = pdi->pi_d; meta->dev_data = pdi; if (pde->pe_snapshot == NULL) { @@ -2383,8 +2362,8 @@ ret = pci_snapshot_pci_dev(meta); if (ret != 0) { - fprintf(stderr, "%s: failed to snapshot pci dev\r\n", - __func__); + fprintf(stderr, "%s: failed to snapshot pci dev %s: %d\n", + __func__, meta->dev_name, ret); return (-1); } @@ -2393,58 +2372,40 @@ return (ret); } -int -pci_pause(const char *dev_name) +static int +pci_pause(void *cbdata) { struct pci_devemu *pde; - struct pci_devinst *pdi; - int ret; + struct pci_devinst *pdi = (struct pci_devinst *)cbdata; - assert(dev_name != NULL); + assert(pdi != NULL); - ret = pci_find_slotted_dev(dev_name, &pde, &pdi); - if (ret != 0) { - /* - * It is possible to call this function without - * checking that the device is inserted first. - */ - fprintf(stderr, "%s: no such name: %s\n", __func__, dev_name); - return (0); - } + pde = pdi->pi_d; if (pde->pe_pause == NULL) { /* The pause/resume functionality is optional. */ fprintf(stderr, "%s: not implemented for: %s\n", - __func__, dev_name); + __func__, pdi->pi_name); return (0); } return (*pde->pe_pause)(pdi); } -int -pci_resume(const char *dev_name) +static int +pci_resume(void *cbdata) { struct pci_devemu *pde; - struct pci_devinst *pdi; - int ret; + struct pci_devinst *pdi = (struct pci_devinst *)cbdata; - assert(dev_name != NULL); + assert(pdi != NULL); - ret = pci_find_slotted_dev(dev_name, &pde, &pdi); - if (ret != 0) { - /* - * It is possible to call this function without - * checking that the device is inserted first. - */ - fprintf(stderr, "%s: no such name: %s\n", __func__, dev_name); - return (0); - } + pde = pdi->pi_d; if (pde->pe_resume == NULL) { /* The pause/resume functionality is optional. */ fprintf(stderr, "%s: not implemented for: %s\n", - __func__, dev_name); + __func__, pdi->pi_name); return (0); } diff --git a/usr.sbin/bhyve/pci_hostbridge.c b/usr.sbin/bhyve/pci_hostbridge.c --- a/usr.sbin/bhyve/pci_hostbridge.c +++ b/usr.sbin/bhyve/pci_hostbridge.c @@ -64,6 +64,14 @@ return (0); } +#ifdef BHYVE_SNAPSHOT +static int +pci_hostbridge_snapshot(struct vm_snapshot_meta *meta __unused) +{ + return (0); +} +#endif + static int pci_amd_hostbridge_legacy_config(nvlist_t *nvl, const char *opts __unused) { @@ -77,11 +85,17 @@ .pe_emu = "amd_hostbridge", .pe_legacy_config = pci_amd_hostbridge_legacy_config, .pe_alias = "hostbridge", +#ifdef BHYVE_SNAPSHOT + .pe_snapshot = pci_hostbridge_snapshot, +#endif }; PCI_EMUL_SET(pci_de_amd_hostbridge); static const struct pci_devemu pci_de_hostbridge = { .pe_emu = "hostbridge", .pe_init = pci_hostbridge_init, +#ifdef BHYVE_SNAPSHOT + .pe_snapshot = pci_hostbridge_snapshot, +#endif }; PCI_EMUL_SET(pci_de_hostbridge); diff --git a/usr.sbin/bhyve/snapshot.h b/usr.sbin/bhyve/snapshot.h --- a/usr.sbin/bhyve/snapshot.h +++ b/usr.sbin/bhyve/snapshot.h @@ -65,22 +65,25 @@ int socket_fd; }; -typedef int (*vm_snapshot_dev_cb)(struct vm_snapshot_meta *); -typedef int (*vm_pause_dev_cb) (const char *); -typedef int (*vm_resume_dev_cb) (const char *); - -struct vm_snapshot_dev_info { - const char *dev_name; /* device name */ - vm_snapshot_dev_cb snapshot_cb; /* callback for device snapshot */ - vm_pause_dev_cb pause_cb; /* callback for device pause */ - vm_resume_dev_cb resume_cb; /* callback for device resume */ +typedef int (*snapshot_dev_cb)(struct vm_snapshot_meta *meta, void *cbdata); +typedef int (*pause_dev_cb)(void *cbdata); +typedef int (*resume_dev_cb)(void *cbdata); + +struct snapshot_ops { + snapshot_dev_cb snapshot_cb; /* callback for device save/restore */ + pause_dev_cb pause_cb; /* callback for device pause (optional) */ + resume_dev_cb resume_cb; /* callback for device resume (optional) */ }; struct vm_snapshot_kern_info { - const char *struct_name; /* kernel structure name*/ + const char *dev_name; /* kernel device name */ enum snapshot_req req; /* request type */ }; +struct snapshot_ops; + +void register_snapshot_dev(const char *devname, struct snapshot_ops *ops, + void *cbdata); void destroy_restore_state(struct restore_state *rstate); const char *lookup_vmname(struct restore_state *rstate); @@ -95,9 +98,9 @@ int restore_vm_mem(struct vmctx *ctx, struct restore_state *rstate); int vm_restore_kern_structs(struct vmctx *ctx, struct restore_state *rstate); -int vm_restore_user_devs(struct vmctx *ctx, struct restore_state *rstate); -int vm_pause_user_devs(void); -int vm_resume_user_devs(void); +int vm_restore_devices(struct vmctx *ctx, struct restore_state *rstate); +int vm_pause_devices(void); +int vm_resume_devices(void); int get_checkpoint_msg(int conn_fd, struct vmctx *ctx); void *checkpoint_thread(void *param); diff --git a/usr.sbin/bhyve/snapshot.c b/usr.sbin/bhyve/snapshot.c --- a/usr.sbin/bhyve/snapshot.c +++ b/usr.sbin/bhyve/snapshot.c @@ -119,12 +119,12 @@ #define SNAPSHOT_BUFFER_SIZE (20 * MB) -#define JSON_STRUCT_ARR_KEY "structs" +#define JSON_KERNEL_ARR_KEY "kern_structs" #define JSON_DEV_ARR_KEY "devices" -#define JSON_BASIC_METADATA_KEY "basic metadata" -#define JSON_SNAPSHOT_REQ_KEY "snapshot_req" +#define JSON_BASIC_METADATA_KEY "basic metadata" +#define JSON_SNAPSHOT_REQ_KEY "device" #define JSON_SIZE_KEY "size" -#define JSON_FILE_OFFSET_KEY "file_offset" +#define JSON_FILE_OFFSET_KEY "file_offset" #define JSON_NCPUS_KEY "ncpus" #define JSON_VMNAME_KEY "vmname" @@ -138,20 +138,6 @@ _a < _b ? _a : _b; \ }) -static const struct vm_snapshot_dev_info snapshot_devs[] = { - { "atkbdc", atkbdc_snapshot, NULL, NULL }, - { "virtio-net", pci_snapshot, pci_pause, pci_resume }, - { "virtio-blk", pci_snapshot, pci_pause, pci_resume }, - { "virtio-rnd", pci_snapshot, NULL, NULL }, - { "lpc", pci_snapshot, NULL, NULL }, - { "fbuf", pci_snapshot, NULL, NULL }, - { "xhci", pci_snapshot, NULL, NULL }, - { "e1000", pci_snapshot, NULL, NULL }, - { "ahci", pci_snapshot, pci_pause, pci_resume }, - { "ahci-hd", pci_snapshot, pci_pause, pci_resume }, - { "ahci-cd", pci_snapshot, pci_pause, pci_resume }, -}; - static const struct vm_snapshot_kern_info snapshot_kern_structs[] = { { "vhpet", STRUCT_VHPET }, { "vm", STRUCT_VM }, @@ -169,6 +155,31 @@ static pthread_cond_t vcpus_idle, vcpus_can_run; static bool checkpoint_active; +struct snapshot_dev { + LIST_ENTRY(snapshot_dev) dev_link; + const char *dev_name; + struct snapshot_ops *dev_ops; + void *dev_cbdata; +}; + +static LIST_HEAD(, snapshot_dev) snapshot_devices; + +void +register_snapshot_dev(const char *name, struct snapshot_ops *ops, + void *cbdata) +{ + struct snapshot_dev *dev; + + assert(ops != NULL && ops->snapshot_cb != NULL); + + dev = calloc(1, sizeof (struct snapshot_dev)); + + dev->dev_name = name; + dev->dev_ops = ops; + dev->dev_cbdata = cbdata; + LIST_INSERT_HEAD(&snapshot_devices, dev, dev_link); +} + /* * TODO: Harden this function and all of its callers since 'base_str' is a user * provided string. @@ -415,50 +426,6 @@ } \ } while(0) -static void * -lookup_struct(enum snapshot_req struct_id, struct restore_state *rstate, - size_t *struct_size) -{ - const ucl_object_t *structs = NULL, *obj = NULL; - ucl_object_iter_t it = NULL; - int64_t snapshot_req, size, file_offset; - - structs = ucl_object_lookup(rstate->meta_root_obj, JSON_STRUCT_ARR_KEY); - if (structs == NULL) { - fprintf(stderr, "Failed to find '%s' object.\n", - JSON_STRUCT_ARR_KEY); - return (NULL); - } - - if (ucl_object_type(structs) != UCL_ARRAY) { - fprintf(stderr, "Object '%s' is not an array.\n", - JSON_STRUCT_ARR_KEY); - return (NULL); - } - - while ((obj = ucl_object_iterate(structs, &it, true)) != NULL) { - snapshot_req = -1; - JSON_GET_INT_OR_RETURN(JSON_SNAPSHOT_REQ_KEY, obj, - &snapshot_req, NULL); - assert(snapshot_req >= 0); - if ((enum snapshot_req) snapshot_req == struct_id) { - JSON_GET_INT_OR_RETURN(JSON_SIZE_KEY, obj, - &size, NULL); - assert(size >= 0); - - JSON_GET_INT_OR_RETURN(JSON_FILE_OFFSET_KEY, obj, - &file_offset, NULL); - assert(file_offset >= 0); - assert((uint64_t)file_offset + size <= - rstate->kdata_len); - - *struct_size = (size_t)size; - return ((uint8_t *)rstate->kdata_map + file_offset); - } - } - - return (NULL); -} static void * lookup_check_dev(const char *dev_name, struct restore_state *rstate, @@ -489,14 +456,14 @@ } static void* -lookup_dev(const char *dev_name, struct restore_state *rstate, - size_t *data_size) +lookup_dev(const char *dev_name, const char *key, struct restore_state *rstate, + size_t *data_size) { const ucl_object_t *devs = NULL, *obj = NULL; ucl_object_iter_t it = NULL; void *ret; - devs = ucl_object_lookup(rstate->meta_root_obj, JSON_DEV_ARR_KEY); + devs = ucl_object_lookup(rstate->meta_root_obj, key); if (devs == NULL) { fprintf(stderr, "Failed to find '%s' object.\n", JSON_DEV_ARR_KEY); @@ -861,97 +828,71 @@ return (0); } -static int -vm_restore_kern_struct(struct vmctx *ctx, struct restore_state *rstate, - const struct vm_snapshot_kern_info *info) +int +vm_restore_kern_structs(struct vmctx *ctx, struct restore_state *rstate) { - void *struct_ptr; - size_t struct_size; int ret; - struct vm_snapshot_meta *meta; - - struct_ptr = lookup_struct(info->req, rstate, &struct_size); - if (struct_ptr == NULL) { - fprintf(stderr, "%s: Failed to lookup struct %s\r\n", - __func__, info->struct_name); - ret = -1; - goto done; - } - - if (struct_size == 0) { - fprintf(stderr, "%s: Kernel struct size was 0 for: %s\r\n", - __func__, info->struct_name); - ret = -1; - goto done; - } - - meta = &(struct vm_snapshot_meta) { - .ctx = ctx, - .dev_name = info->struct_name, - .dev_req = info->req, - .buffer.buf_start = struct_ptr, - .buffer.buf_size = struct_size, + for (unsigned i = 0; i < nitems(snapshot_kern_structs); i++) { + const struct vm_snapshot_kern_info *info; + struct vm_snapshot_meta *meta; + void *data; + size_t size; - .buffer.buf = struct_ptr, - .buffer.buf_rem = struct_size, + info = &snapshot_kern_structs[i]; + data = lookup_dev(info->dev_name, JSON_KERNEL_ARR_KEY, rstate, &size); + if (data == NULL) + errx(EX_DATAERR, "Cannot find kern struct %s", info->dev_name); - .op = VM_SNAPSHOT_RESTORE, - }; + if (size == 0) { + warnx("data with zero size for %s", info->dev_name); + return (1); + } + meta = &(struct vm_snapshot_meta) { + .ctx = ctx, + .dev_name = info->dev_name, + .dev_req = info->req, - ret = vm_snapshot_req(meta); - if (ret != 0) { - fprintf(stderr, "%s: Failed to restore struct: %s\r\n", - __func__, info->struct_name); - goto done; - } + .buffer.buf_start = data, + .buffer.buf_size = size, -done: - return (ret); -} + .buffer.buf = data, + .buffer.buf_rem = size, -int -vm_restore_kern_structs(struct vmctx *ctx, struct restore_state *rstate) -{ - size_t i; - int ret; + .op = VM_SNAPSHOT_RESTORE, + }; - for (i = 0; i < nitems(snapshot_kern_structs); i++) { - ret = vm_restore_kern_struct(ctx, rstate, - &snapshot_kern_structs[i]); - if (ret != 0) + ret = vm_snapshot_req(meta); + if (ret != 0) { + warnx("%s: Failed to restore %s", __func__, + info->dev_name); return (ret); + } } - return (0); + return (ret); } -static int -vm_restore_user_dev(struct vmctx *ctx, struct restore_state *rstate, - const struct vm_snapshot_dev_info *info) +static void +vm_restore_device(struct vmctx *ctx, struct restore_state *rstate, + struct snapshot_dev *dev) { void *dev_ptr; size_t dev_size; int ret; struct vm_snapshot_meta *meta; - dev_ptr = lookup_dev(info->dev_name, rstate, &dev_size); - if (dev_ptr == NULL) { - fprintf(stderr, "Failed to lookup dev: %s\r\n", info->dev_name); - fprintf(stderr, "Continuing the restore/migration process\r\n"); - return (0); - } + dev_ptr = lookup_dev(dev->dev_name, JSON_DEV_ARR_KEY, rstate, &dev_size); + if (dev_ptr == NULL) + err(EX_DATAERR, "Failed to lookup dev: %s\r\n", dev->dev_name); - if (dev_size == 0) { - fprintf(stderr, "%s: Device size is 0. " - "Assuming %s is not used\r\n", - __func__, info->dev_name); - return (0); - } + if (dev_size == 0) + err(EX_DATAERR, "%s: device size is 0: %s\n", __func__, + dev->dev_name); meta = &(struct vm_snapshot_meta) { .ctx = ctx, - .dev_name = info->dev_name, + .dev_name = dev->dev_name, .buffer.buf_start = dev_ptr, .buffer.buf_size = dev_size, @@ -962,74 +903,62 @@ .op = VM_SNAPSHOT_RESTORE, }; - ret = (*info->snapshot_cb)(meta); - if (ret != 0) { - fprintf(stderr, "Failed to restore dev: %s\r\n", - info->dev_name); - return (-1); - } - - return (0); + ret = (dev->dev_ops->snapshot_cb)(meta, dev->dev_cbdata); + if (ret != 0) + err(EX_DATAERR, "Failed to restore dev: %s", dev->dev_name); } int -vm_restore_user_devs(struct vmctx *ctx, struct restore_state *rstate) +vm_restore_devices(struct vmctx *ctx, struct restore_state *rstate) { - size_t i; - int ret; + struct snapshot_dev *dev; - for (i = 0; i < nitems(snapshot_devs); i++) { - ret = vm_restore_user_dev(ctx, rstate, &snapshot_devs[i]); - if (ret != 0) - return (ret); + LIST_FOREACH(dev, &snapshot_devices, dev_link) { + vm_restore_device(ctx, rstate, dev); } return 0; } int -vm_pause_user_devs(void) +vm_pause_devices() { - const struct vm_snapshot_dev_info *info; - size_t i; - int ret; + struct snapshot_dev *dev; + int err; - for (i = 0; i < nitems(snapshot_devs); i++) { - info = &snapshot_devs[i]; - if (info->pause_cb == NULL) + LIST_FOREACH(dev, &snapshot_devices, dev_link) { + if (!dev->dev_ops->pause_cb) continue; - ret = info->pause_cb(info->dev_name); - if (ret != 0) - return (ret); + err = dev->dev_ops->pause_cb(dev->dev_cbdata); + if (err != 0) + return (err); } return (0); } int -vm_resume_user_devs(void) +vm_resume_devices() { - const struct vm_snapshot_dev_info *info; - size_t i; - int ret; + struct snapshot_dev *dev; + int err; - for (i = 0; i < nitems(snapshot_devs); i++) { - info = &snapshot_devs[i]; - if (info->resume_cb == NULL) + LIST_FOREACH(dev, &snapshot_devices, dev_link) { + if (!dev->dev_ops->resume_cb) continue; - ret = info->resume_cb(info->dev_name); - if (ret != 0) - return (ret); + err = dev->dev_ops->resume_cb(dev->dev_cbdata); + if (err != 0) + return (err); } return (0); } static int -vm_snapshot_kern_struct(int data_fd, xo_handle_t *xop, const char *array_key, +vm_save_kern_struct(int data_fd, xo_handle_t *xop, const char *array_key, struct vm_snapshot_meta *meta, off_t *offset) { int ret; @@ -1056,12 +985,10 @@ /* Write metadata. */ xo_open_instance_h(xop, array_key); - xo_emit_h(xop, "{:debug_name/%s}\n", meta->dev_name); - xo_emit_h(xop, "{:" JSON_SNAPSHOT_REQ_KEY "/%d}\n", - meta->dev_req); + xo_emit_h(xop, "{:" JSON_SNAPSHOT_REQ_KEY "/%s}\n", meta->dev_name); xo_emit_h(xop, "{:" JSON_SIZE_KEY "/%lu}\n", data_size); xo_emit_h(xop, "{:" JSON_FILE_OFFSET_KEY "/%lu}\n", *offset); - xo_close_instance_h(xop, JSON_STRUCT_ARR_KEY); + xo_close_instance_h(xop, JSON_KERNEL_ARR_KEY); *offset += data_size; @@ -1070,7 +997,7 @@ } static int -vm_snapshot_kern_structs(struct vmctx *ctx, int data_fd, xo_handle_t *xop) +vm_save_kern_structs(struct vmctx *ctx, int data_fd, xo_handle_t *xop) { int ret, error; size_t buf_size, i, offset; @@ -1097,23 +1024,23 @@ .op = VM_SNAPSHOT_SAVE, }; - xo_open_list_h(xop, JSON_STRUCT_ARR_KEY); + xo_open_list_h(xop, JSON_KERNEL_ARR_KEY); for (i = 0; i < nitems(snapshot_kern_structs); i++) { - meta->dev_name = snapshot_kern_structs[i].struct_name; + meta->dev_name = snapshot_kern_structs[i].dev_name; meta->dev_req = snapshot_kern_structs[i].req; memset(meta->buffer.buf_start, 0, meta->buffer.buf_size); meta->buffer.buf = meta->buffer.buf_start; meta->buffer.buf_rem = meta->buffer.buf_size; - ret = vm_snapshot_kern_struct(data_fd, xop, JSON_DEV_ARR_KEY, + ret = vm_save_kern_struct(data_fd, xop, JSON_DEV_ARR_KEY, meta, &offset); if (ret != 0) { error = -1; goto err_vm_snapshot_kern_data; } } - xo_close_list_h(xop, JSON_STRUCT_ARR_KEY); + xo_close_list_h(xop, JSON_KERNEL_ARR_KEY); err_vm_snapshot_kern_data: if (buffer != NULL) @@ -1122,7 +1049,7 @@ } static int -vm_snapshot_basic_metadata(struct vmctx *ctx, xo_handle_t *xop, size_t memsz) +vm_save_basic_metadata(struct vmctx *ctx, xo_handle_t *xop, size_t memsz) { xo_open_container_h(xop, JSON_BASIC_METADATA_KEY); @@ -1164,16 +1091,20 @@ } static int -vm_snapshot_user_dev(const struct vm_snapshot_dev_info *info, - int data_fd, xo_handle_t *xop, - struct vm_snapshot_meta *meta, off_t *offset) +vm_save_device(struct snapshot_dev *dev, + int data_fd, xo_handle_t *xop, + struct vm_snapshot_meta *meta, off_t *offset) { int ret; - ret = (*info->snapshot_cb)(meta); + meta->dev_name = dev->dev_name; + memset(meta->buffer.buf_start, 0, meta->buffer.buf_size); + meta->buffer.buf = meta->buffer.buf_start; + meta->buffer.buf_rem = meta->buffer.buf_size; + + ret = (dev->dev_ops->snapshot_cb)(meta, dev->dev_cbdata); if (ret != 0) { - fprintf(stderr, "Failed to snapshot %s; ret=%d\r\n", - meta->dev_name, ret); + warnx("Failed to snapshot %s: ret=%d", meta->dev_name, ret); return (ret); } @@ -1186,13 +1117,14 @@ } static int -vm_snapshot_user_devs(struct vmctx *ctx, int data_fd, xo_handle_t *xop) +vm_save_devices(struct vmctx *ctx, int data_fd, xo_handle_t *xop) { int ret; off_t offset; void *buffer; - size_t buf_size, i; + size_t buf_size; struct vm_snapshot_meta *meta; + struct snapshot_dev *dev; buf_size = SNAPSHOT_BUFFER_SIZE; @@ -1221,15 +1153,8 @@ xo_open_list_h(xop, JSON_DEV_ARR_KEY); /* Restore other devices that support this feature */ - for (i = 0; i < nitems(snapshot_devs); i++) { - meta->dev_name = snapshot_devs[i].dev_name; - - memset(meta->buffer.buf_start, 0, meta->buffer.buf_size); - meta->buffer.buf = meta->buffer.buf_start; - meta->buffer.buf_rem = meta->buffer.buf_size; - - ret = vm_snapshot_user_dev(&snapshot_devs[i], data_fd, xop, - meta, &offset); + LIST_FOREACH(dev, &snapshot_devices, dev_link) { + ret = vm_save_device(dev, data_fd, xop, meta, &offset); if (ret != 0) goto snapshot_err; } @@ -1366,7 +1291,7 @@ vm_vcpu_pause(ctx); - ret = vm_pause_user_devs(); + ret = vm_pause_devices(); if (ret != 0) { fprintf(stderr, "Could not pause devices\r\n"); error = ret; @@ -1380,22 +1305,21 @@ goto done; } - ret = vm_snapshot_basic_metadata(ctx, xop, memsz); + ret = vm_save_basic_metadata(ctx, xop, memsz); if (ret != 0) { fprintf(stderr, "Failed to snapshot vm basic metadata.\n"); error = -1; goto done; } - - ret = vm_snapshot_kern_structs(ctx, kdata_fd, xop); + ret = vm_save_kern_structs(ctx, kdata_fd, xop); if (ret != 0) { fprintf(stderr, "Failed to snapshot vm kernel data.\n"); error = -1; goto done; } - ret = vm_snapshot_user_devs(ctx, kdata_fd, xop); + ret = vm_save_devices(ctx, kdata_fd, xop); if (ret != 0) { fprintf(stderr, "Failed to snapshot device state.\n"); error = -1; @@ -1410,7 +1334,7 @@ } done: - ret = vm_resume_user_devs(); + ret = vm_resume_devices(); if (ret != 0) fprintf(stderr, "Could not resume devices\r\n"); vm_vcpu_resume(ctx);