mirror of
https://github.com/xen-project/xen.git
synced 2026-10-29 10:40:03 +01:00
If several migrations log their output to xend.log its not clear which line belongs to a which guest. Print entry/exit of xc_save and xc_restore and also request to print pid with each log call. Signed-off-by: Olaf Hering <olaf@aepfle.de> Acked-by: Ian Campbell <ian.campbell@citrix.com> Committed-by: Ian Campbell <ian.campbell@citrix.com>
228 lines
6.3 KiB
C
228 lines
6.3 KiB
C
/*
|
|
* This file is subject to the terms and conditions of the GNU General
|
|
* Public License. See the file "COPYING" in the main directory of
|
|
* this archive for more details.
|
|
*
|
|
* Copyright (C) 2005 by Christian Limpach
|
|
*
|
|
*/
|
|
|
|
#include <err.h>
|
|
#include <stdlib.h>
|
|
#include <stdint.h>
|
|
#include <string.h>
|
|
#include <stdio.h>
|
|
#include <sys/ipc.h>
|
|
#include <sys/shm.h>
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <fcntl.h>
|
|
#include <err.h>
|
|
|
|
#include <xenstore.h>
|
|
#include <xenctrl.h>
|
|
#include <xenguest.h>
|
|
|
|
static struct suspendinfo {
|
|
xc_interface *xch;
|
|
xc_evtchn *xce; /* event channel handle */
|
|
int suspend_evtchn;
|
|
int domid;
|
|
unsigned int flags;
|
|
} si;
|
|
|
|
/**
|
|
* Issue a suspend request through stdout, and receive the acknowledgement
|
|
* from stdin. This is handled by XendCheckpoint in the Python layer.
|
|
*/
|
|
static int compat_suspend(void)
|
|
{
|
|
char ans[30];
|
|
|
|
printf("suspend\n");
|
|
fflush(stdout);
|
|
|
|
return (fgets(ans, sizeof(ans), stdin) != NULL &&
|
|
!strncmp(ans, "done\n", 5));
|
|
}
|
|
|
|
/**
|
|
* Issue a suspend request to a dedicated event channel in the guest, and
|
|
* receive the acknowledgement from the subscribe event channel. */
|
|
static int evtchn_suspend(void)
|
|
{
|
|
int rc;
|
|
|
|
rc = xc_evtchn_notify(si.xce, si.suspend_evtchn);
|
|
if (rc < 0) {
|
|
warnx("failed to notify suspend request channel: %d", rc);
|
|
return 0;
|
|
}
|
|
|
|
if (xc_await_suspend(si.xch, si.xce, si.suspend_evtchn) < 0) {
|
|
warnx("suspend failed");
|
|
return 0;
|
|
}
|
|
|
|
/* notify xend that it can do device migration */
|
|
printf("suspended\n");
|
|
fflush(stdout);
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int suspend(void* data)
|
|
{
|
|
unsigned long sx_state = 0;
|
|
|
|
/* Cannot notify guest to shut itself down if it's in ACPI sleep state. */
|
|
if (si.flags & XCFLAGS_HVM)
|
|
xc_get_hvm_param(si.xch, si.domid,
|
|
HVM_PARAM_ACPI_S_STATE, &sx_state);
|
|
|
|
if ((sx_state == 0) && (si.suspend_evtchn >= 0))
|
|
return evtchn_suspend();
|
|
|
|
return compat_suspend();
|
|
}
|
|
|
|
/* For HVM guests, there are two sources of dirty pages: the Xen shadow
|
|
* log-dirty bitmap, which we get with a hypercall, and qemu's version.
|
|
* The protocol for getting page-dirtying data from qemu uses a
|
|
* double-buffered shared memory interface directly between xc_save and
|
|
* qemu-dm.
|
|
*
|
|
* xc_save calculates the size of the bitmaps and notifies qemu-dm
|
|
* through the store that it wants to share the bitmaps. qemu-dm then
|
|
* starts filling in the 'active' buffer.
|
|
*
|
|
* To change the buffers over, xc_save writes the other buffer number to
|
|
* the store and waits for qemu to acknowledge that it is now writing to
|
|
* the new active buffer. xc_save can then process and clear the old
|
|
* active buffer. */
|
|
|
|
|
|
static int switch_qemu_logdirty(int domid, unsigned int enable, void *data)
|
|
{
|
|
struct xs_handle *xs;
|
|
char *path, *p, *ret_str, *cmd_str, **watch;
|
|
unsigned int len;
|
|
struct timeval tv;
|
|
fd_set fdset;
|
|
|
|
if ((xs = xs_daemon_open()) == NULL)
|
|
errx(1, "Couldn't contact xenstore");
|
|
if (!(path = strdup("/local/domain/0/device-model/")))
|
|
errx(1, "can't get domain path in store");
|
|
if (!(path = realloc(path, strlen(path)
|
|
+ 10
|
|
+ strlen("/logdirty/cmd") + 1)))
|
|
errx(1, "no memory for constructing xenstore path");
|
|
snprintf(path + strlen(path), 11, "%i", domid);
|
|
strcat(path, "/logdirty/");
|
|
p = path + strlen(path);
|
|
|
|
|
|
/* Watch for qemu's return value */
|
|
strcpy(p, "ret");
|
|
if (!xs_watch(xs, path, "qemu-logdirty-ret"))
|
|
errx(1, "can't set watch in store (%s)\n", path);
|
|
|
|
if (!(cmd_str = strdup( enable == 0 ? "disable" : "enable")))
|
|
errx(1, "can't get logdirty cmd path in store");
|
|
|
|
/* Tell qemu that we want it to start logging dirty page to Xen */
|
|
strcpy(p, "cmd");
|
|
if (!xs_write(xs, XBT_NULL, path, cmd_str, strlen(cmd_str)))
|
|
errx(1, "can't write to store path (%s)\n",
|
|
path);
|
|
|
|
/* 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)
|
|
errx(1, "timed out waiting for qemu logdirty response.\n");
|
|
|
|
watch = xs_read_watch(xs, &len);
|
|
free(watch);
|
|
|
|
strcpy(p, "ret");
|
|
ret_str = xs_read(xs, XBT_NULL, path, &len);
|
|
if (ret_str == NULL || strcmp(ret_str, cmd_str))
|
|
/* Watch fired but value is not yet right */
|
|
goto read_again;
|
|
|
|
free(path);
|
|
free(cmd_str);
|
|
free(ret_str);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
main(int argc, char **argv)
|
|
{
|
|
unsigned int maxit, max_f, lflags;
|
|
int io_fd, ret, port;
|
|
struct save_callbacks callbacks;
|
|
xentoollog_level lvl;
|
|
xentoollog_logger *l;
|
|
|
|
if (argc != 6)
|
|
errx(1, "usage: %s iofd domid maxit maxf flags", argv[0]);
|
|
|
|
io_fd = atoi(argv[1]);
|
|
si.domid = atoi(argv[2]);
|
|
maxit = atoi(argv[3]);
|
|
max_f = atoi(argv[4]);
|
|
si.flags = atoi(argv[5]);
|
|
|
|
si.suspend_evtchn = -1;
|
|
|
|
lvl = si.flags & XCFLAGS_DEBUG ? XTL_DEBUG: XTL_DETAIL;
|
|
lflags = XTL_STDIOSTREAM_SHOW_PID | XTL_STDIOSTREAM_HIDE_PROGRESS;
|
|
l = (xentoollog_logger *)xtl_createlogger_stdiostream(stderr, lvl, lflags);
|
|
si.xch = xc_interface_open(l, 0, 0);
|
|
if (!si.xch)
|
|
errx(1, "failed to open control interface");
|
|
|
|
si.xce = xc_evtchn_open(NULL, 0);
|
|
if (si.xce == NULL)
|
|
warnx("failed to open event channel handle");
|
|
else
|
|
{
|
|
port = xs_suspend_evtchn_port(si.domid);
|
|
|
|
if (port < 0)
|
|
warnx("failed to get the suspend evtchn port\n");
|
|
else
|
|
{
|
|
si.suspend_evtchn =
|
|
xc_suspend_evtchn_init(si.xch, si.xce, si.domid, port);
|
|
|
|
if (si.suspend_evtchn < 0)
|
|
warnx("suspend event channel initialization failed, "
|
|
"using slow path");
|
|
}
|
|
}
|
|
memset(&callbacks, 0, sizeof(callbacks));
|
|
callbacks.suspend = suspend;
|
|
callbacks.switch_qemu_logdirty = switch_qemu_logdirty;
|
|
ret = xc_domain_save(si.xch, io_fd, si.domid, maxit, max_f, si.flags,
|
|
&callbacks, !!(si.flags & XCFLAGS_HVM), 0);
|
|
|
|
if (si.suspend_evtchn > 0)
|
|
xc_suspend_evtchn_release(si.xch, si.xce, si.domid, si.suspend_evtchn);
|
|
|
|
if (si.xce > 0)
|
|
xc_evtchn_close(si.xce);
|
|
|
|
xc_interface_close(si.xch);
|
|
|
|
return ret;
|
|
}
|