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 |