diff --git a/usr.sbin/bhyve/migration.c b/usr.sbin/bhyve/migration.c --- a/usr.sbin/bhyve/migration.c +++ b/usr.sbin/bhyve/migration.c @@ -588,6 +588,160 @@ return (error); } +static inline const struct vm_snapshot_dev_info * +find_entry_for_dev(const char *name) +{ + int i; + int ndevs; + const struct vm_snapshot_dev_info *snapshot_devs; + + snapshot_devs = get_snapshot_devs(&ndevs); + + for (i = 0; i < ndevs; i++) { + if (strncmp(name, snapshot_devs[i].dev_name, MAX_DEV_NAME_LEN) == 0) { + return (&snapshot_devs[i]); + } + } + + return (NULL); +} + +static inline int +migrate_transfer_dev(int socket, const char *dev, + char *buffer, size_t len, enum migration_transfer_req req) +{ + int rc; + size_t data_size; + struct migration_message_type msg; + struct vm_snapshot_meta *meta; + const struct vm_snapshot_dev_info *dev_info; + + if ((req != MIGRATION_SEND_REQ) && (req != MIGRATION_RECV_REQ)) { + EPRINTF("Unknown transfer request option"); + return (EINVAL); + } + + memset(&msg, 0, sizeof(msg)); + memset(buffer, 0, len); + if (req == MIGRATION_SEND_REQ) { + dev_info = find_entry_for_dev(dev); + if (dev_info == NULL) { + EPRINTF("Could not find the device %s " + "or migration not implemented yet for it.", dev); + return (0); + } + + meta = ALLOCA_VM_SNAPSHOT_META(dev, 0, buffer, len, VM_SNAPSHOT_SAVE); + + rc = (*dev_info->snapshot_cb)(meta); + if (rc != 0) { + EPRINTF("Could not get info about %s dev", dev); + return (rc); + } + + data_size = vm_get_snapshot_size(meta); + + msg.type = MESSAGE_TYPE_DEV; + msg.len = data_size; + strlcpy(msg.name, dev, MAX_DEV_NAME_LEN); + } + + rc = migration_transfer_data(socket, &msg, sizeof(msg), req); + if (rc != 0) { + EPRINTF("Could not transfer msg for %s dev", dev); + return (rc); + } + + if (req == MIGRATION_RECV_REQ) { + if (msg.type != MESSAGE_TYPE_DEV) { + EPRINTF("Wrong message type for device."); + return (EINVAL); + } + + data_size = msg.len; + } + + /* This type of device is not used */ + if (data_size == 0) + return (0); + + + rc = migration_transfer_data(socket, buffer, data_size, req); + if (rc != 0) { + EPRINTF("Could not transfer %s dev", dev); + return (rc); + } + + if (req == MIGRATION_RECV_REQ) { + dev_info = find_entry_for_dev(msg.name); + if (dev_info == NULL) { + EPRINTF("Could not find the device %s " + "or migration not implemented yet for it.", msg.name); + return (0); + } + + meta = ALLOCA_VM_SNAPSHOT_META(msg.name, 0, buffer, data_size, VM_SNAPSHOT_RESTORE); + + rc = (*dev_info->snapshot_cb)(meta); + if (rc != 0) { + EPRINTF("Could not restore %s dev", msg.name); + return (rc); + } + } + + return (0); +} + +static int +migrate_devs(int socket, enum migration_transfer_req req) +{ + int i, num_items; + int error; + char *buffer; + const char *dev_name = NULL; + const struct vm_snapshot_dev_info *snapshot_devs; + + error = 0; + buffer = malloc(SNAPSHOT_BUFFER_SIZE); + if (buffer == NULL) { + EPRINTF("Could not allocate memory"); + error = ENOMEM; + goto end; + } + + /* + * Send to the destination the number of devices that will + * be migrated. + */ + if (req == MIGRATION_SEND_REQ) + snapshot_devs = get_snapshot_devs(&num_items); + + error = migration_transfer_data(socket, &num_items, sizeof(num_items), req); + if (error != 0) { + EPRINTF("Could not transfer number of devices"); + goto end; + } + + for (i = 0; i < num_items; i++) { + if (req == MIGRATION_SEND_REQ) + dev_name = snapshot_devs[i].dev_name; + + error = migrate_transfer_dev(socket, dev_name, + buffer, SNAPSHOT_BUFFER_SIZE, req); + + if (error != 0) { + DPRINTF("Could not transfer dev %s", snapshot_devs[i].dev_name); + goto end; + } + } + +end: + if (buffer != NULL) + free(buffer); + + return (error); +} + static inline int migrate_connections(struct migrate_req req, int *socket_fd, enum migration_transfer_req type) @@ -751,6 +905,13 @@ goto unlock_vm_and_exit; } + rc = migrate_devs(s, MIGRATION_SEND_REQ); + if (rc != 0) { + EPRINTF("Could not send pci devs to destination"); + error = rc; + goto unlock_vm_and_exit; + } + rc = migration_transfer_data(s, &migration_completed, sizeof(migration_completed), MIGRATION_RECV_REQ); if ((rc != 0) || (migration_completed != MIGRATION_SPECS_OK)) { @@ -759,10 +920,6 @@ goto unlock_vm_and_exit; } - EPRINTF("Rest of migration not yet implemented"); - error = EOPNOTSUPP; - goto unlock_vm_and_exit; - vm_destroy(ctx); exit(0); @@ -828,7 +985,13 @@ goto done; } - // fprintf(stdout, "%s: Migration completed\n", __func__); + rc = migrate_devs(s, MIGRATION_RECV_REQ); + if (rc != 0) { + EPRINTF("Could not recv pci devs"); + goto done; + } + + fprintf(stdout, "%s: Migration completed\n", __func__); migration_completed = MIGRATION_SPECS_OK; rc = migration_transfer_data(s, &migration_completed, @@ -840,8 +1003,6 @@ done: close(s); - EPRINTF("Rest of migration not currently implemented"); - rc = EOPNOTSUPP; return (rc); } 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 @@ -83,6 +83,7 @@ enum snapshot_req req; /* request type */ }; +const struct vm_snapshot_dev_info *get_snapshot_devs(int *ndevs); const struct vm_snapshot_kern_info *get_snapshot_kern_structs(int *ndevs); void destroy_restore_state(struct restore_state *rstate); 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 @@ -163,6 +163,15 @@ { "vrtc", STRUCT_VRTC }, }; +const struct vm_snapshot_dev_info * +get_snapshot_devs(int *ndevs) +{ + if (ndevs != NULL) + *ndevs = nitems(snapshot_devs); + + return (snapshot_devs); +} + const struct vm_snapshot_kern_info * get_snapshot_kern_structs(int *ndevs) { diff --git a/usr.sbin/bhyve/virtio.c b/usr.sbin/bhyve/virtio.c --- a/usr.sbin/bhyve/virtio.c +++ b/usr.sbin/bhyve/virtio.c @@ -869,7 +869,6 @@ SNAPSHOT_VAR_CMP_OR_LEAVE(vc->vc_nvq, meta, ret, done); SNAPSHOT_VAR_CMP_OR_LEAVE(vc->vc_cfgsize, meta, ret, done); - SNAPSHOT_VAR_CMP_OR_LEAVE(vc->vc_hv_caps, meta, ret, done); done: return (ret);