Changeset View
Changeset View
Standalone View
Standalone View
usr.sbin/bhyve/migration.c
| Show All 29 Lines | |||||
| #include <stdlib.h> | #include <stdlib.h> | ||||
| #include <string.h> | #include <string.h> | ||||
| #include <vmmapi.h> | #include <vmmapi.h> | ||||
| #include "migration.h" | #include "migration.h" | ||||
| #include "pci_emul.h" | #include "pci_emul.h" | ||||
| #include "snapshot.h" | #include "snapshot.h" | ||||
| #define MB (1024UL * 1024) | |||||
| #define GB (1024UL * MB) | |||||
| #ifdef BHYVE_DEBUG | #ifdef BHYVE_DEBUG | ||||
| #define DPRINTF(FMT, ...) \ | #define DPRINTF(FMT, ...) \ | ||||
| ({ \ | ({ \ | ||||
| fprintf(stderr, "%s: " FMT "\n", __func__, ##__VA_ARGS__); \ | fprintf(stderr, "%s: " FMT "\n", __func__, ##__VA_ARGS__); \ | ||||
| }) | }) | ||||
| #else | #else | ||||
| #define DPRINTF(FMT, ...) | #define DPRINTF(FMT, ...) | ||||
| ▲ Show 20 Lines • Show All 258 Lines • ▼ Show 20 Lines | default: | ||||
| EPRINTF("Unknown address family."); | EPRINTF("Unknown address family."); | ||||
| error = EINVAL; | error = EINVAL; | ||||
| } | } | ||||
| freeaddrinfo(res); | freeaddrinfo(res); | ||||
| return (error); | return (error); | ||||
| } | } | ||||
| static int | |||||
| migrate_check_memsize(size_t local_lowmem_size, size_t local_highmem_size, | |||||
| size_t remote_lowmem_size, size_t remote_highmem_size) | |||||
| { | |||||
| int rc; | |||||
| rc = MIGRATION_SPECS_OK; | |||||
| if (local_lowmem_size != remote_lowmem_size){ | |||||
| rc = MIGRATION_SPECS_NOT_OK; | |||||
| EPRINTF("Local and remote lowmem size mismatch"); | |||||
| } | |||||
| if (local_highmem_size != remote_highmem_size){ | |||||
| rc = MIGRATION_SPECS_NOT_OK; | |||||
| EPRINTF("Local and remote highmem size mismatch"); | |||||
| } | |||||
| return (rc); | |||||
| } | |||||
| static int | |||||
| migrate_recv_memory(struct vmctx *ctx, int socket) | |||||
| { | |||||
| size_t local_lowmem_size, local_highmem_size; | |||||
| size_t remote_lowmem_size, remote_highmem_size; | |||||
| char *baseaddr; | |||||
| int memsize_ok; | |||||
| int rc; | |||||
| local_lowmem_size = local_highmem_size = 0; | |||||
| remote_lowmem_size = remote_highmem_size = 0; | |||||
| rc = 0; | |||||
| rc = vm_get_guestmem_from_ctx(ctx, | |||||
| &baseaddr, &local_lowmem_size, | |||||
| &local_highmem_size); | |||||
| if (rc != 0) { | |||||
| EPRINTF("Could not get guest lowmem size and highmem size"); | |||||
| return (rc); | |||||
| } | |||||
| rc = migration_transfer_data(socket, &remote_lowmem_size, sizeof(remote_lowmem_size), MIGRATION_RECV_REQ); | |||||
| if (rc != 0) { | |||||
| EPRINTF("Could not recv lowmem size"); | |||||
| return (rc); | |||||
| } | |||||
| rc = migration_transfer_data(socket, &remote_highmem_size, sizeof(remote_highmem_size), MIGRATION_RECV_REQ); | |||||
| if (rc != 0) { | |||||
| EPRINTF("Could not recv highmem size"); | |||||
| return (rc); | |||||
| } | |||||
| memsize_ok = migrate_check_memsize(local_lowmem_size, local_highmem_size, | |||||
| remote_lowmem_size, remote_highmem_size); | |||||
| rc = migration_transfer_data(socket, | |||||
| &memsize_ok, sizeof(memsize_ok), MIGRATION_SEND_REQ); | |||||
| if (rc != 0) { | |||||
| EPRINTF("Could not send migration_ok to remote"); | |||||
| return (rc); | |||||
| } | |||||
| if (memsize_ok != MIGRATION_SPECS_OK) { | |||||
| EPRINTF("Memory size mismatch with remote host"); | |||||
| return (EINVAL); | |||||
| } | |||||
| rc = migration_transfer_data(socket, baseaddr, local_lowmem_size, MIGRATION_RECV_REQ); | |||||
| if (rc != 0) { | |||||
| EPRINTF("Could not recv chunk lowmem."); | |||||
| return (rc); | |||||
| } | |||||
| if (local_highmem_size > 0) { | |||||
| rc = migration_transfer_data(socket, baseaddr + 4 * GB, local_highmem_size, MIGRATION_RECV_REQ); | |||||
| if (rc != 0) { | |||||
| EPRINTF("Could not recv highmem"); | |||||
| return (rc); | |||||
| } | |||||
| } | |||||
| return (0); | |||||
| } | |||||
| static int | |||||
| migrate_send_memory(struct vmctx *ctx, int socket) | |||||
| { | |||||
| size_t lowmem_size, highmem_size; | |||||
| char *mmap_vm_lowmem, *mmap_vm_highmem; | |||||
| char *baseaddr; | |||||
| int memsize_ok; | |||||
| int rc; | |||||
| rc = 0; | |||||
| mmap_vm_lowmem = MAP_FAILED; | |||||
| mmap_vm_highmem = MAP_FAILED; | |||||
| rc = vm_get_guestmem_from_ctx(ctx, &baseaddr, | |||||
| &lowmem_size, &highmem_size); | |||||
| if (rc != 0) { | |||||
| EPRINTF("Could not get guest lowmem size and highmem size"); | |||||
| return (rc); | |||||
| } | |||||
| /* Send the size of the lowmem segment */ | |||||
| rc = migration_transfer_data(socket, &lowmem_size, sizeof(lowmem_size), MIGRATION_SEND_REQ); | |||||
| if (rc != 0) { | |||||
| EPRINTF("Could not send lowmem size"); | |||||
| return (rc); | |||||
| } | |||||
| /* Send the size of the highmem segment */ | |||||
| rc = migration_transfer_data(socket, &highmem_size, sizeof(lowmem_size), MIGRATION_SEND_REQ); | |||||
| if (rc != 0) { | |||||
| EPRINTF("Could not send highmem size"); | |||||
| return (rc); | |||||
| } | |||||
| /* Wait for answer - params ok (if memory size matches) */ | |||||
| rc = migration_transfer_data(socket, &memsize_ok, sizeof(memsize_ok), MIGRATION_RECV_REQ); | |||||
| if (rc != 0) { | |||||
| EPRINTF("Could not receive response from remote"); | |||||
| return (rc); | |||||
| } | |||||
| if (memsize_ok != MIGRATION_SPECS_OK) { | |||||
| EPRINTF("Memory size mismatch with remote host"); | |||||
| return (EINVAL); | |||||
| } | |||||
| mmap_vm_lowmem = baseaddr; | |||||
| mmap_vm_highmem = baseaddr + 4 * GB; | |||||
| /* Send the lowmem segment */ | |||||
| rc = migration_transfer_data(socket, mmap_vm_lowmem, lowmem_size, MIGRATION_SEND_REQ); | |||||
| if (rc != 0) { | |||||
| EPRINTF("Could not send lowmem"); | |||||
| return (rc); | |||||
| } | |||||
| /* Send the highmem segment */ | |||||
| if (highmem_size > 0) { | |||||
| rc = migration_transfer_data(socket, mmap_vm_highmem, highmem_size, MIGRATION_SEND_REQ); | |||||
| if (rc != 0) { | |||||
| EPRINTF("Could not send highmem"); | |||||
| return (rc); | |||||
| } | |||||
| } | |||||
| return (0); | |||||
| } | |||||
| static inline int | static inline int | ||||
| migrate_connections(struct migrate_req req, int *socket_fd, | migrate_connections(struct migrate_req req, int *socket_fd, | ||||
| enum migration_transfer_req type) | enum migration_transfer_req type) | ||||
| { | { | ||||
| int error; | int error; | ||||
| int s, con_socket; | int s, con_socket; | ||||
| struct sockaddr_in sa, client_sa; | struct sockaddr_in sa, client_sa; | ||||
| struct in_addr req_addr; | struct in_addr req_addr; | ||||
| ▲ Show 20 Lines • Show All 115 Lines • ▼ Show 20 Lines | #endif | ||||
| rc = migration_transfer_data(s, &is_live, sizeof(is_live), MIGRATION_SEND_REQ); | rc = migration_transfer_data(s, &is_live, sizeof(is_live), MIGRATION_SEND_REQ); | ||||
| if (rc != 0) { | if (rc != 0) { | ||||
| EPRINTF("Could not send migration type"); | EPRINTF("Could not send migration type"); | ||||
| error = rc; | error = rc; | ||||
| goto done; | goto done; | ||||
| } | } | ||||
| if (is_live) { | |||||
| EPRINTF("Live migration not implemented"); | |||||
| rc = EOPNOTSUPP; | |||||
| if (rc != 0) | |||||
| EPRINTF("Could not live migrate the guest's memory"); | |||||
| error = rc; | |||||
| goto done; | |||||
| } /* else continue the warm migration procedure */ | |||||
| vm_vcpu_pause(ctx); | vm_vcpu_pause(ctx); | ||||
| rc = vm_pause_user_devs(); | rc = vm_pause_user_devs(); | ||||
| if (rc != 0) { | if (rc != 0) { | ||||
| EPRINTF("Could not pause devices"); | EPRINTF("Could not pause devices"); | ||||
| error = rc; | error = rc; | ||||
| goto unlock_vm_and_exit; | goto unlock_vm_and_exit; | ||||
| } | } | ||||
| rc = migrate_send_memory(ctx, s); | |||||
| if (rc != 0) { | |||||
| EPRINTF("Could not send memory to destination"); | |||||
| error = rc; | |||||
| goto unlock_vm_and_exit; | |||||
| } | |||||
| rc = migration_transfer_data(s, &migration_completed, | rc = migration_transfer_data(s, &migration_completed, | ||||
| sizeof(migration_completed), MIGRATION_RECV_REQ); | sizeof(migration_completed), MIGRATION_RECV_REQ); | ||||
| if ((rc != 0) || (migration_completed != MIGRATION_SPECS_OK)) { | if ((rc != 0) || (migration_completed != MIGRATION_SPECS_OK)) { | ||||
| EPRINTF("Could not recv 'migration completed' from remote or received error"); | EPRINTF("Could not recv 'migration completed' from remote or received error"); | ||||
| error = rc != 0 ? rc : EINVAL; | error = rc != 0 ? rc : EINVAL; | ||||
| goto unlock_vm_and_exit; | goto unlock_vm_and_exit; | ||||
| } | } | ||||
| Show All 11 Lines | unlock_vm_and_exit: | ||||
| vm_vcpu_resume(ctx); | vm_vcpu_resume(ctx); | ||||
| done: | done: | ||||
| close(s); | close(s); | ||||
| return (error); | return (error); | ||||
| } | } | ||||
| static int | static int | ||||
| vm_recv_migrate_req(struct vmctx __unused *ctx, struct migrate_req req) | vm_recv_migrate_req(struct vmctx *ctx, struct migrate_req req) | ||||
| { | { | ||||
| int s; | int s; | ||||
| int rc = 0; | int rc = 0; | ||||
| bool is_live; | bool is_live; | ||||
| size_t migration_completed; | size_t migration_completed; | ||||
| rc = migrate_connections(req, &s, MIGRATION_RECV_REQ); | rc = migrate_connections(req, &s, MIGRATION_RECV_REQ); | ||||
| if (rc != 0) { | if (rc != 0) { | ||||
| EPRINTF("Could not create connections"); | EPRINTF("Could not create connections"); | ||||
| return (rc); | return (rc); | ||||
| } | } | ||||
| rc = migration_check_specs(s, MIGRATION_RECV_REQ); | rc = migration_check_specs(s, MIGRATION_RECV_REQ); | ||||
| if (rc != 0) { | if (rc != 0) { | ||||
| EPRINTF("Error while checking specs"); | EPRINTF("Error while checking specs"); | ||||
| goto done; | goto done; | ||||
| } | } | ||||
| rc = migration_transfer_data(s, &is_live, sizeof(is_live), MIGRATION_RECV_REQ); | rc = migration_transfer_data(s, &is_live, sizeof(is_live), MIGRATION_RECV_REQ); | ||||
| if (rc != 0) { | if (rc != 0) { | ||||
| EPRINTF("Could not recv migration type"); | EPRINTF("Could not recv migration type"); | ||||
| goto done; | goto done; | ||||
| } | |||||
| /* For recv, the only difference between warm and live migration is the | |||||
| * way in which the memory is migrated. | |||||
| */ | |||||
| if (is_live) { | |||||
| EPRINTF("Live migration not implemented"); | |||||
| rc = EOPNOTSUPP; | |||||
| if (rc != 0) { | |||||
| EPRINTF("Could not live migrate the guest's memory"); | |||||
| goto done; | |||||
| } | |||||
| } else { | |||||
| /* if not live migration, then migrate memory normally. */ | |||||
| rc = migrate_recv_memory(ctx, s); | |||||
| if (rc != 0) { | |||||
| EPRINTF("Could not recv lowmem and highmem"); | |||||
| goto done; | |||||
| } | |||||
| } | } | ||||
| // fprintf(stdout, "%s: Migration completed\n", __func__); | // fprintf(stdout, "%s: Migration completed\n", __func__); | ||||
| migration_completed = MIGRATION_SPECS_OK; | migration_completed = MIGRATION_SPECS_OK; | ||||
| rc = migration_transfer_data(s, &migration_completed, | rc = migration_transfer_data(s, &migration_completed, | ||||
| sizeof(migration_completed), MIGRATION_SEND_REQ); | sizeof(migration_completed), MIGRATION_SEND_REQ); | ||||
| if (rc != 0) { | if (rc != 0) { | ||||
| ▲ Show 20 Lines • Show All 74 Lines • Show Last 20 Lines | |||||