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 @@ -633,6 +633,179 @@ 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(struct vmctx *ctx, 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)) { + DPRINTF("Unknown transfer request option"); + return (-1); + } + + 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(ctx, dev, 0, buffer, len, VM_SNAPSHOT_SAVE); + + 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; + + rc = (*dev_info->snapshot_cb)(meta); + if (rc < 0) { + DPRINTF("Could not get info about %s dev", dev); + return (-1); + } + + 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) { + DPRINTF("Could not transfer msg for %s dev", dev); + return (-1); + } + + if (req == MIGRATION_RECV_REQ) { + if (msg.type != MESSAGE_TYPE_DEV) { + DPRINTF("Wrong message type for device."); + return (-1); + } + + data_size = msg.len; + } + + if (data_size == 0) + return (0); // this type of device is not used + + + rc = migration_transfer_data(socket, buffer, data_size, req); + if (rc < 0) { + DPRINTF("Could not transfer %s dev", dev); + return (-1); + } + + 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(ctx, msg.name, 0, buffer, data_size, VM_SNAPSHOT_RESTORE); + meta->buffer.buf = meta->buffer.buf_start; + meta->buffer.buf_rem = meta->buffer.buf_size; + + rc = (*dev_info->snapshot_cb)(meta); + if (rc != 0) { + EPRINTF("Could not restore %s dev", msg.name); + return (-1); + } + } + + return (0); +} + +static int +migrate_devs(struct vmctx *ctx, int socket, enum migration_transfer_req req) +{ + int i, num_items; + int rc, error; + char *buffer; + const struct vm_snapshot_dev_info *snapshot_devs; + + error = 0; + buffer = malloc(SNAPSHOT_BUFFER_SIZE); + if (buffer == NULL) { + EPRINTF("Could not allocate memory"); + error = -1; + goto end; + } + + if (req == MIGRATION_SEND_REQ) { + /* + * Send to the destination the number of devices that will + * be migrated. + */ + snapshot_devs = get_snapshot_devs(&num_items); + + rc = migration_transfer_data(socket, &num_items, sizeof(num_items), req); + if (rc < 0) { + DPRINTF("Could not send num_items to destination"); + return (-1); + } + + for (i = 0; i < num_items; i++) { + rc = migrate_transfer_dev(ctx, socket, snapshot_devs[i].dev_name, + buffer, SNAPSHOT_BUFFER_SIZE, req); + + if (rc < 0) { + DPRINTF("Could not send %s", snapshot_devs[i].dev_name); + error = -1; + goto end; + } + } + } else if (req == MIGRATION_RECV_REQ) { + /* receive the number of devices that will be migrated */ + rc = migration_transfer_data(socket, &num_items, sizeof(num_items), MIGRATION_RECV_REQ); + if (rc < 0) { + DPRINTF("Could not recv num_items from source"); + return (-1); + } + + for (i = 0; i < num_items; i++) { + rc = migrate_transfer_dev(ctx, socket, NULL, buffer, SNAPSHOT_BUFFER_SIZE, req); + if (rc < 0) { + DPRINTF("Could not recv device"); + error = -1; + goto end; + } + } + } + +end: + if (buffer != NULL) + free(buffer); + + return (error); +} + static inline int migrate_connections(struct migrate_req req, int *socket_fd, int *connection_socket_fd, @@ -785,6 +958,13 @@ goto unlock_vm_and_exit; } + rc = migrate_devs(ctx, 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)) { @@ -793,10 +973,6 @@ goto unlock_vm_and_exit; } - EPRINTF("Migration not yet implemented"); - error = -1; - goto unlock_vm_and_exit; - vm_destroy(ctx); exit(0); @@ -848,6 +1024,14 @@ return (-1); } + rc = migrate_devs(ctx, con_socket, MIGRATION_RECV_REQ); + if (rc < 0) { + EPRINTF("Could not recv pci devs"); + close(con_socket); + close(s); + return (-1); + } + fprintf(stdout, "%s: Migration completed\r\n", __func__); migration_completed = MIGRATION_SPECS_OK; rc = migration_transfer_data(con_socket, &migration_completed, @@ -861,7 +1045,5 @@ close(con_socket); close(s); - - EPRINTF("Migration not currently implemented"); - return (-1); + return (0); }