[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [Xen-changelog] [xen-unstable] libxenlight: fix suspend/resume
# HG changeset patch # User Keir Fraser <keir.fraser@xxxxxxxxxx> # Date 1259578419 0 # Node ID 093cd0e7d537860df31cb85da5400b437fe7cb7e # Parent 6864bc03783fed55abdef5f6b34e2efd57cf47d0 libxenlight: fix suspend/resume Signed-off-by: Stefano Stabellini <stefano.stabellini@xxxxxxxxxxxxx> --- tools/libxl/libxl.c | 89 ++++++++++++++++----- tools/libxl/libxl.h | 6 + tools/libxl/libxl_dom.c | 176 +++++++++++++++++++++---------------------- tools/libxl/libxl_internal.h | 1 tools/libxl/xl.c | 143 +++++++++++++++++++++++++++++++++- 5 files changed, 294 insertions(+), 121 deletions(-) diff -r 6864bc03783f -r 093cd0e7d537 tools/libxl/libxl.c --- a/tools/libxl/libxl.c Mon Nov 30 10:47:36 2009 +0000 +++ b/tools/libxl/libxl.c Mon Nov 30 10:53:39 2009 +0000 @@ -217,29 +217,36 @@ int libxl_domain_build(struct libxl_ctx } int libxl_domain_restore(struct libxl_ctx *ctx, libxl_domain_build_info *info, - uint32_t domid, int fd) -{ - libxl_domain_build_state state; + uint32_t domid, int fd, libxl_domain_build_state *state, + libxl_device_model_info *dm_info) +{ char **vments = NULL, **localents = NULL; - memset(&state, '\0', sizeof(state)); - - build_pre(ctx, domid, info, &state); - restore_common(ctx, domid, info, &state, fd); + build_pre(ctx, domid, info, state); + restore_common(ctx, domid, info, state, fd); if (info->hvm) { - vments = libxl_calloc(ctx, 4, sizeof(char *)); + vments = libxl_calloc(ctx, 5, sizeof(char *)); vments[0] = "rtc/timeoffset"; vments[1] = (info->u.hvm.timeoffset) ? info->u.hvm.timeoffset : ""; + vments[2] = "image/ostype"; + vments[3] = "hvm"; } else { - localents = libxl_calloc(ctx, 4 * 2, sizeof(char *)); - localents[0] = "serial/0/limit"; - localents[1] = libxl_sprintf(ctx, "%d", 65536); - localents[2] = "console/port"; - localents[3] = libxl_sprintf(ctx, "%d", state.console_port); - localents[4] = "console/ring-ref"; - localents[5] = libxl_sprintf(ctx, "%ld", state.console_mfn); - } - build_post(ctx, domid, info, &state, vments, localents); + vments = libxl_calloc(ctx, 9, sizeof(char *)); + vments[0] = "image/ostype"; + vments[1] = "linux"; + vments[2] = "image/kernel"; + vments[3] = (char*) info->kernel; + vments[4] = "image/ramdisk"; + vments[5] = (char*) info->u.pv.ramdisk; + vments[6] = "image/cmdline"; + vments[7] = (char*) info->u.pv.cmdline; + } + build_post(ctx, domid, info, state, vments, localents); + if (info->hvm) + asprintf(&(dm_info->saved_state), "/var/lib/xen/qemu-save.%d", domid); + else + dm_info->saved_state = NULL; + return 0; } @@ -299,17 +306,37 @@ xc_dominfo_t * libxl_domain_infolist(str return info; } +static int libxl_save_device_model(struct libxl_ctx *ctx, uint32_t domid, int fd) +{ + int fd2, c; + char buf[1024]; + char *filename = libxl_sprintf(ctx, "/var/lib/xen/qemu-save.%d", domid); + + XL_LOG(ctx, XL_LOG_DEBUG, "Saving device model state to %s", filename); + libxl_xs_write(ctx, XBT_NULL, libxl_sprintf(ctx, "/local/domain/0/device-model/%d/command", domid), "save", strlen("save")); + libxl_wait_for_device_model(ctx, domid, "paused", NULL, NULL); + + write(fd, QEMU_SIGNATURE, strlen(QEMU_SIGNATURE)); + fd2 = open(filename, O_RDONLY); + while ((c = read(fd2, buf, sizeof(buf))) != 0) { + write(fd, buf, c); + } + close(fd2); + unlink(filename); + return 0; +} + int libxl_domain_suspend(struct libxl_ctx *ctx, libxl_domain_suspend_info *info, uint32_t domid, int fd) { - int hvm = 1; - int live = 0; - int debug = 0; - char savesig[] = "XenSavedDomain\n"; - - write(fd, savesig, strlen(savesig)); + int hvm = is_hvm(ctx, domid); + int live = info != NULL && info->flags & XL_SUSPEND_LIVE; + int debug = info != NULL && info->flags & XL_SUSPEND_LIVE; + core_suspend(ctx, domid, fd, hvm, live, debug); + if (hvm) + libxl_save_device_model(ctx, domid, fd); return 0; } @@ -322,7 +349,19 @@ int libxl_domain_pause(struct libxl_ctx int libxl_domain_unpause(struct libxl_ctx *ctx, uint32_t domid) { + char path[50]; + char *state; + + if (is_hvm(ctx, domid)) { + snprintf(path, sizeof(path), "/local/domain/0/device-model/%d/state", domid); + state = libxl_xs_read(ctx, XBT_NULL, path); + if (state != NULL && !strcmp(state, "paused")) { + libxl_xs_write(ctx, XBT_NULL, libxl_sprintf(ctx, "/local/domain/0/device-model/%d/command", domid), "continue", strlen("continue")); + libxl_wait_for_device_model(ctx, domid, "running", NULL, NULL); + } + } xc_domain_unpause(ctx->xch, domid); + return 0; } @@ -578,6 +617,10 @@ static char ** libxl_build_device_model_ vifs[i].devid, vifs[i].ifname, vifs[i].bridge)); } } + } + if (info->saved_state) { + flexarray_set(dm_args, num++, "-loadvm"); + flexarray_set(dm_args, num++, info->saved_state); } for (i = 0; info->extra && info->extra[i] != NULL; i++) flexarray_set(dm_args, num++, info->extra[i]); diff -r 6864bc03783f -r 093cd0e7d537 tools/libxl/libxl.h --- a/tools/libxl/libxl.h Mon Nov 30 10:47:36 2009 +0000 +++ b/tools/libxl/libxl.h Mon Nov 30 10:53:39 2009 +0000 @@ -94,6 +94,8 @@ typedef struct { } libxl_domain_build_state; typedef struct { +#define XL_SUSPEND_DEBUG 1 +#define XL_SUSPEND_LIVE 2 int flags; int (*suspend_callback)(void *, int); } libxl_domain_suspend_info; @@ -107,6 +109,7 @@ typedef struct { int domid; char *dom_name; char *device_model; + char *saved_state; libxl_qemu_machine_type type; int videoram; /* size of the videoram in MB */ bool stdvga; /* stdvga enabled or disabled */ @@ -254,7 +257,8 @@ int libxl_domain_make(struct libxl_ctx * int libxl_domain_make(struct libxl_ctx *ctx, libxl_domain_create_info *info, uint32_t *domid); int libxl_domain_build(struct libxl_ctx *ctx, libxl_domain_build_info *info, uint32_t domid, /* out */ libxl_domain_build_state *state); int libxl_domain_restore(struct libxl_ctx *ctx, libxl_domain_build_info *info, - uint32_t domid, int fd); + uint32_t domid, int fd, libxl_domain_build_state *state, + libxl_device_model_info *dm_info); int libxl_domain_suspend(struct libxl_ctx *ctx, libxl_domain_suspend_info *info, uint32_t domid, int fd); int libxl_domain_shutdown(struct libxl_ctx *ctx, uint32_t domid, int req); diff -r 6864bc03783f -r 093cd0e7d537 tools/libxl/libxl_dom.c --- a/tools/libxl/libxl_dom.c Mon Nov 30 10:47:36 2009 +0000 +++ b/tools/libxl/libxl_dom.c Mon Nov 30 10:53:39 2009 +0000 @@ -163,71 +163,36 @@ int restore_common(struct libxl_ctx *ctx state->store_port, &state->store_mfn, state->console_port, &state->console_mfn, info->hvm, info->u.hvm.pae, 0); - return 0; -} - -/* the following code is extremely ugly and racy without forking. - we intend to fix the re-entrancy of the underlying code instead of forking */ -static struct libxl_ctx *global_suspend_ctx = NULL; -static struct suspendinfo { - int xch; +#if defined(__i386__) || defined(__x86_64__) + xc_cpuid_apply_policy(ctx->xch, domid); +#endif + return 0; +} + +struct suspendinfo { + struct libxl_ctx *ctx; int xce; /* event channel handle */ int suspend_eventchn; int domid; int hvm; unsigned int flags; -} si; - -void core_suspend_switch_qemu_logdirty(int domid, unsigned int enable) -{ - struct xs_handle *xs; - char *path, *ret_path, *cmd_path, *ret_str, *cmd_str, **watch; - unsigned int len; - struct timeval tv; - fd_set fdset; - struct libxl_ctx *ctx = global_suspend_ctx; - - xs = xs_daemon_open(); - if (!xs) - return; - path = libxl_sprintf(ctx, "/local/domain/0/device-model/%i/logdirty", domid); - if (!path) - return; - ret_path = libxl_sprintf(ctx, "%s/ret", path); - if (!ret_path) - return; - cmd_path = libxl_sprintf(ctx, "%s/cmd", path); - if (!ret_path) - return; - - /* Watch for qemu's return value */ - if (!xs_watch(xs, ret_path, "qemu-logdirty-ret")) - return; - - cmd_str = (enable == 0) ? "disable" : "enable"; - - /* Tell qemu that we want it to start logging dirty page to Xen */ - if (!xs_write(xs, XBT_NULL, cmd_path, cmd_str, strlen(cmd_str))) - return; - - /* Wait a while for qemu to signal that it has service logdirty command */ -read_again: - tv.tv_sec = 5; - tv.tv_usec = 0; - FD_ZERO(&fdset); - FD_SET(xs_fileno(xs), &fdset); - - if ((select(xs_fileno(xs) + 1, &fdset, NULL, NULL, &tv)) != 1) - return; - - watch = xs_read_watch(xs, &len); - free(watch); - - ret_str = xs_read(xs, XBT_NULL, ret_path, &len); - if (ret_str == NULL || strcmp(ret_str, cmd_str)) - /* Watch fired but value is not yet right */ - goto read_again; - free(ret_str); +}; + +static void core_suspend_switch_qemu_logdirty(int domid, unsigned int enable) +{ + struct xs_handle *xsh; + char path[64]; + + snprintf(path, sizeof(path), "/local/domain/0/device-model/%u/logdirty/cmd", domid); + + xsh = xs_daemon_open(); + + if (enable) + xs_write(xsh, XBT_NULL, path, "enable", strlen("enable")); + else + xs_write(xsh, XBT_NULL, path, "disable", strlen("disable")); + + xs_daemon_close(xsh); } static int core_suspend_callback(void *data) @@ -235,46 +200,77 @@ static int core_suspend_callback(void *d struct suspendinfo *si = data; unsigned long s_state = 0; int ret; + char *path, *state = "suspend"; + int watchdog = 60; if (si->hvm) - xc_get_hvm_param(si->xch, si->domid, HVM_PARAM_ACPI_S_STATE, &s_state); + xc_get_hvm_param(si->ctx->xch, si->domid, HVM_PARAM_ACPI_S_STATE, &s_state); if ((s_state == 0) && (si->suspend_eventchn >= 0)) { - ret = xc_evtchn_notify(si->xch, si->suspend_eventchn); + ret = xc_evtchn_notify(si->xce, si->suspend_eventchn); if (ret < 0) { + XL_LOG(si->ctx, XL_LOG_ERROR, "xc_evtchn_notify failed ret=%d", ret); return 0; } - ret = xc_await_suspend(si->xch, si->suspend_eventchn); + ret = xc_await_suspend(si->xce, si->suspend_eventchn); if (ret < 0) { + XL_LOG(si->ctx, XL_LOG_ERROR, "xc_await_suspend failed ret=%d", ret); return 0; } return 1; } - /* need to shutdown (to suspend) the domain here */ - return 0; -} - -static struct save_callbacks callbacks; + path = libxl_sprintf(si->ctx, "%s/control/shutdown", libxl_xs_get_dompath(si->ctx, si->domid)); + libxl_xs_write(si->ctx, XBT_NULL, path, "suspend", strlen("suspend")); + if (si->hvm) { + unsigned long hvm_pvdrv, hvm_s_state; + xc_get_hvm_param(si->ctx->xch, si->domid, HVM_PARAM_CALLBACK_IRQ, &hvm_pvdrv); + xc_get_hvm_param(si->ctx->xch, si->domid, HVM_PARAM_ACPI_S_STATE, &hvm_s_state); + if (!hvm_pvdrv || hvm_s_state) { + XL_LOG(si->ctx, XL_LOG_DEBUG, "Calling xc_domain_shutdown on the domain"); + xc_domain_shutdown(si->ctx->xch, si->domid, SHUTDOWN_suspend); + } + } + XL_LOG(si->ctx, XL_LOG_DEBUG, "wait for the guest to suspend"); + while (!strcmp(state, "suspend") && watchdog > 0) { + int nb_domain, i; + xc_dominfo_t *list = NULL; + usleep(100000); + list = libxl_domain_infolist(si->ctx, &nb_domain); + for (i = 0; i < nb_domain; i++) { + if (si->domid == list[i].domid) { + if (list[i].shutdown != 0 && list[i].shutdown_reason == SHUTDOWN_suspend) { + free(list); + return 1; + } + } + } + free(list); + state = libxl_xs_read(si->ctx, XBT_NULL, path); + watchdog--; + } + if (!strcmp(state, "suspend")) { + XL_LOG(si->ctx, XL_LOG_ERROR, "guest didn't suspend in time"); + libxl_xs_write(si->ctx, XBT_NULL, path, "", 1); + } + return 1; +} int core_suspend(struct libxl_ctx *ctx, uint32_t domid, int fd, int hvm, int live, int debug) { int flags; int port; + struct save_callbacks callbacks; + struct suspendinfo si; flags = (live) ? XCFLAGS_LIVE : 0 - | (debug) ? XCFLAGS_DEBUG : 0; - - /* crappy global lock until we make everything clean */ - while (global_suspend_ctx) { - sleep(1); - } - global_suspend_ctx = ctx; + | (debug) ? XCFLAGS_DEBUG : 0 + | (hvm) ? XCFLAGS_HVM : 0; si.domid = domid; si.flags = flags; si.hvm = hvm; - si.suspend_eventchn = si.xce = -1; - si.xch = ctx->xch; + si.ctx = ctx; + si.suspend_eventchn = -1; si.xce = xc_evtchn_open(); if (si.xce < 0) @@ -284,28 +280,28 @@ int core_suspend(struct libxl_ctx *ctx, port = xs_suspend_evtchn_port(si.domid); if (port < 0) { + XL_LOG(ctx, XL_LOG_WARNING, "Failed to get the suspend evtchn port"); } else { - si.suspend_eventchn = xc_suspend_evtchn_init(si.xch, si.xce, si.domid, port); - - if (si.suspend_eventchn < 0) { - } - } - } - + si.suspend_eventchn = xc_suspend_evtchn_init(si.ctx->xch, si.xce, si.domid, port); + + if (si.suspend_eventchn < 0) + XL_LOG(ctx, XL_LOG_WARNING, "Suspend event channel initialization failed"); + } + } + + memset(&callbacks, 0, sizeof(callbacks)); callbacks.suspend = core_suspend_callback; - callbacks.postcopy = NULL; - callbacks.checkpoint = NULL; callbacks.data = &si; xc_domain_save(ctx->xch, fd, domid, 0, 0, flags, &callbacks, hvm, - core_suspend_switch_qemu_logdirty); + &core_suspend_switch_qemu_logdirty); if (si.suspend_eventchn > 0) xc_suspend_evtchn_release(si.xce, si.suspend_eventchn); if (si.xce > 0) xc_evtchn_close(si.xce); - global_suspend_ctx = NULL; - return 0; -} + return 0; +} + diff -r 6864bc03783f -r 093cd0e7d537 tools/libxl/libxl_internal.h --- a/tools/libxl/libxl_internal.h Mon Nov 30 10:47:36 2009 +0000 +++ b/tools/libxl/libxl_internal.h Mon Nov 30 10:53:39 2009 +0000 @@ -30,6 +30,7 @@ #define LIBXL_DESTROY_TIMEOUT 10 #define LIBXL_XENCONSOLE_LIMIT 1048576 #define LIBXL_XENCONSOLE_PROTOCOL "vt100" +#define QEMU_SIGNATURE "QemuDeviceModelRecord" #define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0])) diff -r 6864bc03783f -r 093cd0e7d537 tools/libxl/xl.c --- a/tools/libxl/xl.c Mon Nov 30 10:47:36 2009 +0000 +++ b/tools/libxl/xl.c Mon Nov 30 10:53:39 2009 +0000 @@ -31,6 +31,7 @@ #include <sys/select.h> #include <arpa/inet.h> #include <xenctrl.h> + #include "libxl.h" #include "libxl_utils.h" @@ -575,7 +576,7 @@ skip_pci: } \ }) -static void create_domain(int debug, const char *filename) +static void create_domain(int debug, const char *config_file, const char *restore_file, int paused) { struct libxl_ctx ctx; uint32_t domid; @@ -593,9 +594,10 @@ static void create_domain(int debug, con int i, fd; int need_daemon = 1; libxl_device_model_starting *dm_starting = 0; - - printf("Parsing config file %s\n", filename); - parse_config_file(filename, &info1, &info2, &disks, &num_disks, &vifs, &num_vifs, &pcidevs, &num_pcidevs, &vfbs, &num_vfbs, &vkbs, &num_vkbs, &dm_info); + memset(&dm_info, 0x00, sizeof(dm_info)); + + printf("Parsing config file %s\n", config_file); + parse_config_file(config_file, &info1, &info2, &disks, &num_disks, &vifs, &num_vifs, &pcidevs, &num_pcidevs, &vfbs, &num_vfbs, &vkbs, &num_vkbs, &dm_info); if (debug) printf_info(&info1, &info2, disks, num_disks, vifs, num_vifs, pcidevs, num_pcidevs, vfbs, num_vfbs, vkbs, num_vkbs, &dm_info); @@ -605,7 +607,20 @@ start: libxl_ctx_init(&ctx); libxl_ctx_set_log(&ctx, log_callback, NULL); libxl_domain_make(&ctx, &info1, &domid); - libxl_domain_build(&ctx, &info2, domid, &state); + + if (!restore_file || !need_daemon) { + if (dm_info.saved_state) { + free(dm_info.saved_state); + dm_info.saved_state = NULL; + } + libxl_domain_build(&ctx, &info2, domid, &state); + } else { + int restore_fd; + + restore_fd = open(restore_file, O_RDONLY); + libxl_domain_restore(&ctx, &info2, domid, restore_fd, &state, &dm_info); + close(restore_fd); + } for (i = 0; i < num_disks; i++) { disk_info_domid_fixup(disks + i, domid); @@ -640,7 +655,8 @@ start: for (i = 0; i < num_pcidevs; i++) libxl_device_pci_add(&ctx, domid, &pcidevs[i]); - libxl_domain_unpause(&ctx, domid); + if (!paused) + libxl_domain_unpause(&ctx, domid); if (need_daemon) { char *fullname, *name; @@ -712,6 +728,8 @@ static void help(char *command) printf(" pause pause execution of a domain\n\n"); printf(" unpause unpause a paused domain\n\n"); printf(" console attach to domain's console\n\n"); + printf(" save save a domain state to restore later\n\n"); + printf(" restore restore a domain from a saved state\n\n"); } else if(!strcmp(command, "create")) { printf("Usage: xl create <ConfigFile> [options] [vars]\n\n"); printf("Create a domain based on <ConfigFile>.\n\n"); @@ -736,6 +754,18 @@ static void help(char *command) } else if(!strcmp(command, "unpause")) { printf("Usage: xl unpause <Domain>\n\n"); printf("Unpause a paused domain.\n\n"); + } else if(!strcmp(command, "save")) { + printf("Usage: xl save [options] <Domain> <CheckpointFile>\n\n"); + printf("Save a domain state to restore later.\n\n"); + printf("Options:\n\n"); + printf("-h Print this help.\n"); + printf("-c Leave domain running after creating the snapshot.\n"); + } else if(!strcmp(command, "restore")) { + printf("Usage: xl restore [options] <ConfigFile> <CheckpointFile>\n\n"); + printf("Restore a domain from a saved state.\n\n"); + printf("Options:\n\n"); + printf("-h Print this help.\n"); + printf("-p Do not unpause domain after restoring it.\n"); } else if(!strcmp(command, "destroy")) { printf("Usage: xl destroy <Domain>\n\n"); printf("Terminate a domain immediately.\n\n"); @@ -1015,6 +1045,101 @@ void list_domains(void) free(info); } +int save_domain(char *p, char *filename, int checkpoint) +{ + struct libxl_ctx ctx; + uint32_t domid; + int fd; + + libxl_ctx_init(&ctx); + libxl_ctx_set_log(&ctx, log_callback, NULL); + + if (libxl_param_to_domid(&ctx, p, &domid) < 0) { + fprintf(stderr, "%s is an invalid domain identifier\n", p); + exit(2); + } + fd = open(filename, O_WRONLY|O_CREAT|O_TRUNC, 0644); + if (fd < 0) { + fprintf(stderr, "Failed to open temp file %s for writing\n", filename); + exit(2); + } + libxl_domain_suspend(&ctx, NULL, domid, fd); + close(fd); + + if (checkpoint) + libxl_domain_unpause(&ctx, domid); + else + libxl_domain_destroy(&ctx, domid, 0); + + exit(0); +} + +int main_restore(int argc, char **argv) +{ + char *checkpoint_file = NULL; + char *config_file = NULL; + int paused = 0, debug = 0; + int opt; + + while ((opt = getopt(argc, argv, "hpd")) != -1) { + switch (opt) { + case 'p': + paused = 1; + break; + case 'd': + debug = 1; + break; + case 'h': + help("restore"); + exit(0); + default: + fprintf(stderr, "option not supported\n"); + break; + } + } + + if (optind >= argc - 1) { + help("restore"); + exit(2); + } + + config_file = argv[optind]; + checkpoint_file = argv[optind + 1]; + create_domain(debug, config_file, checkpoint_file, paused); + exit(0); +} + +int main_save(int argc, char **argv) +{ + char *filename = NULL, *p = NULL; + int checkpoint = 0; + int opt; + + while ((opt = getopt(argc, argv, "hc")) != -1) { + switch (opt) { + case 'c': + checkpoint = 1; + break; + case 'h': + help("save"); + exit(0); + default: + fprintf(stderr, "option not supported\n"); + break; + } + } + + if (optind >= argc - 1) { + help("save"); + exit(2); + } + + p = argv[optind]; + filename = argv[optind + 1]; + save_domain(p, filename, checkpoint); + exit(0); +} + int main_pause(int argc, char **argv) { int opt; @@ -1140,7 +1265,7 @@ int main_create(int argc, char **argv) } filename = argv[optind]; - create_domain(debug, filename); + create_domain(debug, filename, NULL, 0); exit(0); } @@ -1169,6 +1294,10 @@ int main(int argc, char **argv) main_unpause(argc - 1, argv + 1); } else if (!strcmp(argv[1], "console")) { main_console(argc - 1, argv + 1); + } else if (!strcmp(argv[1], "save")) { + main_save(argc - 1, argv + 1); + } else if (!strcmp(argv[1], "restore")) { + main_restore(argc - 1, argv + 1); } else if (!strcmp(argv[1], "help")) { if (argc > 2) help(argv[2]); _______________________________________________ Xen-changelog mailing list Xen-changelog@xxxxxxxxxxxxxxxxxxx http://lists.xensource.com/xen-changelog
|
Lists.xenproject.org is hosted with RackSpace, monitoring our |