diff --git a/usr.sbin/bhyve/migration.h b/usr.sbin/bhyve/migration.h --- a/usr.sbin/bhyve/migration.h +++ b/usr.sbin/bhyve/migration.h @@ -35,7 +35,6 @@ #ifndef _BHYVE_MIGRATION_ #define _BHYVE_MIGRATION_ -#include #include #include #include "snapshot.h" @@ -82,7 +81,7 @@ size_t hw_pagesize; }; -int vm_send_migrate_req(struct vmctx *ctx, struct migrate_req req); +int vm_send_migrate_req(struct vmctx *ctx, struct migrate_req req, bool live); int vm_recv_migrate_req(struct vmctx *ctx, struct migrate_req req); #endif 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 @@ -909,10 +909,10 @@ } int -vm_send_migrate_req(struct vmctx *ctx, struct migrate_req req) +vm_send_migrate_req(struct vmctx *ctx, struct migrate_req req, bool live) { int s; - int rc, error; + int rc, error, is_live_migration; size_t migration_completed; rc = migrate_connections(req, &s, NULL, MIGRATION_SEND_REQ); @@ -929,6 +929,24 @@ goto done; } + is_live_migration = live; + rc = migration_transfer_data(s, &is_live_migration, + sizeof(is_live_migration), MIGRATION_SEND_REQ); + if (rc < 0) { + DPRINTF("Could not send migration type"); + return (-1); + } + + if (live) { + EPRINTF("Live migration not yet implemented"); + rc = -1; + 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); rc = vm_pause_user_devs(ctx); @@ -986,6 +1004,7 @@ { int s, con_socket; int rc; + int is_live_migration; size_t migration_completed; rc = migrate_connections(req, &s, &con_socket, MIGRATION_RECV_REQ); @@ -1002,14 +1021,36 @@ return (rc); } - rc = migrate_recv_memory(ctx, con_socket); + rc = migration_transfer_data(con_socket, &is_live_migration, + sizeof(is_live_migration), MIGRATION_RECV_REQ); if (rc < 0) { - EPRINTF("Could not recv :lowmem and highmem"); - close(con_socket); - close(s); + EPRINTF("Could not recv migration type"); return (-1); } + /* For recv, the only difference between warm and live migration is the + * way in which the memory is migrated. + */ + if (is_live_migration) { + EPRINTF("Live migration not yet implemented"); + rc = -1; + if (rc != 0) { + EPRINTF("Could not live migrate the guest's memory"); + close(con_socket); + close(s); + return (rc); + } + } else { + /* if not live migration, then migrate memory normally. */ + rc = migrate_recv_memory(ctx, con_socket); + if (rc < 0) { + EPRINTF("Could not recv lowmem and highmem"); + close(con_socket); + close(s); + return (-1); + } + } + rc = migrate_kern_data(ctx, con_socket, MIGRATION_RECV_REQ); if (rc < 0) { EPRINTF("Could not recv kern data"); 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 @@ -1465,7 +1465,7 @@ static int handle_message(struct vmctx *ctx, nvlist_t *nvl) { - int err; + int err, memflags; const char *cmd; struct migrate_req req; @@ -1487,15 +1487,46 @@ } else { memset(&req, 0, sizeof(struct migrate_req)); req.port = nvlist_get_number(nvl, "port"); - strncpy(req.host, nvlist_get_string(nvl, "hostname"), MAX_HOSTNAME_LEN - 1); - req.host[MAX_HOSTNAME_LEN - 1] = 0; + strlcpy(req.host, nvlist_get_string(nvl, "hostname"), MAX_HOSTNAME_LEN); fprintf(stderr, "%s: IP address used for migration: %s;\r\n" "Port used for migration: %d\r\n", __func__, req.host, req.port); - err = vm_send_migrate_req(ctx, req); + err = vm_send_migrate_req(ctx, req, false); + } + } else if (strcmp(cmd, "migrate_live") == 0) { + if (!nvlist_exists_string(nvl, "hostname") || + !nvlist_exists_number(nvl, "port")) { + err = -1; + } else { + fprintf(stdout, "Starting the live migration procedure\r\n"); + + /* Currently, the live migration is implemented only + * for guests that are started using -S (wired + * memory option). + */ + + /* Check memflags. If the VM_MEM_F_WIRED bit is not + * set, then the live migration procedure cannot be + * done. */ + memflags = vm_get_memflags(ctx); + if (!(memflags & VM_MEM_F_WIRED)) { + fprintf(stderr, "%s: Migration not supported for un-wired guests\r\n", __func__); + err = -1; + } else { + memset(&req, 0, sizeof(struct migrate_req)); + req.port = nvlist_get_number(nvl, "port"); + strlcpy(req.host, nvlist_get_string(nvl, "hostname"), MAX_HOSTNAME_LEN); + fprintf(stderr, "%s: IP address used for migration: %s;\r\n" + "Port used for migration: %d\r\n", + __func__, + req.host, + req.port); + + err = vm_send_migrate_req(ctx, req, true); + } } } else { EPRINTLN("Unrecognized checkpoint operation\n"); diff --git a/usr.sbin/bhyvectl/bhyvectl.c b/usr.sbin/bhyvectl/bhyvectl.c --- a/usr.sbin/bhyvectl/bhyvectl.c +++ b/usr.sbin/bhyvectl/bhyvectl.c @@ -91,6 +91,7 @@ " [--checkpoint=]\n" " [--suspend=]\n" " [--migrate=,]\n" + " [--migrate-live=,]\n" #endif " [--get-all]\n" " [--get-stats]\n" @@ -305,6 +306,7 @@ static int vm_checkpoint_opt; static int vm_suspend_opt; static int vm_migrate; +static int vm_migrate_live; #endif /* @@ -597,6 +599,7 @@ SET_CHECKPOINT_FILE, SET_SUSPEND_FILE, MIGRATE_VM, + MIGRATE_VM_LIVE, #endif }; @@ -1470,6 +1473,7 @@ { "checkpoint", REQ_ARG, 0, SET_CHECKPOINT_FILE}, { "suspend", REQ_ARG, 0, SET_SUSPEND_FILE}, { "migrate", REQ_ARG, 0, MIGRATE_VM}, + { "migrate-live", REQ_ARG, 0, MIGRATE_VM_LIVE}, #endif }; @@ -1742,7 +1746,7 @@ } static int -send_start_migrate(struct vmctx *ctx, const char *migrate_vm) +send_start_migrate(struct vmctx *ctx, const char *migrate_vm, bool live) { nvlist_t *nvl; char *hostname, *pos; @@ -1768,7 +1772,10 @@ } nvl = nvlist_create(0); - nvlist_add_string(nvl, "cmd", "migrate"); + if (live) + nvlist_add_string(nvl, "cmd", "migrate_live"); + else + nvlist_add_string(nvl, "cmd", "migrate"); nvlist_add_string(nvl, "hostname", hostname); nvlist_add_number(nvl, "port", port); @@ -1779,7 +1786,6 @@ #endif /* BHYVE_SNAPSHOT */ - int main(int argc, char *argv[]) { @@ -1970,6 +1976,10 @@ vm_migrate = 1; migrate_host = optarg; break; + case MIGRATE_VM_LIVE: + vm_migrate_live = 1; + migrate_host = optarg; + break; #endif default: usage(cpu_intel); @@ -2450,7 +2460,10 @@ error = snapshot_request(ctx, suspend_file, true); if (!error && vm_migrate) - error = send_start_migrate(ctx, migrate_host); + error = send_start_migrate(ctx, migrate_host, false); + + if (!error && vm_migrate_live) + error = send_start_migrate(ctx, migrate_host, true); #endif free (opts);