ia64/xen-unstable

changeset 17966:2922c423a1aa

Teach xc_save to use event-channel-based domain suspend if available.
If the guest provides a suspend event channel through xenstore,
xc_save will use it in preference to the old xenstore-based method.

Xend is still informed when the domain has suspended so that it can
perform device migration in parallel with last-round migration.

Signed-off-by: Brendan Cully <brendan@cs.ubc.ca>
author Keir Fraser <keir.fraser@citrix.com>
date Fri Jul 04 12:00:38 2008 +0100 (2008-07-04)
parents 14fd83fe71c3
children d711529e3de1
files tools/python/xen/xend/XendCheckpoint.py tools/xcutils/xc_save.c
line diff
     1.1 --- a/tools/python/xen/xend/XendCheckpoint.py	Fri Jul 04 12:00:24 2008 +0100
     1.2 +++ b/tools/python/xen/xend/XendCheckpoint.py	Fri Jul 04 12:00:38 2008 +0100
     1.3 @@ -108,6 +108,7 @@ def save(fd, dominfo, network, live, dst
     1.4                  log.debug("Suspending %d ...", dominfo.getDomid())
     1.5                  dominfo.shutdown('suspend')
     1.6                  dominfo.waitForShutdown()
     1.7 +            if line in ('suspend', 'suspended'):
     1.8                  dominfo.migrateDevices(network, dst, DEV_MIGRATE_STEP2,
     1.9                                         domain_name)
    1.10                  log.info("Domain %d suspended.", dominfo.getDomid())
    1.11 @@ -116,6 +117,7 @@ def save(fd, dominfo, network, live, dst
    1.12                  if hvm:
    1.13                      dominfo.image.saveDeviceModel()
    1.14  
    1.15 +            if line == "suspend":
    1.16                  tochild.write("done\n")
    1.17                  tochild.flush()
    1.18                  log.debug('Written done')
     2.1 --- a/tools/xcutils/xc_save.c	Fri Jul 04 12:00:24 2008 +0100
     2.2 +++ b/tools/xcutils/xc_save.c	Fri Jul 04 12:00:38 2008 +0100
     2.3 @@ -23,11 +23,18 @@
     2.4  #include <xenctrl.h>
     2.5  #include <xenguest.h>
     2.6  
     2.7 +static struct suspendinfo {
     2.8 +    int xce; /* event channel handle */
     2.9 +
    2.10 +    int suspend_evtchn;
    2.11 +    int suspended_evtchn;
    2.12 +} si;
    2.13 +
    2.14  /**
    2.15   * Issue a suspend request through stdout, and receive the acknowledgement
    2.16   * from stdin.  This is handled by XendCheckpoint in the Python layer.
    2.17   */
    2.18 -static int suspend(int domid)
    2.19 +static int compat_suspend(int domid)
    2.20  {
    2.21      char ans[30];
    2.22  
    2.23 @@ -38,6 +45,131 @@ static int suspend(int domid)
    2.24              !strncmp(ans, "done\n", 5));
    2.25  }
    2.26  
    2.27 +static int suspend_evtchn_release(int xc, int domid)
    2.28 +{
    2.29 +    if (si.suspended_evtchn >= 0) {
    2.30 +	xc_dom_subscribe(xc, domid, 0);
    2.31 +	xc_evtchn_unbind(si.xce, si.suspended_evtchn);
    2.32 +	si.suspended_evtchn = -1;
    2.33 +    }
    2.34 +    if (si.suspend_evtchn >= 0) {
    2.35 +	xc_evtchn_unbind(si.xce, si.suspend_evtchn);
    2.36 +	si.suspend_evtchn = -1;
    2.37 +    }
    2.38 +    if (si.xce >= 0) {
    2.39 +	xc_evtchn_close(si.xce);
    2.40 +	si.xce = -1;
    2.41 +    }
    2.42 +
    2.43 +    return 0;
    2.44 +}
    2.45 +
    2.46 +static int suspend_evtchn_init(int xc, int domid)
    2.47 +{
    2.48 +    struct xs_handle *xs;
    2.49 +    char path[128];
    2.50 +    char *portstr;
    2.51 +    unsigned int plen;
    2.52 +    int port;
    2.53 +    int rc;
    2.54 +
    2.55 +    si.xce = -1;
    2.56 +    si.suspend_evtchn = -1;
    2.57 +    si.suspended_evtchn = -1;
    2.58 +
    2.59 +    xs = xs_daemon_open();
    2.60 +    if (!xs) {
    2.61 +	errx(1, "failed to get xenstore handle");
    2.62 +	return -1;
    2.63 +    }
    2.64 +    sprintf(path, "/local/domain/%d/device/suspend/event-channel", domid);
    2.65 +    portstr = xs_read(xs, XBT_NULL, path, &plen);
    2.66 +    xs_daemon_close(xs);
    2.67 +
    2.68 +    if (!portstr || !plen) {
    2.69 +	warnx("could not read suspend event channel");
    2.70 +	return -1;
    2.71 +    }
    2.72 +
    2.73 +    port = atoi(portstr);
    2.74 +    free(portstr);
    2.75 +
    2.76 +    si.xce = xc_evtchn_open();
    2.77 +    if (si.xce < 0) {
    2.78 +	errx(1, "failed to open event channel handle");
    2.79 +	goto cleanup;
    2.80 +    }
    2.81 +
    2.82 +    si.suspend_evtchn = xc_evtchn_bind_interdomain(si.xce, domid, port);
    2.83 +    if (si.suspend_evtchn < 0) {
    2.84 +	errx(1, "failed to bind suspend event channel: %d",
    2.85 +	     si.suspend_evtchn);
    2.86 +	goto cleanup;
    2.87 +    }
    2.88 +
    2.89 +    si.suspended_evtchn = xc_evtchn_bind_unbound_port(si.xce, domid);
    2.90 +    if (si.suspended_evtchn < 0) {
    2.91 +	errx(1, "failed to allocate suspend notification port: %d",
    2.92 +	     si.suspended_evtchn);
    2.93 +	goto cleanup;
    2.94 +    }
    2.95 +
    2.96 +    rc = xc_dom_subscribe(xc, domid, si.suspended_evtchn);
    2.97 +    if (rc < 0) {
    2.98 +	errx(1, "failed to subscribe to domain: %d", rc);
    2.99 +	goto cleanup;
   2.100 +    }
   2.101 +
   2.102 +    return 0;
   2.103 +
   2.104 +  cleanup:
   2.105 +    suspend_evtchn_release(xc, domid);
   2.106 +
   2.107 +    return -1;
   2.108 +}
   2.109 +
   2.110 +/**
   2.111 + * Issue a suspend request to a dedicated event channel in the guest, and
   2.112 + * receive the acknowledgement from the subscribe event channel. */
   2.113 +static int evtchn_suspend(int domid)
   2.114 +{
   2.115 +    int xcefd;
   2.116 +    int rc;
   2.117 +
   2.118 +    rc = xc_evtchn_notify(si.xce, si.suspend_evtchn);
   2.119 +    if (rc < 0) {
   2.120 +	errx(1, "failed to notify suspend request channel: %d", rc);
   2.121 +	return 0;
   2.122 +    }
   2.123 +
   2.124 +    xcefd = xc_evtchn_fd(si.xce);
   2.125 +    do {
   2.126 +      rc = xc_evtchn_pending(si.xce);
   2.127 +      if (rc < 0) {
   2.128 +	errx(1, "error polling suspend notification channel: %d", rc);
   2.129 +	return 0;
   2.130 +      }
   2.131 +    } while (rc != si.suspended_evtchn);
   2.132 +
   2.133 +    /* harmless for one-off suspend */
   2.134 +    if (xc_evtchn_unmask(si.xce, si.suspended_evtchn) < 0)
   2.135 +	errx(1, "failed to unmask suspend notification channel: %d", rc);
   2.136 +
   2.137 +    /* notify xend that it can do device migration */
   2.138 +    printf("suspended\n");
   2.139 +    fflush(stdout);
   2.140 +
   2.141 +    return 1;
   2.142 +}
   2.143 +
   2.144 +static int suspend(int domid)
   2.145 +{
   2.146 +    if (si.suspend_evtchn >= 0)
   2.147 +	return evtchn_suspend(domid);
   2.148 +
   2.149 +    return compat_suspend(domid);
   2.150 +}
   2.151 +
   2.152  /* For HVM guests, there are two sources of dirty pages: the Xen shadow
   2.153   * log-dirty bitmap, which we get with a hypercall, and qemu's version.
   2.154   * The protocol for getting page-dirtying data from qemu uses a
   2.155 @@ -188,10 +320,14 @@ main(int argc, char **argv)
   2.156      max_f = atoi(argv[4]);
   2.157      flags = atoi(argv[5]);
   2.158  
   2.159 +    suspend_evtchn_init(xc_fd, domid);
   2.160 +
   2.161      ret = xc_domain_save(xc_fd, io_fd, domid, maxit, max_f, flags, 
   2.162                           &suspend, !!(flags & XCFLAGS_HVM),
   2.163                           &init_qemu_maps, &qemu_flip_buffer);
   2.164  
   2.165 +    suspend_evtchn_release(xc_fd, domid);
   2.166 +
   2.167      xc_interface_close(xc_fd);
   2.168  
   2.169      return ret;