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