ia64/xen-unstable

view tools/xcutils/xc_save.c @ 19615:13a4f4e6d0a3

x86 hvm: Correctly emulate task switches into vm86 mode.

Signed-off-by: Keir Fraser <keir.fraser@citrix.com>
author Keir Fraser <keir.fraser@citrix.com>
date Tue May 19 02:12:04 2009 +0100 (2009-05-19)
parents 1afc1ded0ed7
children
line source
1 /*
2 * This file is subject to the terms and conditions of the GNU General
3 * Public License. See the file "COPYING" in the main directory of
4 * this archive for more details.
5 *
6 * Copyright (C) 2005 by Christian Limpach
7 *
8 */
10 #include <err.h>
11 #include <stdlib.h>
12 #include <stdint.h>
13 #include <string.h>
14 #include <stdio.h>
15 #include <sys/ipc.h>
16 #include <sys/shm.h>
17 #include <sys/types.h>
18 #include <sys/stat.h>
19 #include <fcntl.h>
20 #include <err.h>
22 #include <xs.h>
23 #include <xenctrl.h>
24 #include <xenguest.h>
26 static struct suspendinfo {
27 int xc_fd; /* libxc handle */
28 int xce; /* event channel handle */
29 int suspend_evtchn;
30 int domid;
31 unsigned int flags;
32 } si;
34 /**
35 * Issue a suspend request through stdout, and receive the acknowledgement
36 * from stdin. This is handled by XendCheckpoint in the Python layer.
37 */
38 static int compat_suspend(void)
39 {
40 char ans[30];
42 printf("suspend\n");
43 fflush(stdout);
45 return (fgets(ans, sizeof(ans), stdin) != NULL &&
46 !strncmp(ans, "done\n", 5));
47 }
49 /**
50 * Issue a suspend request to a dedicated event channel in the guest, and
51 * receive the acknowledgement from the subscribe event channel. */
52 static int evtchn_suspend(void)
53 {
54 int rc;
56 rc = xc_evtchn_notify(si.xce, si.suspend_evtchn);
57 if (rc < 0) {
58 warnx("failed to notify suspend request channel: %d", rc);
59 return 0;
60 }
62 if (xc_await_suspend(si.xce, si.suspend_evtchn) < 0) {
63 warnx("suspend failed");
64 return 0;
65 }
67 /* notify xend that it can do device migration */
68 printf("suspended\n");
69 fflush(stdout);
71 return 1;
72 }
74 static int suspend(void)
75 {
76 unsigned long sx_state = 0;
78 /* Cannot notify guest to shut itself down if it's in ACPI sleep state. */
79 if (si.flags & XCFLAGS_HVM)
80 xc_get_hvm_param(si.xc_fd, si.domid,
81 HVM_PARAM_ACPI_S_STATE, &sx_state);
83 if ((sx_state == 0) && (si.suspend_evtchn >= 0))
84 return evtchn_suspend();
86 return compat_suspend();
87 }
89 /* For HVM guests, there are two sources of dirty pages: the Xen shadow
90 * log-dirty bitmap, which we get with a hypercall, and qemu's version.
91 * The protocol for getting page-dirtying data from qemu uses a
92 * double-buffered shared memory interface directly between xc_save and
93 * qemu-dm.
94 *
95 * xc_save calculates the size of the bitmaps and notifies qemu-dm
96 * through the store that it wants to share the bitmaps. qemu-dm then
97 * starts filling in the 'active' buffer.
98 *
99 * To change the buffers over, xc_save writes the other buffer number to
100 * the store and waits for qemu to acknowledge that it is now writing to
101 * the new active buffer. xc_save can then process and clear the old
102 * active buffer. */
104 static char *qemu_active_path;
105 static char *qemu_next_active_path;
106 static int qemu_shmid = -1;
107 static struct xs_handle *xs;
110 /* Mark the shared-memory segment for destruction */
111 static void qemu_destroy_buffer(void)
112 {
113 if (qemu_shmid != -1)
114 shmctl(qemu_shmid, IPC_RMID, NULL);
115 qemu_shmid = -1;
116 }
118 /* Get qemu to change buffers. */
119 static void qemu_flip_buffer(int domid, int next_active)
120 {
121 char digit = '0' + next_active;
122 unsigned int len;
123 char *active_str, **watch;
124 struct timeval tv;
125 fd_set fdset;
127 /* Tell qemu that we want it to start writing log-dirty bits to the
128 * other buffer */
129 if (!xs_write(xs, XBT_NULL, qemu_next_active_path, &digit, 1))
130 errx(1, "can't write next-active to store path (%s)\n",
131 qemu_next_active_path);
133 /* Wait a while for qemu to signal that it has switched to the new
134 * active buffer */
135 read_again:
136 tv.tv_sec = 5;
137 tv.tv_usec = 0;
138 FD_ZERO(&fdset);
139 FD_SET(xs_fileno(xs), &fdset);
140 if ((select(xs_fileno(xs) + 1, &fdset, NULL, NULL, &tv)) != 1)
141 errx(1, "timed out waiting for qemu to switch buffers\n");
142 watch = xs_read_watch(xs, &len);
143 free(watch);
145 active_str = xs_read(xs, XBT_NULL, qemu_active_path, &len);
146 if (active_str == NULL || active_str[0] - '0' != next_active)
147 /* Watch fired but value is not yet right */
148 goto read_again;
149 }
151 static void *init_qemu_maps(int domid, unsigned int bitmap_size)
152 {
153 key_t key;
154 char key_ascii[17] = {0,};
155 void *seg;
156 char *path, *p;
158 /* Make a shared-memory segment */
159 do {
160 key = rand(); /* No security, just a sequence of numbers */
161 qemu_shmid = shmget(key, 2 * bitmap_size,
162 IPC_CREAT|IPC_EXCL|S_IRUSR|S_IWUSR);
163 if (qemu_shmid == -1 && errno != EEXIST)
164 errx(1, "can't get shmem to talk to qemu-dm");
165 } while (qemu_shmid == -1);
167 /* Remember to tidy up after ourselves */
168 atexit(qemu_destroy_buffer);
170 /* Map it into our address space */
171 seg = shmat(qemu_shmid, NULL, 0);
172 if (seg == (void *) -1)
173 errx(1, "can't map shmem to talk to qemu-dm");
174 memset(seg, 0, 2 * bitmap_size);
176 /* Write the size of it into the first 32 bits */
177 *(uint32_t *)seg = bitmap_size;
179 /* Tell qemu about it */
180 if ((xs = xs_daemon_open()) == NULL)
181 errx(1, "Couldn't contact xenstore");
182 if (!(path = strdup("/local/domain/0/device-model/")))
183 errx(1, "can't get domain path in store");
184 if (!(path = realloc(path, strlen(path)
185 + 10
186 + strlen("/logdirty/next-active") + 1)))
187 errx(1, "no memory for constructing xenstore path");
188 snprintf(path + strlen(path), 11, "%i", domid);
189 strcat(path, "/logdirty/");
190 p = path + strlen(path);
192 strcpy(p, "key");
193 snprintf(key_ascii, 17, "%16.16llx", (unsigned long long) key);
194 if (!xs_write(xs, XBT_NULL, path, key_ascii, 16))
195 errx(1, "can't write key (%s) to store path (%s)\n", key_ascii, path);
197 /* Watch for qemu's indication of the active buffer, and request it
198 * to start writing to buffer 0 */
199 strcpy(p, "active");
200 if (!xs_watch(xs, path, "qemu-active-buffer"))
201 errx(1, "can't set watch in store (%s)\n", path);
202 if (!(qemu_active_path = strdup(path)))
203 errx(1, "no memory for copying xenstore path");
205 strcpy(p, "next-active");
206 if (!(qemu_next_active_path = strdup(path)))
207 errx(1, "no memory for copying xenstore path");
209 qemu_flip_buffer(domid, 0);
211 free(path);
212 return seg;
213 }
215 int
216 main(int argc, char **argv)
217 {
218 unsigned int maxit, max_f;
219 int io_fd, ret, port;
221 if (argc != 6)
222 errx(1, "usage: %s iofd domid maxit maxf flags", argv[0]);
224 si.xc_fd = xc_interface_open();
225 if (si.xc_fd < 0)
226 errx(1, "failed to open control interface");
228 io_fd = atoi(argv[1]);
229 si.domid = atoi(argv[2]);
230 maxit = atoi(argv[3]);
231 max_f = atoi(argv[4]);
232 si.flags = atoi(argv[5]);
234 si.suspend_evtchn = si.xce = -1;
236 si.xce = xc_evtchn_open();
237 if (si.xce < 0)
238 warnx("failed to open event channel handle");
240 if (si.xce > 0)
241 {
242 port = xs_suspend_evtchn_port(si.domid);
244 if (port < 0)
245 warnx("failed to get the suspend evtchn port\n");
246 else
247 {
248 si.suspend_evtchn =
249 xc_suspend_evtchn_init(si.xc_fd, si.xce, si.domid, port);
251 if (si.suspend_evtchn < 0)
252 warnx("suspend event channel initialization failed"
253 "using slow path");
254 }
255 }
256 ret = xc_domain_save(si.xc_fd, io_fd, si.domid, maxit, max_f, si.flags,
257 &suspend, !!(si.flags & XCFLAGS_HVM),
258 &init_qemu_maps, &qemu_flip_buffer);
260 if (si.suspend_evtchn > 0)
261 xc_suspend_evtchn_release(si.xce, si.suspend_evtchn);
263 if (si.xce > 0)
264 xc_evtchn_close(si.xce);
266 xc_interface_close(si.xc_fd);
268 return ret;
269 }