1 /*
2 * Copyright (C) 2009 Citrix Ltd.
3 * Author Vincent Hanquez <vincent.hanquez@eu.citrix.com>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU Lesser General Public License as published
7 * by the Free Software Foundation; version 2.1 only. with the special
8 * exception on linking described in file LICENSE.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU Lesser General Public License for more details.
14 */
15
16 #include "libxl_osdeps.h" /* must come before any other headers */
17
18 #include "libxl_internal.h"
19
20 /*====================== Domain suspend =======================*/
21
libxl__domain_suspend_init(libxl__egc * egc,libxl__domain_suspend_state * dsps,libxl_domain_type type)22 int libxl__domain_suspend_init(libxl__egc *egc,
23 libxl__domain_suspend_state *dsps,
24 libxl_domain_type type)
25 {
26 STATE_AO_GC(dsps->ao);
27 int rc = ERROR_FAIL;
28 int port;
29
30 /* Convenience aliases */
31 const uint32_t domid = dsps->domid;
32
33 libxl__xswait_init(&dsps->pvcontrol);
34 libxl__ev_evtchn_init(&dsps->guest_evtchn);
35 libxl__ev_xswatch_init(&dsps->guest_watch);
36 libxl__ev_time_init(&dsps->guest_timeout);
37 libxl__ev_qmp_init(&dsps->qmp);
38
39 if (type == LIBXL_DOMAIN_TYPE_INVALID) goto out;
40 dsps->type = type;
41
42 dsps->guest_evtchn.port = -1;
43 dsps->guest_evtchn_lockfd = -1;
44 dsps->guest_responded = 0;
45 dsps->dm_savefile = libxl__device_model_savefile(gc, domid);
46
47 port = xs_suspend_evtchn_port(domid);
48
49 if (port >= 0) {
50 rc = libxl__ctx_evtchn_init(gc);
51 if (rc) goto out;
52
53 dsps->guest_evtchn.port =
54 xc_suspend_evtchn_init_exclusive(CTX->xch, CTX->xce,
55 domid, port, &dsps->guest_evtchn_lockfd);
56
57 if (dsps->guest_evtchn.port < 0) {
58 LOGD(WARN, domid, "Suspend event channel initialization failed");
59 rc = ERROR_FAIL;
60 goto out;
61 }
62 }
63
64 rc = 0;
65
66 out:
67 return rc;
68 }
69
70 /*----- callbacks, called by xc_domain_save -----*/
71
libxl__domain_suspend_device_model(libxl__egc * egc,libxl__domain_suspend_state * dsps)72 void libxl__domain_suspend_device_model(libxl__egc *egc,
73 libxl__domain_suspend_state *dsps)
74 {
75 STATE_AO_GC(dsps->ao);
76 int rc = 0;
77 uint32_t const domid = dsps->domid;
78 const char *const filename = dsps->dm_savefile;
79
80 switch (libxl__device_model_version_running(gc, domid)) {
81 case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN_TRADITIONAL: {
82 LOGD(DEBUG, domid, "Saving device model state to %s", filename);
83 libxl__qemu_traditional_cmd(gc, domid, "save");
84 libxl__wait_for_device_model_deprecated(gc, domid, "paused", NULL, NULL, NULL);
85 break;
86 }
87 case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN:
88 /* calls dsps->callback_device_model_done when done */
89 libxl__qmp_suspend_save(egc, dsps); /* must be last */
90 return;
91 default:
92 rc = ERROR_INVAL;
93 goto out;
94 }
95
96 out:
97 if (rc)
98 LOGD(ERROR, dsps->domid,
99 "failed to suspend device model, rc=%d", rc);
100 dsps->callback_device_model_done(egc, dsps, rc); /* must be last */
101 }
102
103 static void domain_suspend_common_wait_guest(libxl__egc *egc,
104 libxl__domain_suspend_state *dsps);
105 static void domain_suspend_common_guest_suspended(libxl__egc *egc,
106 libxl__domain_suspend_state *dsps);
107
108 static void domain_suspend_common_pvcontrol_suspending(libxl__egc *egc,
109 libxl__xswait_state *xswa, int rc, const char *state);
110 static void domain_suspend_common_wait_guest_evtchn(libxl__egc *egc,
111 libxl__ev_evtchn *evev);
112 static void suspend_common_wait_guest_watch(libxl__egc *egc,
113 libxl__ev_xswatch *xsw, const char *watch_path, const char *event_path);
114 static void suspend_common_wait_guest_check(libxl__egc *egc,
115 libxl__domain_suspend_state *dsps);
116 static void suspend_common_wait_guest_timeout(libxl__egc *egc,
117 libxl__ev_time *ev, const struct timeval *requested_abs, int rc);
118
119 static void domain_suspend_common_done(libxl__egc *egc,
120 libxl__domain_suspend_state *dsps,
121 int rc);
122
123 static void domain_suspend_callback_common(libxl__egc *egc,
124 libxl__domain_suspend_state *dsps);
125 static void domain_suspend_callback_common_done(libxl__egc *egc,
126 libxl__domain_suspend_state *dsps, int rc);
127
128 /* calls dsps->callback_common_done when done */
libxl__domain_suspend(libxl__egc * egc,libxl__domain_suspend_state * dsps)129 void libxl__domain_suspend(libxl__egc *egc,
130 libxl__domain_suspend_state *dsps)
131 {
132 domain_suspend_callback_common(egc, dsps);
133 }
134
domain_suspend_pvcontrol_acked(const char * state)135 static bool domain_suspend_pvcontrol_acked(const char *state) {
136 /* any value other than "suspend", including ENOENT (i.e. !state), is OK */
137 if (!state) return 1;
138 return strcmp(state,"suspend");
139 }
140
141 /* calls dsps->callback_common_done when done */
domain_suspend_callback_common(libxl__egc * egc,libxl__domain_suspend_state * dsps)142 static void domain_suspend_callback_common(libxl__egc *egc,
143 libxl__domain_suspend_state *dsps)
144 {
145 STATE_AO_GC(dsps->ao);
146 uint64_t hvm_s_state = 0, hvm_pvdrv = 0;
147 int ret, rc;
148
149 /* Convenience aliases */
150 const uint32_t domid = dsps->domid;
151
152 if (dsps->type != LIBXL_DOMAIN_TYPE_PV) {
153 xc_hvm_param_get(CTX->xch, domid, HVM_PARAM_CALLBACK_IRQ, &hvm_pvdrv);
154 xc_hvm_param_get(CTX->xch, domid, HVM_PARAM_ACPI_S_STATE, &hvm_s_state);
155 }
156
157 if ((hvm_s_state == 0) && (dsps->guest_evtchn.port >= 0)) {
158 LOGD(DEBUG, domid, "issuing %s suspend request via event channel",
159 dsps->type != LIBXL_DOMAIN_TYPE_PV ? "PVH/HVM" : "PV");
160 ret = xenevtchn_notify(CTX->xce, dsps->guest_evtchn.port);
161 if (ret < 0) {
162 LOGD(ERROR, domid, "xenevtchn_notify failed ret=%d", ret);
163 rc = ERROR_FAIL;
164 goto err;
165 }
166
167 dsps->guest_evtchn.callback = domain_suspend_common_wait_guest_evtchn;
168 rc = libxl__ev_evtchn_wait(gc, &dsps->guest_evtchn);
169 if (rc) goto err;
170
171 rc = libxl__ev_time_register_rel(ao, &dsps->guest_timeout,
172 suspend_common_wait_guest_timeout,
173 60*1000);
174 if (rc) goto err;
175
176 return;
177 }
178
179 if (dsps->type == LIBXL_DOMAIN_TYPE_HVM && (!hvm_pvdrv || hvm_s_state)) {
180 LOGD(DEBUG, domid, "Calling xc_domain_shutdown on HVM domain");
181 ret = xc_domain_shutdown(CTX->xch, domid, SHUTDOWN_suspend);
182 if (ret < 0) {
183 LOGED(ERROR, domid, "xc_domain_shutdown failed");
184 rc = ERROR_FAIL;
185 goto err;
186 }
187 /* The guest does not (need to) respond to this sort of request. */
188 dsps->guest_responded = 1;
189 domain_suspend_common_wait_guest(egc, dsps);
190 return;
191 }
192
193 LOGD(DEBUG, domid, "issuing %s suspend request via XenBus control node",
194 dsps->type != LIBXL_DOMAIN_TYPE_PV ? "PVH/HVM" : "PV");
195
196 dsps->pvcontrol.ao = ao;
197 dsps->pvcontrol.callback = domain_suspend_common_pvcontrol_suspending;
198 rc = libxl__domain_pvcontrol(egc, &dsps->pvcontrol, domid, "suspend");
199 if (rc) goto err;
200
201 return;
202
203 err:
204 domain_suspend_common_done(egc, dsps, rc);
205 }
206
domain_suspend_common_wait_guest_evtchn(libxl__egc * egc,libxl__ev_evtchn * evev)207 static void domain_suspend_common_wait_guest_evtchn(libxl__egc *egc,
208 libxl__ev_evtchn *evev)
209 {
210 libxl__domain_suspend_state *dsps = CONTAINER_OF(evev, *dsps, guest_evtchn);
211 STATE_AO_GC(dsps->ao);
212 /* If we should be done waiting, suspend_common_wait_guest_check
213 * will end up calling domain_suspend_common_guest_suspended or
214 * domain_suspend_common_done, both of which cancel the evtchn
215 * wait as needed. So re-enable it now. */
216 libxl__ev_evtchn_wait(gc, &dsps->guest_evtchn);
217 suspend_common_wait_guest_check(egc, dsps);
218 }
219
domain_suspend_common_pvcontrol_suspending(libxl__egc * egc,libxl__xswait_state * xswa,int rc,const char * state)220 static void domain_suspend_common_pvcontrol_suspending(libxl__egc *egc,
221 libxl__xswait_state *xswa, int rc, const char *state)
222 {
223 libxl__domain_suspend_state *dsps = CONTAINER_OF(xswa, *dsps, pvcontrol);
224 STATE_AO_GC(dsps->ao);
225 xs_transaction_t t = 0;
226
227 if (!rc && !domain_suspend_pvcontrol_acked(state))
228 /* keep waiting */
229 return;
230
231 libxl__xswait_stop(gc, &dsps->pvcontrol);
232
233 if (rc == ERROR_TIMEDOUT) {
234 /*
235 * Guest appears to not be responding. Cancel the suspend
236 * request.
237 *
238 * We re-read the suspend node and clear it within a
239 * transaction in order to handle the case where we race
240 * against the guest catching up and acknowledging the request
241 * at the last minute.
242 */
243 for (;;) {
244 rc = libxl__xs_transaction_start(gc, &t);
245 if (rc) goto err;
246
247 rc = libxl__xs_read_checked(gc, t, xswa->path, &state);
248 if (rc) goto err;
249
250 if (domain_suspend_pvcontrol_acked(state))
251 /* last minute ack */
252 break;
253
254 rc = libxl__xs_write_checked(gc, t, xswa->path, "");
255 if (rc) goto err;
256
257 rc = libxl__xs_transaction_commit(gc, &t);
258 if (!rc) {
259 LOGD(ERROR, dsps->domid,
260 "guest didn't acknowledge suspend, cancelling request");
261 goto err;
262 }
263 if (rc<0) goto err;
264 }
265 } else if (rc) {
266 /* some error in xswait's read of xenstore, already logged */
267 goto err;
268 }
269
270 assert(domain_suspend_pvcontrol_acked(state));
271 LOGD(DEBUG, dsps->domid, "guest acknowledged suspend request");
272
273 libxl__xs_transaction_abort(gc, &t);
274 dsps->guest_responded = 1;
275 domain_suspend_common_wait_guest(egc,dsps);
276 return;
277
278 err:
279 libxl__xs_transaction_abort(gc, &t);
280 domain_suspend_common_done(egc, dsps, rc);
281 return;
282 }
283
domain_suspend_common_wait_guest(libxl__egc * egc,libxl__domain_suspend_state * dsps)284 static void domain_suspend_common_wait_guest(libxl__egc *egc,
285 libxl__domain_suspend_state *dsps)
286 {
287 STATE_AO_GC(dsps->ao);
288 int rc;
289
290 LOGD(DEBUG, dsps->domid, "wait for the guest to suspend");
291
292 rc = libxl__ev_xswatch_register(gc, &dsps->guest_watch,
293 suspend_common_wait_guest_watch,
294 "@releaseDomain");
295 if (rc) goto err;
296
297 rc = libxl__ev_time_register_rel(ao, &dsps->guest_timeout,
298 suspend_common_wait_guest_timeout,
299 60*1000);
300 if (rc) goto err;
301 return;
302
303 err:
304 domain_suspend_common_done(egc, dsps, rc);
305 }
306
suspend_common_wait_guest_watch(libxl__egc * egc,libxl__ev_xswatch * xsw,const char * watch_path,const char * event_path)307 static void suspend_common_wait_guest_watch(libxl__egc *egc,
308 libxl__ev_xswatch *xsw, const char *watch_path, const char *event_path)
309 {
310 libxl__domain_suspend_state *dsps = CONTAINER_OF(xsw, *dsps, guest_watch);
311 suspend_common_wait_guest_check(egc, dsps);
312 }
313
suspend_common_wait_guest_check(libxl__egc * egc,libxl__domain_suspend_state * dsps)314 static void suspend_common_wait_guest_check(libxl__egc *egc,
315 libxl__domain_suspend_state *dsps)
316 {
317 STATE_AO_GC(dsps->ao);
318 xc_domaininfo_t info;
319 int ret;
320 int shutdown_reason;
321
322 /* Convenience aliases */
323 const uint32_t domid = dsps->domid;
324
325 ret = xc_domain_getinfolist(CTX->xch, domid, 1, &info);
326 if (ret < 0) {
327 LOGED(ERROR, domid, "unable to check for status of guest");
328 goto err;
329 }
330
331 if (!(ret == 1 && info.domain == domid)) {
332 LOGED(ERROR, domid, "guest we were suspending has been destroyed");
333 goto err;
334 }
335
336 if (!(info.flags & XEN_DOMINF_shutdown))
337 /* keep waiting */
338 return;
339
340 shutdown_reason = (info.flags >> XEN_DOMINF_shutdownshift)
341 & XEN_DOMINF_shutdownmask;
342 if (shutdown_reason != SHUTDOWN_suspend) {
343 LOGD(DEBUG, domid, "guest we were suspending has shut down"
344 " with unexpected reason code %d", shutdown_reason);
345 goto err;
346 }
347
348 LOGD(DEBUG, domid, "guest has suspended");
349 domain_suspend_common_guest_suspended(egc, dsps);
350 return;
351
352 err:
353 domain_suspend_common_done(egc, dsps, ERROR_FAIL);
354 }
355
suspend_common_wait_guest_timeout(libxl__egc * egc,libxl__ev_time * ev,const struct timeval * requested_abs,int rc)356 static void suspend_common_wait_guest_timeout(libxl__egc *egc,
357 libxl__ev_time *ev, const struct timeval *requested_abs, int rc)
358 {
359 libxl__domain_suspend_state *dsps = CONTAINER_OF(ev, *dsps, guest_timeout);
360 STATE_AO_GC(dsps->ao);
361 if (rc == ERROR_TIMEDOUT) {
362 LOGD(ERROR, dsps->domid, "guest did not suspend, timed out");
363 rc = ERROR_GUEST_TIMEDOUT;
364 }
365 domain_suspend_common_done(egc, dsps, rc);
366 }
367
domain_suspend_common_guest_suspended(libxl__egc * egc,libxl__domain_suspend_state * dsps)368 static void domain_suspend_common_guest_suspended(libxl__egc *egc,
369 libxl__domain_suspend_state *dsps)
370 {
371 STATE_AO_GC(dsps->ao);
372
373 libxl__ev_evtchn_cancel(gc, &dsps->guest_evtchn);
374 libxl__ev_xswatch_deregister(gc, &dsps->guest_watch);
375 libxl__ev_time_deregister(gc, &dsps->guest_timeout);
376
377 if (dsps->type == LIBXL_DOMAIN_TYPE_HVM) {
378 dsps->callback_device_model_done = domain_suspend_common_done;
379 libxl__domain_suspend_device_model(egc, dsps); /* must be last */
380 return;
381 }
382 domain_suspend_common_done(egc, dsps, 0);
383 }
384
domain_suspend_common_done(libxl__egc * egc,libxl__domain_suspend_state * dsps,int rc)385 static void domain_suspend_common_done(libxl__egc *egc,
386 libxl__domain_suspend_state *dsps,
387 int rc)
388 {
389 EGC_GC;
390 assert(!libxl__xswait_inuse(&dsps->pvcontrol));
391 libxl__ev_evtchn_cancel(gc, &dsps->guest_evtchn);
392 libxl__ev_xswatch_deregister(gc, &dsps->guest_watch);
393 libxl__ev_time_deregister(gc, &dsps->guest_timeout);
394 libxl__ev_qmp_dispose(gc, &dsps->qmp);
395 dsps->callback_common_done(egc, dsps, rc);
396 }
397
libxl__domain_suspend_callback(void * data)398 void libxl__domain_suspend_callback(void *data)
399 {
400 libxl__save_helper_state *shs = data;
401 libxl__egc *egc = shs->egc;
402 libxl__domain_save_state *dss = shs->caller_state;
403 libxl__domain_suspend_state *dsps = &dss->dsps;
404
405 dsps->callback_common_done = domain_suspend_callback_common_done;
406 domain_suspend_callback_common(egc, dsps);
407 }
408
domain_suspend_callback_common_done(libxl__egc * egc,libxl__domain_suspend_state * dsps,int rc)409 static void domain_suspend_callback_common_done(libxl__egc *egc,
410 libxl__domain_suspend_state *dsps, int rc)
411 {
412 libxl__domain_save_state *dss = CONTAINER_OF(dsps, *dss, dsps);
413 dss->rc = rc;
414 libxl__xc_domain_saverestore_async_callback_done(egc, &dss->sws.shs, !rc);
415 }
416
417 /*======================= Domain resume ========================*/
418
libxl__domain_resume_device_model_deprecated(libxl__gc * gc,uint32_t domid)419 int libxl__domain_resume_device_model_deprecated(libxl__gc *gc, uint32_t domid)
420 {
421 const char *path, *state;
422
423 switch (libxl__device_model_version_running(gc, domid)) {
424 case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN_TRADITIONAL: {
425 uint32_t dm_domid = libxl_get_stubdom_id(CTX, domid);
426
427 path = DEVICE_MODEL_XS_PATH(gc, dm_domid, domid, "/state");
428 state = libxl__xs_read(gc, XBT_NULL, path);
429 if (state != NULL && !strcmp(state, "paused")) {
430 libxl__qemu_traditional_cmd(gc, domid, "continue");
431 libxl__wait_for_device_model_deprecated(gc, domid, "running",
432 NULL, NULL, NULL);
433 }
434 break;
435 }
436 case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN:
437 if (libxl__qmp_resume(gc, domid))
438 return ERROR_FAIL;
439 break;
440 default:
441 return ERROR_INVAL;
442 }
443
444 return 0;
445 }
446
libxl__domain_resume_deprecated(libxl__gc * gc,uint32_t domid,int suspend_cancel)447 int libxl__domain_resume_deprecated(libxl__gc *gc, uint32_t domid, int suspend_cancel)
448 {
449 int rc = 0;
450
451 libxl_domain_type type = libxl__domain_type(gc, domid);
452 if (type == LIBXL_DOMAIN_TYPE_INVALID) {
453 rc = ERROR_FAIL;
454 goto out;
455 }
456
457 if (type == LIBXL_DOMAIN_TYPE_HVM) {
458 rc = libxl__domain_resume_device_model_deprecated(gc, domid);
459 if (rc) {
460 LOGD(ERROR, domid, "failed to resume device model:%d", rc);
461 goto out;
462 }
463 }
464
465 if (xc_domain_resume(CTX->xch, domid, suspend_cancel)) {
466 LOGED(ERROR, domid, "xc_domain_resume failed");
467 rc = ERROR_FAIL;
468 goto out;
469 }
470
471 if (!xs_resume_domain(CTX->xsh, domid)) {
472 LOGED(ERROR, domid, "xs_resume_domain failed");
473 rc = ERROR_FAIL;
474 }
475 out:
476 return rc;
477 }
478
dm_resume_init(libxl__dm_resume_state * dmrs)479 static void dm_resume_init(libxl__dm_resume_state *dmrs)
480 {
481 libxl__ev_qmp_init(&dmrs->qmp);
482 libxl__ev_time_init(&dmrs->time);
483 libxl__ev_xswatch_init(&dmrs->watch);
484 }
485
dm_resume_dispose(libxl__gc * gc,libxl__dm_resume_state * dmrs)486 static void dm_resume_dispose(libxl__gc *gc,
487 libxl__dm_resume_state *dmrs)
488 {
489 libxl__ev_qmp_dispose(gc, &dmrs->qmp);
490 libxl__ev_time_deregister(gc, &dmrs->time);
491 libxl__ev_xswatch_deregister(gc, &dmrs->watch);
492 }
493
494 static void dm_resume_xswatch_cb(libxl__egc *egc,
495 libxl__ev_xswatch *, const char *watch_path, const char *);
496 static void dm_resume_qmp_done(libxl__egc *egc,
497 libxl__ev_qmp *qmp, const libxl__json_object *, int rc);
498 static void dm_resume_timeout(libxl__egc *egc,
499 libxl__ev_time *, const struct timeval *, int rc);
500 static void dm_resume_done(libxl__egc *egc,
501 libxl__dm_resume_state *dmrs, int rc);
502
libxl__dm_resume(libxl__egc * egc,libxl__dm_resume_state * dmrs)503 void libxl__dm_resume(libxl__egc *egc,
504 libxl__dm_resume_state *dmrs)
505 {
506 STATE_AO_GC(dmrs->ao);
507 int rc = 0;
508
509 /* Convenience aliases */
510 libxl_domid domid = dmrs->domid;
511 libxl__ev_qmp *qmp = &dmrs->qmp;
512
513 dm_resume_init(dmrs);
514
515 rc = libxl__ev_time_register_rel(dmrs->ao,
516 &dmrs->time,
517 dm_resume_timeout,
518 LIBXL_DEVICE_MODEL_START_TIMEOUT * 1000);
519 if (rc) goto out;
520
521 switch (libxl__device_model_version_running(gc, domid)) {
522 case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN_TRADITIONAL: {
523 uint32_t dm_domid = libxl_get_stubdom_id(CTX, domid);
524 const char *path, *state;
525
526 path = DEVICE_MODEL_XS_PATH(gc, dm_domid, domid, "/state");
527 rc = libxl__xs_read_checked(gc, XBT_NULL, path, &state);
528 if (rc) goto out;
529 if (!state || strcmp(state, "paused")) {
530 /* already running */
531 rc = 0;
532 goto out;
533 }
534
535 rc = libxl__qemu_traditional_cmd(gc, domid, "continue");
536 if (rc) goto out;
537 rc = libxl__ev_xswatch_register(gc, &dmrs->watch,
538 dm_resume_xswatch_cb,
539 path);
540 if (rc) goto out;
541 break;
542 }
543 case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN:
544 qmp->ao = dmrs->ao;
545 qmp->domid = domid;
546 qmp->callback = dm_resume_qmp_done;
547 qmp->payload_fd = -1;
548 rc = libxl__ev_qmp_send(egc, qmp, "cont", NULL);
549 if (rc) goto out;
550 break;
551 default:
552 rc = ERROR_INVAL;
553 goto out;
554 }
555
556 return;
557
558 out:
559 dm_resume_done(egc, dmrs, rc);
560 }
561
dm_resume_xswatch_cb(libxl__egc * egc,libxl__ev_xswatch * xsw,const char * watch_path,const char * event_path)562 static void dm_resume_xswatch_cb(libxl__egc *egc,
563 libxl__ev_xswatch *xsw,
564 const char *watch_path,
565 const char *event_path)
566 {
567 EGC_GC;
568 libxl__dm_resume_state *dmrs = CONTAINER_OF(xsw, *dmrs, watch);
569 int rc;
570 const char *value;
571
572 rc = libxl__xs_read_checked(gc, XBT_NULL, watch_path, &value);
573 if (rc) goto out;
574
575 if (!value || strcmp(value, "running"))
576 return;
577
578 rc = 0;
579 out:
580 dm_resume_done(egc, dmrs, rc);
581 }
582
dm_resume_qmp_done(libxl__egc * egc,libxl__ev_qmp * qmp,const libxl__json_object * response,int rc)583 static void dm_resume_qmp_done(libxl__egc *egc,
584 libxl__ev_qmp *qmp,
585 const libxl__json_object *response,
586 int rc)
587 {
588 libxl__dm_resume_state *dmrs = CONTAINER_OF(qmp, *dmrs, qmp);
589 dm_resume_done(egc, dmrs, rc);
590 }
591
dm_resume_timeout(libxl__egc * egc,libxl__ev_time * ev,const struct timeval * requested_abs,int rc)592 static void dm_resume_timeout(libxl__egc *egc,
593 libxl__ev_time *ev,
594 const struct timeval *requested_abs,
595 int rc)
596 {
597 libxl__dm_resume_state *dmrs = CONTAINER_OF(ev, *dmrs, time);
598 dm_resume_done(egc, dmrs, rc);
599 }
600
dm_resume_done(libxl__egc * egc,libxl__dm_resume_state * dmrs,int rc)601 static void dm_resume_done(libxl__egc *egc,
602 libxl__dm_resume_state *dmrs,
603 int rc)
604 {
605 EGC_GC;
606
607 if (rc) {
608 LOGD(ERROR, dmrs->domid,
609 "Failed to resume device model: rc=%d", rc);
610 }
611
612 dm_resume_dispose(gc, dmrs);
613 dmrs->dm_resumed_callback(egc, dmrs, rc);
614 }
615
616
617 static void domain_resume_done(libxl__egc *egc,
618 libxl__dm_resume_state *dmrs, int rc);
619
libxl__domain_resume(libxl__egc * egc,libxl__dm_resume_state * dmrs,bool suspend_cancel)620 void libxl__domain_resume(libxl__egc *egc,
621 libxl__dm_resume_state *dmrs,
622 bool suspend_cancel)
623 {
624 STATE_AO_GC(dmrs->ao);
625 int rc = 0;
626 libxl_domain_type type = libxl__domain_type(gc, dmrs->domid);
627
628 if (type == LIBXL_DOMAIN_TYPE_INVALID) {
629 rc = ERROR_FAIL;
630 goto out;
631 }
632
633 if (type != LIBXL_DOMAIN_TYPE_HVM) {
634 rc = 0;
635 goto out;
636 }
637
638 dmrs->suspend_cancel = suspend_cancel;
639 dmrs->dm_resumed_callback = domain_resume_done;
640 libxl__dm_resume(egc, dmrs); /* must be last */
641 return;
642
643 out:
644 domain_resume_done(egc, dmrs, rc);
645 }
646
domain_resume_done(libxl__egc * egc,libxl__dm_resume_state * dmrs,int rc)647 static void domain_resume_done(libxl__egc *egc,
648 libxl__dm_resume_state *dmrs, int rc)
649 {
650 EGC_GC;
651
652 /* Convenience aliases */
653 libxl_domid domid = dmrs->domid;
654
655 if (rc) goto out;
656
657 if (xc_domain_resume(CTX->xch, domid, dmrs->suspend_cancel)) {
658 LOGED(ERROR, domid, "xc_domain_resume failed");
659 rc = ERROR_FAIL;
660 goto out;
661 }
662
663 if (!xs_resume_domain(CTX->xsh, domid)) {
664 LOGED(ERROR, domid, "xs_resume_domain failed");
665 rc = ERROR_FAIL;
666 }
667 out:
668 dmrs->callback(egc, dmrs, rc);
669 }
670
671 /*
672 * Local variables:
673 * mode: C
674 * c-basic-offset: 4
675 * indent-tabs-mode: nil
676 * End:
677 */
678