1 /*
2  * Copyright 2009-2017 Citrix Ltd and other contributors
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU Lesser General Public License as published
6  * by the Free Software Foundation; version 2.1 only. with the special
7  * exception on linking described in file LICENSE.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU Lesser General Public License for more details.
13  */
14 
15 #include "libxl_osdeps.h"
16 
17 #include "libxl_internal.h"
18 
19 #define PAGE_TO_MEMKB(pages) ((pages) * 4)
20 
libxl__domain_rename(libxl__gc * gc,uint32_t domid,const char * old_name,const char * new_name,xs_transaction_t trans)21 int libxl__domain_rename(libxl__gc *gc, uint32_t domid,
22                          const char *old_name, const char *new_name,
23                          xs_transaction_t trans)
24 {
25     libxl_ctx *ctx = libxl__gc_owner(gc);
26     char *dom_path = 0;
27     const char *name_path;
28     char *got_old_name;
29     unsigned int got_old_len;
30     xs_transaction_t our_trans = 0;
31     uint32_t stub_dm_domid;
32     const char *stub_dm_old_name = NULL, *stub_dm_new_name = NULL;
33     int rc;
34     libxl_dominfo info;
35     char *uuid;
36     const char *vm_name_path;
37 
38     libxl_dominfo_init(&info);
39 
40     dom_path = libxl__xs_get_dompath(gc, domid);
41     if (!dom_path) goto x_nomem;
42 
43     name_path= GCSPRINTF("%s/name", dom_path);
44     if (!name_path) goto x_nomem;
45 
46     stub_dm_domid = libxl_get_stubdom_id(CTX, domid);
47     if (stub_dm_domid) {
48         stub_dm_old_name = libxl__stub_dm_name(gc, old_name);
49         stub_dm_new_name = libxl__stub_dm_name(gc, new_name);
50     }
51 
52  retry_transaction:
53     if (!trans) {
54         trans = our_trans = xs_transaction_start(ctx->xsh);
55         if (!our_trans) {
56             LOGEVD(ERROR, errno, domid, "Create xs transaction for domain (re)name");
57             goto x_fail;
58         }
59     }
60 
61     if (!new_name) {
62         LOGD(ERROR, domid, "New domain name not specified");
63         rc = ERROR_INVAL;
64         goto x_rc;
65     }
66 
67     if (new_name[0]) {
68         /* nonempty names must be unique */
69         uint32_t domid_e;
70         rc = libxl_name_to_domid(ctx, new_name, &domid_e);
71         if (rc == ERROR_INVAL) {
72             /* no such domain, good */
73         } else if (rc != 0) {
74             LOGD(ERROR, domid, "Unexpected error checking for existing domain");
75             goto x_rc;
76         } else if (domid_e == domid) {
77             /* domain already has this name, ok (but we do still
78              * need the rest of the code as we may need to check
79              * old_name, for example). */
80         } else {
81             LOGD(ERROR, domid, "Domain with name \"%s\" already exists.", new_name);
82             rc = ERROR_INVAL;
83             goto x_rc;
84         }
85     }
86 
87     if (old_name) {
88         got_old_name = xs_read(ctx->xsh, trans, name_path, &got_old_len);
89         if (!got_old_name) {
90             LOGEVD(ERROR, errno, domid,
91                    "Check old name for domain allegedly named `%s'",
92                    old_name);
93             goto x_fail;
94         }
95         if (strcmp(old_name, got_old_name)) {
96             LOGD(ERROR, domid,
97                  "Allegedly named `%s' is actually named `%s' - racing ?",
98                  old_name,
99                  got_old_name);
100             free(got_old_name);
101             goto x_fail;
102         }
103         free(got_old_name);
104     }
105     if (!xs_write(ctx->xsh, trans, name_path,
106                   new_name, strlen(new_name))) {
107         LOGD(ERROR, domid,
108              "Failed to write new name `%s'"
109              " for domain previously named `%s'",
110              new_name,
111              old_name);
112         goto x_fail;
113     }
114 
115     /* update /vm/<uuid>/name */
116     rc = libxl_domain_info(ctx, &info, domid);
117     if (rc)
118         goto x_rc;
119 
120     uuid = GCSPRINTF(LIBXL_UUID_FMT, LIBXL_UUID_BYTES(info.uuid));
121     vm_name_path = GCSPRINTF("/vm/%s/name", uuid);
122     if (libxl__xs_write_checked(gc, trans, vm_name_path, new_name))
123         goto x_fail;
124 
125     if (stub_dm_domid) {
126         rc = libxl__domain_rename(gc, stub_dm_domid,
127                                   stub_dm_old_name,
128                                   stub_dm_new_name,
129                                   trans);
130         if (rc) {
131             LOGED(ERROR, domid, "Unable to rename stub-domain");
132             goto x_rc;
133         }
134     }
135 
136     if (our_trans) {
137         if (!xs_transaction_end(ctx->xsh, our_trans, 0)) {
138             trans = our_trans = 0;
139             if (errno != EAGAIN) {
140                 LOGD(ERROR, domid,
141                      "Failed to commit new name `%s'"
142                      " for domain previously named `%s'",
143                      new_name,
144                      old_name);
145                 goto x_fail;
146             }
147             LOGD(DEBUG, domid,
148                  "Need to retry rename transaction"
149                  " for domain (name_path=\"%s\", new_name=\"%s\")",
150                  name_path,
151                  new_name);
152             goto retry_transaction;
153         }
154         our_trans = 0;
155     }
156 
157     rc = 0;
158  x_rc:
159     if (our_trans) xs_transaction_end(ctx->xsh, our_trans, 1);
160     libxl_dominfo_dispose(&info);
161     return rc;
162 
163  x_fail:  rc = ERROR_FAIL;  goto x_rc;
164  x_nomem: rc = ERROR_NOMEM; goto x_rc;
165 }
166 
libxl_domain_rename(libxl_ctx * ctx,uint32_t domid,const char * old_name,const char * new_name)167 int libxl_domain_rename(libxl_ctx *ctx, uint32_t domid,
168                         const char *old_name, const char *new_name)
169 {
170     GC_INIT(ctx);
171     int rc;
172     rc = libxl__domain_rename(gc, domid, old_name, new_name, XBT_NULL);
173     GC_FREE;
174     return rc;
175 }
176 
177 static void domain_resume_done(libxl__egc *egc,
178                                libxl__dm_resume_state *,
179                                int rc);
180 
libxl_domain_resume(libxl_ctx * ctx,uint32_t domid,int suspend_cancel,const libxl_asyncop_how * ao_how)181 int libxl_domain_resume(libxl_ctx *ctx, uint32_t domid, int suspend_cancel,
182                         const libxl_asyncop_how *ao_how)
183 {
184     AO_CREATE(ctx, domid, ao_how);
185     libxl__dm_resume_state *dmrs;
186 
187     GCNEW(dmrs);
188     dmrs->ao = ao;
189     dmrs->domid = domid;
190     dmrs->callback = domain_resume_done;
191     libxl__domain_resume(egc, dmrs, suspend_cancel);
192     return AO_INPROGRESS;
193 }
194 
domain_resume_done(libxl__egc * egc,libxl__dm_resume_state * dmrs,int rc)195 static void domain_resume_done(libxl__egc *egc,
196                                libxl__dm_resume_state *dmrs,
197                                int rc)
198 {
199     STATE_AO_GC(dmrs->ao);
200     libxl__ao_complete(egc, ao, rc);
201 }
202 
203 /*
204  * Preserves a domain but rewrites xenstore etc to make it unique so
205  * that the domain can be restarted.
206  *
207  * Does not modify info so that it may be reused.
208  */
libxl_domain_preserve(libxl_ctx * ctx,uint32_t domid,libxl_domain_create_info * info,const char * name_suffix,libxl_uuid new_uuid)209 int libxl_domain_preserve(libxl_ctx *ctx, uint32_t domid,
210                           libxl_domain_create_info *info, const char *name_suffix, libxl_uuid new_uuid)
211 {
212     GC_INIT(ctx);
213     struct xs_permissions roperm[2];
214     xs_transaction_t t;
215     char *preserved_name;
216     char *uuid_string;
217     char *vm_path;
218     char *dom_path;
219 
220     int rc;
221 
222     preserved_name = GCSPRINTF("%s%s", info->name, name_suffix);
223     if (!preserved_name) {
224         GC_FREE;
225         return ERROR_NOMEM;
226     }
227 
228     uuid_string = libxl__uuid2string(gc, new_uuid);
229     if (!uuid_string) {
230         GC_FREE;
231         return ERROR_NOMEM;
232     }
233 
234     dom_path = libxl__xs_get_dompath(gc, domid);
235     if (!dom_path) {
236         GC_FREE;
237         return ERROR_FAIL;
238     }
239 
240     vm_path = GCSPRINTF("/vm/%s", uuid_string);
241     if (!vm_path) {
242         GC_FREE;
243         return ERROR_FAIL;
244     }
245 
246     roperm[0].id = 0;
247     roperm[0].perms = XS_PERM_NONE;
248     roperm[1].id = domid;
249     roperm[1].perms = XS_PERM_READ;
250 
251  retry_transaction:
252     t = xs_transaction_start(ctx->xsh);
253 
254     xs_rm(ctx->xsh, t, vm_path);
255     xs_mkdir(ctx->xsh, t, vm_path);
256     xs_set_permissions(ctx->xsh, t, vm_path, roperm, ARRAY_SIZE(roperm));
257 
258     xs_write(ctx->xsh, t, GCSPRINTF("%s/vm", dom_path), vm_path, strlen(vm_path));
259     rc = libxl__domain_rename(gc, domid, info->name, preserved_name, t);
260     if (rc) {
261         GC_FREE;
262         return rc;
263     }
264 
265     xs_write(ctx->xsh, t, GCSPRINTF("%s/uuid", vm_path), uuid_string, strlen(uuid_string));
266 
267     if (!xs_transaction_end(ctx->xsh, t, 0))
268         if (errno == EAGAIN)
269             goto retry_transaction;
270 
271     GC_FREE;
272     return 0;
273 }
274 
libxl__xcinfo2xlinfo(libxl_ctx * ctx,const xc_domaininfo_t * xcinfo,libxl_dominfo * xlinfo)275 void libxl__xcinfo2xlinfo(libxl_ctx *ctx,
276                           const xc_domaininfo_t *xcinfo,
277                           libxl_dominfo *xlinfo)
278 {
279     size_t size;
280 
281     memcpy(&(xlinfo->uuid), xcinfo->handle, sizeof(xen_domain_handle_t));
282     xlinfo->domid = xcinfo->domain;
283     xlinfo->ssidref = xcinfo->ssidref;
284     if (libxl_flask_sid_to_context(ctx, xlinfo->ssidref,
285                                    &xlinfo->ssid_label, &size) < 0)
286         xlinfo->ssid_label = NULL;
287 
288     xlinfo->dying      = !!(xcinfo->flags&XEN_DOMINF_dying);
289     xlinfo->shutdown   = !!(xcinfo->flags&XEN_DOMINF_shutdown);
290     xlinfo->paused     = !!(xcinfo->flags&XEN_DOMINF_paused);
291     xlinfo->blocked    = !!(xcinfo->flags&XEN_DOMINF_blocked);
292     xlinfo->running    = !!(xcinfo->flags&XEN_DOMINF_running);
293     xlinfo->never_stop = !!(xcinfo->flags&XEN_DOMINF_xs_domain);
294 
295     if (xlinfo->shutdown)
296         xlinfo->shutdown_reason = (xcinfo->flags>>XEN_DOMINF_shutdownshift) & XEN_DOMINF_shutdownmask;
297     else
298         xlinfo->shutdown_reason = LIBXL_SHUTDOWN_REASON_UNKNOWN;
299 
300     xlinfo->outstanding_memkb = PAGE_TO_MEMKB(xcinfo->outstanding_pages);
301     xlinfo->current_memkb = PAGE_TO_MEMKB(xcinfo->tot_pages);
302     xlinfo->shared_memkb = PAGE_TO_MEMKB(xcinfo->shr_pages);
303     xlinfo->paged_memkb = PAGE_TO_MEMKB(xcinfo->paged_pages);
304     xlinfo->max_memkb = PAGE_TO_MEMKB(xcinfo->max_pages);
305     xlinfo->cpu_time = xcinfo->cpu_time;
306     xlinfo->vcpu_max_id = xcinfo->max_vcpu_id;
307     xlinfo->vcpu_online = xcinfo->nr_online_vcpus;
308     xlinfo->cpupool = xcinfo->cpupool;
309     xlinfo->gpaddr_bits = xcinfo->gpaddr_bits;
310     xlinfo->domain_type = (xcinfo->flags & XEN_DOMINF_hvm_guest) ?
311         LIBXL_DOMAIN_TYPE_HVM : LIBXL_DOMAIN_TYPE_PV;
312 }
313 
libxl_list_domain(libxl_ctx * ctx,int * nb_domain_out)314 libxl_dominfo * libxl_list_domain(libxl_ctx *ctx, int *nb_domain_out)
315 {
316     libxl_dominfo *ptr = NULL;
317     int i, ret;
318     xc_domaininfo_t *info;
319     int size = 0;
320     uint32_t domid = 0;
321     GC_INIT(ctx);
322 
323     GCNEW_ARRAY(info, 1024);
324 
325     while ((ret = xc_domain_getinfolist(ctx->xch, domid, 1024, info)) > 0) {
326         ptr = libxl__realloc(NOGC, ptr, (size + ret) * sizeof(libxl_dominfo));
327         for (i = 0; i < ret; i++) {
328             libxl__xcinfo2xlinfo(ctx, &info[i], &ptr[size + i]);
329         }
330         domid = info[ret - 1].domain + 1;
331         size += ret;
332     }
333 
334     if (ret < 0) {
335         LOGE(ERROR, "getting domain info list");
336         free(ptr);
337         GC_FREE;
338         return NULL;
339     }
340 
341     *nb_domain_out = size;
342     GC_FREE;
343     return ptr;
344 }
345 
libxl_domain_info(libxl_ctx * ctx,libxl_dominfo * info_r,uint32_t domid)346 int libxl_domain_info(libxl_ctx *ctx, libxl_dominfo *info_r,
347                       uint32_t domid) {
348     xc_domaininfo_t xcinfo;
349     int ret;
350     GC_INIT(ctx);
351 
352     ret = xc_domain_getinfo_single(ctx->xch, domid, &xcinfo);
353     if (ret < 0) {
354         LOGED(ERROR, domid, "Getting domain info");
355         GC_FREE;
356         return errno == ESRCH ? ERROR_DOMAIN_NOTFOUND : ERROR_FAIL;
357     }
358 
359     if (info_r)
360         libxl__xcinfo2xlinfo(ctx, &xcinfo, info_r);
361     GC_FREE;
362     return 0;
363 }
364 
365 /* this API call only list VM running on this host. A VM can
366  * be an aggregate of multiple domains. */
libxl_list_vm(libxl_ctx * ctx,int * nb_vm_out)367 libxl_vminfo * libxl_list_vm(libxl_ctx *ctx, int *nb_vm_out)
368 {
369     GC_INIT(ctx);
370     libxl_dominfo *info;
371     libxl_vminfo *ptr = NULL;
372     int idx, i, n_doms;
373 
374     info = libxl_list_domain(ctx, &n_doms);
375     if (!info)
376         goto out;
377 
378     /*
379      * Always make sure to allocate at least one element; if we don't and we
380      * request zero, libxl__calloc (might) think its internal call to calloc
381      * has failed (if it returns null), if so it would kill our process.
382      */
383     ptr = libxl__calloc(NOGC, n_doms ? n_doms : 1, sizeof(libxl_vminfo));
384 
385     for (idx = i = 0; i < n_doms; i++) {
386         if (libxl_is_stubdom(ctx, info[i].domid, NULL))
387             continue;
388         ptr[idx].uuid = info[i].uuid;
389         ptr[idx].domid = info[i].domid;
390 
391         idx++;
392     }
393     *nb_vm_out = idx;
394     libxl_dominfo_list_free(info, n_doms);
395 
396 out:
397     GC_FREE;
398     return ptr;
399 }
400 
401 static void remus_failover_cb(libxl__egc *egc,
402                               libxl__domain_save_state *dss, int rc);
403 
libxl_domain_remus_start(libxl_ctx * ctx,libxl_domain_remus_info * info,uint32_t domid,int send_fd,int recv_fd,const libxl_asyncop_how * ao_how)404 int libxl_domain_remus_start(libxl_ctx *ctx, libxl_domain_remus_info *info,
405                              uint32_t domid, int send_fd, int recv_fd,
406                              const libxl_asyncop_how *ao_how)
407 {
408     AO_CREATE(ctx, domid, ao_how);
409     libxl__domain_save_state *dss;
410     int rc;
411 
412     libxl_domain_type type = libxl__domain_type(gc, domid);
413     if (type == LIBXL_DOMAIN_TYPE_INVALID) {
414         rc = ERROR_FAIL;
415         goto out;
416     }
417 
418     /* The caller must set this defbool */
419     if (libxl_defbool_is_default(info->colo)) {
420         LOGD(ERROR, domid, "Colo mode must be enabled/disabled");
421         rc = ERROR_FAIL;
422         goto out;
423     }
424 
425     libxl_defbool_setdefault(&info->allow_unsafe, false);
426     libxl_defbool_setdefault(&info->blackhole, false);
427     libxl_defbool_setdefault(&info->compression,
428                              !libxl_defbool_val(info->colo));
429     libxl_defbool_setdefault(&info->netbuf, true);
430     libxl_defbool_setdefault(&info->diskbuf, true);
431 
432     if (libxl_defbool_val(info->colo) &&
433         libxl_defbool_val(info->compression)) {
434             LOGD(ERROR, domid, "Cannot use memory checkpoint "
435                         "compression in COLO mode");
436             rc = ERROR_FAIL;
437             goto out;
438     }
439 
440     if (!libxl_defbool_val(info->allow_unsafe) &&
441         (libxl_defbool_val(info->blackhole) ||
442          !libxl_defbool_val(info->netbuf) ||
443          !libxl_defbool_val(info->diskbuf))) {
444         LOGD(ERROR, domid, "Unsafe mode must be enabled to replicate to /dev/null,"
445                     "disable network buffering and disk replication");
446         rc = ERROR_FAIL;
447         goto out;
448     }
449 
450 
451     GCNEW(dss);
452     dss->ao = ao;
453     dss->callback = remus_failover_cb;
454     dss->domid = domid;
455     dss->fd = send_fd;
456     dss->recv_fd = recv_fd;
457     dss->type = type;
458     dss->live = 1;
459     dss->debug = 0;
460     dss->remus = info;
461     if (libxl_defbool_val(info->colo))
462         dss->checkpointed_stream = LIBXL_CHECKPOINTED_STREAM_COLO;
463     else
464         dss->checkpointed_stream = LIBXL_CHECKPOINTED_STREAM_REMUS;
465 
466     assert(info);
467 
468     /* Point of no return */
469     if (libxl_defbool_val(info->colo))
470         libxl__colo_save_setup(egc, &dss->css);
471     else
472         libxl__remus_setup(egc, &dss->rs);
473     return AO_INPROGRESS;
474 
475  out:
476     return AO_CREATE_FAIL(rc);
477 }
478 
remus_failover_cb(libxl__egc * egc,libxl__domain_save_state * dss,int rc)479 static void remus_failover_cb(libxl__egc *egc,
480                               libxl__domain_save_state *dss, int rc)
481 {
482     STATE_AO_GC(dss->ao);
483     /*
484      * With Remus, if we reach this point, it means either
485      * backup died or some network error occurred preventing us
486      * from sending checkpoints.
487      */
488     libxl__ao_complete(egc, ao, rc);
489 }
490 
domain_suspend_cb(libxl__egc * egc,libxl__domain_save_state * dss,int rc)491 static void domain_suspend_cb(libxl__egc *egc,
492                               libxl__domain_save_state *dss, int rc)
493 {
494     STATE_AO_GC(dss->ao);
495     int flrc;
496 
497     flrc = libxl__fd_flags_restore(gc, dss->fd, dss->fdfl);
498     /* If suspend has failed already then report that error not this one. */
499     if (flrc && !rc) rc = flrc;
500 
501     libxl__ao_complete(egc,ao,rc);
502 
503 }
504 
libxl_domain_suspend(libxl_ctx * ctx,uint32_t domid,int fd,int flags,const libxl_asyncop_how * ao_how)505 int libxl_domain_suspend(libxl_ctx *ctx, uint32_t domid, int fd, int flags,
506                          const libxl_asyncop_how *ao_how)
507 {
508     AO_CREATE(ctx, domid, ao_how);
509     int rc;
510 
511     libxl_domain_type type = libxl__domain_type(gc, domid);
512     if (type == LIBXL_DOMAIN_TYPE_INVALID) {
513         rc = ERROR_FAIL;
514         goto out_err;
515     }
516 
517     libxl__domain_save_state *dss;
518     GCNEW(dss);
519 
520     dss->ao = ao;
521     dss->callback = domain_suspend_cb;
522 
523     dss->domid = domid;
524     dss->fd = fd;
525     dss->type = type;
526     dss->live = flags & LIBXL_SUSPEND_LIVE;
527     dss->debug = flags & LIBXL_SUSPEND_DEBUG;
528     dss->checkpointed_stream = LIBXL_CHECKPOINTED_STREAM_NONE;
529 
530     rc = libxl__fd_flags_modify_save(gc, dss->fd,
531                                      ~(O_NONBLOCK|O_NDELAY), 0,
532                                      &dss->fdfl);
533     if (rc < 0) goto out_err;
534 
535     libxl__domain_save(egc, dss);
536     return AO_INPROGRESS;
537 
538  out_err:
539     return AO_CREATE_FAIL(rc);
540 }
541 
domain_suspend_empty_cb(libxl__egc * egc,libxl__domain_suspend_state * dss,int rc)542 static void domain_suspend_empty_cb(libxl__egc *egc,
543                               libxl__domain_suspend_state *dss, int rc)
544 {
545     STATE_AO_GC(dss->ao);
546     libxl__ao_complete(egc,ao,rc);
547 }
548 
libxl_domain_suspend_only(libxl_ctx * ctx,uint32_t domid,const libxl_asyncop_how * ao_how)549 int libxl_domain_suspend_only(libxl_ctx *ctx, uint32_t domid,
550                               const libxl_asyncop_how *ao_how)
551 {
552     AO_CREATE(ctx, domid, ao_how);
553     libxl__domain_suspend_state *dsps;
554     int rc;
555 
556     libxl_domain_type type = libxl__domain_type(gc, domid);
557     if (type == LIBXL_DOMAIN_TYPE_INVALID) {
558         rc = ERROR_FAIL;
559         goto out_err;
560     }
561 
562     GCNEW(dsps);
563     dsps->ao = ao;
564     dsps->domid = domid;
565     dsps->type = type;
566     rc = libxl__domain_suspend_init(egc, dsps, type);
567     if (rc < 0) goto out_err;
568     dsps->callback_common_done = domain_suspend_empty_cb;
569     libxl__domain_suspend(egc, dsps);
570     return AO_INPROGRESS;
571 
572  out_err:
573     return AO_CREATE_FAIL(rc);
574 }
575 
libxl_domain_pause(libxl_ctx * ctx,uint32_t domid,const libxl_asyncop_how * ao_how)576 int libxl_domain_pause(libxl_ctx *ctx, uint32_t domid,
577                        const libxl_asyncop_how *ao_how)
578 {
579     AO_CREATE(ctx, domid, ao_how);
580     int r;
581     r = xc_domain_pause(ctx->xch, domid);
582     if (r < 0) {
583         LOGED(ERROR, domid, "Pausing domain");
584         return AO_CREATE_FAIL(ERROR_FAIL);
585     }
586     libxl__ao_complete(egc, ao, 0);
587     return AO_INPROGRESS;
588 }
589 
libxl_domain_core_dump(libxl_ctx * ctx,uint32_t domid,const char * filename,const libxl_asyncop_how * ao_how)590 int libxl_domain_core_dump(libxl_ctx *ctx, uint32_t domid,
591                            const char *filename,
592                            const libxl_asyncop_how *ao_how)
593 {
594     AO_CREATE(ctx, domid, ao_how);
595     int ret, rc;
596 
597     ret = xc_domain_dumpcore(ctx->xch, domid, filename);
598     if (ret<0) {
599         LOGED(ERROR, domid, "Core dumping domain to %s", filename);
600         rc = ERROR_FAIL;
601         goto out;
602     }
603 
604     rc = 0;
605 out:
606 
607     libxl__ao_complete(egc, ao, rc);
608 
609     return AO_INPROGRESS;
610 }
611 
libxl__domain_unpause_deprecated(libxl__gc * gc,libxl_domid domid)612 int libxl__domain_unpause_deprecated(libxl__gc *gc, libxl_domid domid)
613 {
614     int r, rc;
615 
616     libxl_domain_type type = libxl__domain_type(gc, domid);
617     if (type == LIBXL_DOMAIN_TYPE_INVALID) {
618         rc = ERROR_FAIL;
619         goto out;
620     }
621 
622     if (type == LIBXL_DOMAIN_TYPE_HVM) {
623         rc = libxl__domain_resume_device_model_deprecated(gc, domid);
624         if (rc < 0) {
625             LOGD(ERROR, domid,
626                  "Failed to unpause device model for domain: %d", rc);
627             goto out;
628         }
629     }
630     r = xc_domain_unpause(CTX->xch, domid);
631     if (r < 0) {
632         LOGED(ERROR, domid, "Unpausing domain");
633         rc = ERROR_FAIL;
634         goto out;
635     }
636     rc = 0;
637 out:
638     return rc;
639 }
640 
641 static void domain_unpause_done(libxl__egc *egc,
642                                 libxl__dm_resume_state *,
643                                 int rc);
644 
libxl__domain_unpause(libxl__egc * egc,libxl__dm_resume_state * dmrs)645 void libxl__domain_unpause(libxl__egc *egc,
646                            libxl__dm_resume_state *dmrs)
647 {
648     STATE_AO_GC(dmrs->ao);
649     int rc = 0;
650 
651     /* Convenience aliases */
652     libxl_domid domid = dmrs->domid;
653 
654     libxl_domain_type type = libxl__domain_type(gc, domid);
655     if (type == LIBXL_DOMAIN_TYPE_INVALID) {
656         rc = ERROR_FAIL;
657         goto out;
658     }
659 
660     if (type == LIBXL_DOMAIN_TYPE_HVM) {
661         dmrs->dm_resumed_callback = domain_unpause_done;
662         libxl__dm_resume(egc, dmrs); /* must be last */
663         return;
664     }
665     rc = 0;
666 out:
667     domain_unpause_done(egc, dmrs, rc);
668 }
669 
domain_unpause_done(libxl__egc * egc,libxl__dm_resume_state * dmrs,int rc)670 static void domain_unpause_done(libxl__egc *egc,
671                                 libxl__dm_resume_state *dmrs,
672                                 int rc)
673 {
674     EGC_GC;
675     int r;
676 
677     /* Convenience aliases */
678     libxl_domid domid = dmrs->domid;
679 
680     if (rc) goto out;
681 
682     r = xc_domain_unpause(CTX->xch, domid);
683     if (r < 0) {
684         LOGED(ERROR, domid, "Unpausing domain");
685         rc = ERROR_FAIL;
686         goto out;
687     }
688     rc = 0;
689 out:
690     dmrs->callback(egc, dmrs, rc);
691 }
692 
693 static void domain_unpause_ao_done(libxl__egc *egc,
694                                    libxl__dm_resume_state *,
695                                    int rc);
696 
libxl_domain_unpause(libxl_ctx * ctx,uint32_t domid,const libxl_asyncop_how * ao_how)697 int libxl_domain_unpause(libxl_ctx *ctx, uint32_t domid,
698                          const libxl_asyncop_how *ao_how)
699 {
700     AO_CREATE(ctx, domid, ao_how);
701     libxl__dm_resume_state *dmrs;
702 
703     GCNEW(dmrs);
704     dmrs->ao = ao;
705     dmrs->domid = domid;
706     dmrs->callback = domain_unpause_ao_done;
707     libxl__domain_unpause(egc, dmrs); /* must be last */
708     return AO_INPROGRESS;
709 }
710 
domain_unpause_ao_done(libxl__egc * egc,libxl__dm_resume_state * dmrs,int rc)711 static void domain_unpause_ao_done(libxl__egc *egc,
712                                    libxl__dm_resume_state *dmrs,
713                                    int rc)
714 {
715     STATE_AO_GC(dmrs->ao);
716 
717     libxl__ao_complete(egc, ao, rc);
718 }
719 
libxl__domain_pvcontrol_available(libxl__gc * gc,uint32_t domid)720 int libxl__domain_pvcontrol_available(libxl__gc *gc, uint32_t domid)
721 {
722     libxl_ctx *ctx = libxl__gc_owner(gc);
723 
724     uint64_t pvdriver = 0;
725     int ret;
726 
727     libxl_domain_type domtype = libxl__domain_type(gc, domid);
728     if (domtype == LIBXL_DOMAIN_TYPE_INVALID)
729         return ERROR_FAIL;
730 
731     if (domtype != LIBXL_DOMAIN_TYPE_HVM)
732         return 1;
733 
734     ret = xc_hvm_param_get(ctx->xch, domid, HVM_PARAM_CALLBACK_IRQ, &pvdriver);
735     if (ret<0) {
736         LOGED(ERROR, domid, "Getting HVM callback IRQ");
737         return ERROR_FAIL;
738     }
739     return !!pvdriver;
740 }
741 
libxl__domain_pvcontrol_xspath(libxl__gc * gc,uint32_t domid)742 const char *libxl__domain_pvcontrol_xspath(libxl__gc *gc, uint32_t domid)
743 {
744     const char *dom_path;
745 
746     dom_path = libxl__xs_get_dompath(gc, domid);
747     if (!dom_path)
748         return NULL;
749 
750     return GCSPRINTF("%s/control/shutdown", dom_path);
751 }
752 
libxl__domain_pvcontrol_read(libxl__gc * gc,xs_transaction_t t,uint32_t domid)753 char * libxl__domain_pvcontrol_read(libxl__gc *gc, xs_transaction_t t,
754                                     uint32_t domid)
755 {
756     const char *shutdown_path;
757 
758     shutdown_path = libxl__domain_pvcontrol_xspath(gc, domid);
759     if (!shutdown_path)
760         return NULL;
761 
762     return libxl__xs_read(gc, t, shutdown_path);
763 }
764 
libxl__domain_pvcontrol(libxl__egc * egc,libxl__xswait_state * pvcontrol,domid_t domid,const char * cmd)765 int libxl__domain_pvcontrol(libxl__egc *egc, libxl__xswait_state *pvcontrol,
766                             domid_t domid, const char *cmd)
767 {
768     STATE_AO_GC(pvcontrol->ao);
769     const char *shutdown_path;
770     int rc;
771 
772     rc = libxl__domain_pvcontrol_available(gc, domid);
773     if (rc < 0)
774         return rc;
775 
776     if (!rc)
777         return ERROR_NOPARAVIRT;
778 
779     shutdown_path = libxl__domain_pvcontrol_xspath(gc, domid);
780     if (!shutdown_path)
781         return ERROR_FAIL;
782 
783     rc = libxl__xs_printf(gc, XBT_NULL, shutdown_path, "%s", cmd);
784     if (rc)
785         return rc;
786 
787     pvcontrol->path = shutdown_path;
788     pvcontrol->what = GCSPRINTF("guest acknowledgement of %s request", cmd);
789     pvcontrol->timeout_ms = 60 * 1000;
790     rc = libxl__xswait_start(gc, pvcontrol);
791     if (rc)
792         return rc;
793 
794     return 0;
795 }
796 
pvcontrol_acked(const char * state)797 static bool pvcontrol_acked(const char *state)
798 {
799     if (!state || !strcmp(state,""))
800         return true;
801 
802     return false;
803 }
804 
805 /* Xenstore watch callback prototype for the reboot/poweroff operations. */
806 static void pvcontrol_cb(libxl__egc *egc, libxl__xswait_state *xswa, int rc,
807                          const char *state);
808 
libxl_domain_shutdown(libxl_ctx * ctx,uint32_t domid,const libxl_asyncop_how * ao_how)809 int libxl_domain_shutdown(libxl_ctx *ctx, uint32_t domid,
810                           const libxl_asyncop_how *ao_how)
811 {
812     AO_CREATE(ctx, domid, ao_how);
813     libxl__xswait_state *pvcontrol;
814     int rc;
815 
816     GCNEW(pvcontrol);
817     pvcontrol->ao = ao;
818     pvcontrol->callback = pvcontrol_cb;
819     rc = libxl__domain_pvcontrol(egc, pvcontrol, domid, "poweroff");
820 
821     return rc ? AO_CREATE_FAIL(rc) : AO_INPROGRESS;
822 }
823 
libxl_domain_reboot(libxl_ctx * ctx,uint32_t domid,const libxl_asyncop_how * ao_how)824 int libxl_domain_reboot(libxl_ctx *ctx, uint32_t domid,
825                         const libxl_asyncop_how *ao_how)
826 {
827     AO_CREATE(ctx, domid, ao_how);
828     libxl__xswait_state *pvcontrol;
829     int rc;
830 
831     GCNEW(pvcontrol);
832     pvcontrol->ao = ao;
833     pvcontrol->callback = pvcontrol_cb;
834     rc = libxl__domain_pvcontrol(egc, pvcontrol, domid, "reboot");
835 
836     return rc ? AO_CREATE_FAIL(rc) : AO_INPROGRESS;
837 }
838 
pvcontrol_cb(libxl__egc * egc,libxl__xswait_state * xswa,int rc,const char * state)839 static void pvcontrol_cb(libxl__egc *egc, libxl__xswait_state *xswa, int rc,
840                          const char *state)
841 {
842     STATE_AO_GC(xswa->ao);
843 
844     if (!rc && !pvcontrol_acked(state))
845         return;
846 
847     libxl__xswait_stop(gc, xswa);
848 
849     if (rc)
850         LOG(ERROR, "guest didn't acknowledge control request: %d", rc);
851 
852     libxl__ao_complete(egc, ao, rc);
853 }
854 
domain_death_occurred(libxl__egc * egc,libxl_evgen_domain_death ** evg_upd,const char * why)855 static void domain_death_occurred(libxl__egc *egc,
856                                   libxl_evgen_domain_death **evg_upd,
857                                   const char *why) {
858     /* Removes **evg_upd from death_list and puts it on death_reported
859      * and advances *evg_upd to the next entry.
860      * Call sites in domain_death_xswatch_callback must use "continue". */
861     EGC_GC;
862     libxl_evgen_domain_death *const evg = *evg_upd;
863 
864     LOGD(DEBUG, evg->domid, "%s", why);
865 
866     libxl_evgen_domain_death *evg_next = XEN_TAILQ_NEXT(evg, entry);
867     *evg_upd = evg_next;
868 
869     libxl_event *ev = NEW_EVENT(egc, DOMAIN_DEATH, evg->domid, evg->user);
870 
871     libxl__event_occurred(egc, ev);
872 
873     evg->death_reported = 1;
874     XEN_TAILQ_REMOVE(&CTX->death_list, evg, entry);
875     XEN_TAILQ_INSERT_HEAD(&CTX->death_reported, evg, entry);
876 }
877 
domain_death_xswatch_callback(libxl__egc * egc,libxl__ev_xswatch * w,const char * wpath,const char * epath)878 static void domain_death_xswatch_callback(libxl__egc *egc, libxl__ev_xswatch *w,
879                                         const char *wpath, const char *epath) {
880     EGC_GC;
881     libxl_evgen_domain_death *evg;
882     int rc;
883 
884     CTX_LOCK;
885 
886     evg = XEN_TAILQ_FIRST(&CTX->death_list);
887 
888     for (;;) {
889         if (!evg) goto out;
890 
891         int nentries = XEN_TAILQ_NEXT(evg, entry) ? 200 : 1;
892         xc_domaininfo_t domaininfos[nentries];
893         const xc_domaininfo_t *got = domaininfos, *gotend;
894 
895         rc = xc_domain_getinfolist(CTX->xch, evg->domid, nentries, domaininfos);
896         if (rc == -1) {
897             LIBXL__EVENT_DISASTER(gc, "xc_domain_getinfolist failed while"
898                                   " processing @releaseDomain watch event",
899                                   errno, 0);
900             goto out;
901         }
902         gotend = &domaininfos[rc];
903 
904         LOGD(DEBUG, evg->domid, "[evg=%p] nentries=%d rc=%d %ld..%ld",
905              evg, nentries, rc,
906              rc>0 ? (long)domaininfos[0].domain : 0,
907              rc>0 ? (long)domaininfos[rc-1].domain : 0);
908 
909         for (;;) {
910             if (!evg) {
911                 LOG(DEBUG, "[evg=0] all reported");
912                 goto all_reported;
913             }
914 
915             LOGD(DEBUG, evg->domid, "[evg=%p]"
916                  "   got=domaininfos[%d] got->domain=%ld",
917                  evg, (int)(got - domaininfos),
918                  got < gotend ? (long)got->domain : -1L);
919 
920             if (!rc) {
921                 domain_death_occurred(egc, &evg, "empty list");
922                 continue;
923             }
924 
925             if (got == gotend) {
926                 LOG(DEBUG, " got==gotend");
927                 break;
928             }
929 
930             if (got->domain > evg->domid) {
931                 /* ie, the list doesn't contain evg->domid any more so
932                  * the domain has been destroyed */
933                 domain_death_occurred(egc, &evg, "missing from list");
934                 continue;
935             }
936 
937             if (got->domain < evg->domid) {
938                 got++;
939                 continue;
940             }
941 
942             assert(evg->domid == got->domain);
943             LOGD(DEBUG, evg->domid, "Exists shutdown_reported=%d"" dominf.flags=%x",
944                  evg->shutdown_reported, got->flags);
945 
946             if (got->flags & XEN_DOMINF_dying) {
947                 domain_death_occurred(egc, &evg, "dying");
948                 continue;
949             }
950 
951             if (!evg->shutdown_reported &&
952                 (got->flags & XEN_DOMINF_shutdown)) {
953                 libxl_event *ev = NEW_EVENT(egc, DOMAIN_SHUTDOWN,
954                                             got->domain, evg->user);
955 
956                 LOG(DEBUG, " shutdown reporting");
957 
958                 ev->u.domain_shutdown.shutdown_reason =
959                     (got->flags >> XEN_DOMINF_shutdownshift) &
960                     XEN_DOMINF_shutdownmask;
961                 libxl__event_occurred(egc, ev);
962 
963                 evg->shutdown_reported = 1;
964             }
965             evg = XEN_TAILQ_NEXT(evg, entry);
966         }
967 
968         assert(rc); /* rc==0 results in us eating all evgs and quitting */
969     }
970  all_reported:
971  out:
972 
973     LOG(DEBUG, "domain death search done");
974 
975     CTX_UNLOCK;
976 }
977 
libxl_evenable_domain_death(libxl_ctx * ctx,uint32_t domid,libxl_ev_user user,libxl_evgen_domain_death ** evgen_out)978 int libxl_evenable_domain_death(libxl_ctx *ctx, uint32_t domid,
979                 libxl_ev_user user, libxl_evgen_domain_death **evgen_out) {
980     GC_INIT(ctx);
981     libxl_evgen_domain_death *evg, *evg_search;
982     int rc;
983 
984     CTX_LOCK;
985 
986     evg = malloc(sizeof(*evg));  if (!evg) { rc = ERROR_NOMEM; goto out; }
987     memset(evg, 0, sizeof(*evg));
988     evg->domid = domid;
989     evg->user = user;
990 
991     LIBXL_TAILQ_INSERT_SORTED(&ctx->death_list, entry, evg, evg_search, ,
992                               evg->domid > evg_search->domid);
993 
994     if (!libxl__ev_xswatch_isregistered(&ctx->death_watch)) {
995         rc = libxl__ev_xswatch_register(gc, &ctx->death_watch,
996                         domain_death_xswatch_callback, "@releaseDomain");
997         if (rc) { libxl__evdisable_domain_death(gc, evg); goto out; }
998     }
999 
1000     *evgen_out = evg;
1001     rc = 0;
1002 
1003  out:
1004     CTX_UNLOCK;
1005     GC_FREE;
1006     return rc;
1007 };
1008 
libxl__evdisable_domain_death(libxl__gc * gc,libxl_evgen_domain_death * evg)1009 void libxl__evdisable_domain_death(libxl__gc *gc,
1010                                    libxl_evgen_domain_death *evg) {
1011     CTX_LOCK;
1012 
1013     if (!evg->death_reported)
1014         XEN_TAILQ_REMOVE(&CTX->death_list, evg, entry);
1015     else
1016         XEN_TAILQ_REMOVE(&CTX->death_reported, evg, entry);
1017 
1018     free(evg);
1019 
1020     if (!XEN_TAILQ_FIRST(&CTX->death_list) &&
1021         libxl__ev_xswatch_isregistered(&CTX->death_watch))
1022         libxl__ev_xswatch_deregister(gc, &CTX->death_watch);
1023 
1024     CTX_UNLOCK;
1025 }
1026 
libxl_evdisable_domain_death(libxl_ctx * ctx,libxl_evgen_domain_death * evg)1027 void libxl_evdisable_domain_death(libxl_ctx *ctx,
1028                                   libxl_evgen_domain_death *evg) {
1029     GC_INIT(ctx);
1030     libxl__evdisable_domain_death(gc, evg);
1031     GC_FREE;
1032 }
1033 
1034 /* Callbacks for libxl_domain_destroy */
1035 
1036 static void domain_destroy_cb(libxl__egc *egc, libxl__domain_destroy_state *dds,
1037                               int rc);
1038 
libxl_domain_destroy(libxl_ctx * ctx,uint32_t domid,const libxl_asyncop_how * ao_how)1039 int libxl_domain_destroy(libxl_ctx *ctx, uint32_t domid,
1040                          const libxl_asyncop_how *ao_how)
1041 {
1042     AO_CREATE(ctx, domid, ao_how);
1043     libxl__domain_destroy_state *dds;
1044 
1045     GCNEW(dds);
1046     dds->ao = ao;
1047     dds->domid = domid;
1048     dds->callback = domain_destroy_cb;
1049     libxl__domain_destroy(egc, dds);
1050 
1051     return AO_INPROGRESS;
1052 }
1053 
domain_destroy_cb(libxl__egc * egc,libxl__domain_destroy_state * dds,int rc)1054 static void domain_destroy_cb(libxl__egc *egc, libxl__domain_destroy_state *dds,
1055                               int rc)
1056 {
1057     STATE_AO_GC(dds->ao);
1058 
1059     if (rc)
1060         LOGD(ERROR, dds->domid, "Destruction of domain failed");
1061 
1062     libxl__ao_complete(egc, ao, rc);
1063 }
1064 
1065 /* Callbacks for libxl__domain_destroy */
1066 
1067 static void stubdom_destroy_callback(libxl__egc *egc,
1068                                      libxl__destroy_domid_state *dis,
1069                                      int rc);
1070 
1071 static void domain_destroy_callback(libxl__egc *egc,
1072                                     libxl__destroy_domid_state *dis,
1073                                     int rc);
1074 
1075 static void destroy_finish_check(libxl__egc *egc,
1076                                  libxl__domain_destroy_state *dds);
1077 
libxl__domain_destroy(libxl__egc * egc,libxl__domain_destroy_state * dds)1078 void libxl__domain_destroy(libxl__egc *egc, libxl__domain_destroy_state *dds)
1079 {
1080     STATE_AO_GC(dds->ao);
1081     uint32_t stubdomid = libxl_get_stubdom_id(CTX, dds->domid);
1082 
1083     if (stubdomid) {
1084         dds->stubdom.ao = ao;
1085         dds->stubdom.domid = stubdomid;
1086         dds->stubdom.callback = stubdom_destroy_callback;
1087         dds->stubdom.soft_reset = false;
1088         libxl__destroy_domid(egc, &dds->stubdom);
1089     } else {
1090         dds->stubdom_finished = 1;
1091     }
1092 
1093     dds->domain.ao = ao;
1094     dds->domain.domid = dds->domid;
1095     dds->domain.callback = domain_destroy_callback;
1096     dds->domain.soft_reset = dds->soft_reset;
1097     libxl__destroy_domid(egc, &dds->domain);
1098 }
1099 
stubdom_destroy_callback(libxl__egc * egc,libxl__destroy_domid_state * dis,int rc)1100 static void stubdom_destroy_callback(libxl__egc *egc,
1101                                      libxl__destroy_domid_state *dis,
1102                                      int rc)
1103 {
1104     STATE_AO_GC(dis->ao);
1105     libxl__domain_destroy_state *dds = CONTAINER_OF(dis, *dds, stubdom);
1106     const char *savefile;
1107 
1108     if (rc) {
1109         LOGD(ERROR, dds->domain.domid, "Unable to destroy stubdom with domid %u",
1110              dis->domid);
1111         dds->rc = rc;
1112     }
1113 
1114     dds->stubdom_finished = 1;
1115     savefile = libxl__device_model_savefile(gc, dis->domid);
1116     rc = libxl__remove_file(gc, savefile);
1117     if (rc) {
1118         LOGD(ERROR, dds->domain.domid, "Failed to remove device-model savefile %s",
1119              savefile);
1120     }
1121 
1122     destroy_finish_check(egc, dds);
1123 }
1124 
domain_destroy_callback(libxl__egc * egc,libxl__destroy_domid_state * dis,int rc)1125 static void domain_destroy_callback(libxl__egc *egc,
1126                                     libxl__destroy_domid_state *dis,
1127                                     int rc)
1128 {
1129     STATE_AO_GC(dis->ao);
1130     libxl__domain_destroy_state *dds = CONTAINER_OF(dis, *dds, domain);
1131 
1132     if (rc) {
1133         LOGD(ERROR, dis->domid, "Unable to destroy guest");
1134         dds->rc = rc;
1135     }
1136 
1137     dds->domain_finished = 1;
1138     destroy_finish_check(egc, dds);
1139 }
1140 
destroy_finish_check(libxl__egc * egc,libxl__domain_destroy_state * dds)1141 static void destroy_finish_check(libxl__egc *egc,
1142                                  libxl__domain_destroy_state *dds)
1143 {
1144     if (!(dds->domain_finished && dds->stubdom_finished))
1145         return;
1146 
1147     dds->callback(egc, dds, dds->rc);
1148 }
1149 
1150 /* Callbacks for libxl__destroy_domid */
1151 static void destroy_domid_pci_done(libxl__egc *egc,
1152                                    libxl__multidev *multidev,
1153                                    int rc);
1154 static void dm_destroy_cb(libxl__egc *egc,
1155                           libxl__destroy_devicemodel_state *ddms,
1156                           int rc);
1157 
1158 static void devices_destroy_cb(libxl__egc *egc,
1159                                libxl__devices_remove_state *drs,
1160                                int rc);
1161 
1162 static void domain_destroy_domid_cb(libxl__egc *egc,
1163                                     libxl__ev_child *destroyer,
1164                                     pid_t pid, int status);
1165 
libxl__destroy_domid(libxl__egc * egc,libxl__destroy_domid_state * dis)1166 void libxl__destroy_domid(libxl__egc *egc, libxl__destroy_domid_state *dis)
1167 {
1168     STATE_AO_GC(dis->ao);
1169     uint32_t domid = dis->domid;
1170     int rc;
1171 
1172     libxl__ev_child_init(&dis->destroyer);
1173 
1174     rc = libxl_domain_info(CTX, NULL, domid);
1175     switch(rc) {
1176     case 0:
1177         break;
1178     case ERROR_DOMAIN_NOTFOUND:
1179         LOGD(ERROR, domid, "Non-existant domain");
1180     default:
1181         goto out;
1182     }
1183 
1184     libxl__multidev_begin(ao, &dis->multidev);
1185     dis->multidev.callback = destroy_domid_pci_done;
1186     libxl__device_pci_destroy_all(egc, domid, &dis->multidev);
1187     libxl__multidev_prepared(egc, &dis->multidev, 0);
1188     return;
1189 
1190 out:
1191     assert(rc);
1192     dis->callback(egc, dis, rc);
1193 }
1194 
destroy_domid_pci_done(libxl__egc * egc,libxl__multidev * multidev,int rc)1195 static void destroy_domid_pci_done(libxl__egc *egc,
1196                                    libxl__multidev *multidev,
1197                                    int rc)
1198 {
1199     STATE_AO_GC(multidev->ao);
1200     libxl__destroy_domid_state *dis =
1201         CONTAINER_OF(multidev, *dis, multidev);
1202     int dm_present;
1203     int r;
1204 
1205     /* Convenience aliases */
1206     libxl_domid domid = dis->domid;
1207 
1208     if (rc) {
1209         LOGD(ERROR, domid, "Pci shutdown failed");
1210         goto out;
1211     }
1212 
1213     r = xc_domain_pause(CTX->xch, domid);
1214     if (r < 0) {
1215         LOGEVD(ERROR, r, domid, "xc_domain_pause failed");
1216         rc = ERROR_FAIL;
1217     }
1218 
1219     switch (libxl__domain_type(gc, domid)) {
1220     case LIBXL_DOMAIN_TYPE_HVM:
1221         if (libxl_get_stubdom_id(CTX, domid)) {
1222             dm_present = 0;
1223             break;
1224         }
1225         /* fall through */
1226     case LIBXL_DOMAIN_TYPE_PVH:
1227     case LIBXL_DOMAIN_TYPE_PV:
1228         dm_present = libxl__dm_active(gc, domid);
1229         break;
1230     case LIBXL_DOMAIN_TYPE_INVALID:
1231         rc = ERROR_FAIL;
1232         goto out;
1233     default:
1234         abort();
1235     }
1236 
1237     if (dm_present) {
1238         dis->ddms.ao = ao;
1239         dis->ddms.domid = domid;
1240         dis->ddms.callback = dm_destroy_cb;
1241 
1242         libxl__destroy_device_model(egc, &dis->ddms);
1243         return;
1244     } else {
1245         dm_destroy_cb(egc, &dis->ddms, 0);
1246         return;
1247     }
1248 
1249 out:
1250     assert(rc);
1251     dis->callback(egc, dis, rc);
1252     return;
1253 }
1254 
dm_destroy_cb(libxl__egc * egc,libxl__destroy_devicemodel_state * ddms,int rc)1255 static void dm_destroy_cb(libxl__egc *egc,
1256                           libxl__destroy_devicemodel_state *ddms,
1257                           int rc)
1258 {
1259     libxl__destroy_domid_state *dis = CONTAINER_OF(ddms, *dis, ddms);
1260     STATE_AO_GC(dis->ao);
1261     uint32_t domid = dis->domid;
1262     uint32_t target_domid;
1263 
1264     if (rc < 0)
1265         LOGD(ERROR, domid, "libxl__destroy_device_model failed");
1266 
1267     if (libxl_is_stubdom(CTX, domid, &target_domid) &&
1268         libxl__stubdomain_is_linux_running(gc, target_domid)) {
1269         char *path = GCSPRINTF("/local/domain/%d/image/qmp-proxy-pid", domid);
1270 
1271         libxl__kill_xs_path(gc, path, "QMP Proxy");
1272         /* qmp-proxy for stubdom registers target_domid's QMP sockets. */
1273         libxl__qmp_cleanup(gc, target_domid);
1274     }
1275 
1276     dis->drs.ao = ao;
1277     dis->drs.domid = domid;
1278     dis->drs.callback = devices_destroy_cb;
1279     dis->drs.force.flag = LIBXL__FORCE_ON;
1280     libxl__devices_destroy(egc, &dis->drs);
1281 }
1282 
libxl__get_domid_reuse_timeout(void)1283 static unsigned int libxl__get_domid_reuse_timeout(void)
1284 {
1285     const char *env_timeout = getenv("LIBXL_DOMID_REUSE_TIMEOUT");
1286 
1287     return env_timeout ? strtol(env_timeout, NULL, 0) :
1288         LIBXL_DOMID_REUSE_TIMEOUT;
1289 }
1290 
libxl__domid_history_path(libxl__gc * gc,const char * suffix)1291 char *libxl__domid_history_path(libxl__gc *gc, const char *suffix)
1292 {
1293     return GCSPRINTF("%s/domid-history%s", libxl__run_dir_path(),
1294                      suffix ?: "");
1295 }
1296 
libxl_clear_domid_history(libxl_ctx * ctx)1297 int libxl_clear_domid_history(libxl_ctx *ctx)
1298 {
1299     GC_INIT(ctx);
1300     char *path;
1301     int rc = ERROR_FAIL;
1302 
1303     path = libxl__domid_history_path(gc, NULL);
1304     if (!path)
1305         goto out;
1306 
1307     if (unlink(path) < 0 && errno != ENOENT) {
1308         LOGE(ERROR, "failed to remove '%s'\n", path);
1309         goto out;
1310     }
1311 
1312     rc = 0;
1313 
1314 out:
1315     GC_FREE;
1316     return rc;
1317 }
1318 
1319 struct libxl__domid_history {
1320     long timeout;
1321     char *path;
1322     FILE *f;
1323     struct timespec ts;
1324 };
1325 
libxl__domid_history_dispose(struct libxl__domid_history * ctxt)1326 static void libxl__domid_history_dispose(
1327     struct libxl__domid_history *ctxt)
1328 {
1329     if (ctxt->f) {
1330         fclose(ctxt->f);
1331         ctxt->f = NULL;
1332     }
1333 }
1334 
libxl__open_domid_history(libxl__gc * gc,struct libxl__domid_history * ctxt)1335 static int libxl__open_domid_history(libxl__gc *gc,
1336                                      struct libxl__domid_history *ctxt)
1337 {
1338     ctxt->timeout = libxl__get_domid_reuse_timeout();
1339     ctxt->path = libxl__domid_history_path(gc, NULL);
1340 
1341     ctxt->f = fopen(ctxt->path, "r");
1342     if (!ctxt->f && errno != ENOENT) {
1343         LOGE(ERROR, "failed to open '%s'", ctxt->path);
1344         return ERROR_FAIL;
1345     }
1346 
1347     if (clock_gettime(CLOCK_MONOTONIC, &ctxt->ts)) {
1348         LOGE(ERROR, "failed to get time");
1349         libxl__domid_history_dispose(ctxt);
1350         return ERROR_FAIL;
1351     }
1352 
1353     return 0;
1354 }
1355 
libxl__close_domid_history(libxl__gc * gc,struct libxl__domid_history * ctxt)1356 static int libxl__close_domid_history(libxl__gc *gc,
1357                                       struct libxl__domid_history *ctxt)
1358 {
1359     int r;
1360 
1361     if (!ctxt->f) return 0;
1362 
1363     r = fclose(ctxt->f);
1364     ctxt->f = NULL;
1365     if (r == EOF) {
1366         LOGE(ERROR, "failed to close '%s'", ctxt->path);
1367         return ERROR_FAIL;
1368     }
1369 
1370     return 0;
1371 }
1372 
libxl__read_recent(libxl__gc * gc,struct libxl__domid_history * ctxt,unsigned long * sec,unsigned int * domid)1373 static int libxl__read_recent(libxl__gc *gc,
1374                               struct libxl__domid_history *ctxt,
1375                               unsigned long *sec, unsigned int *domid)
1376 {
1377     if (!ctxt->f) {
1378         *domid = INVALID_DOMID;
1379         return 0;
1380     }
1381 
1382     for (;;) {
1383         int r = fscanf(ctxt->f, "%lu %u", sec, domid);
1384 
1385         if (r == EOF) {
1386             if (ferror(ctxt->f)) {
1387                 LOGE(ERROR, "failed to read from '%s'", ctxt->path);
1388                 return ERROR_FAIL;
1389             }
1390 
1391             *domid = INVALID_DOMID;
1392             break;
1393         } else if (r == 2 && libxl_domid_valid_guest(*domid) &&
1394                    ctxt->ts.tv_sec - *sec <= ctxt->timeout) {
1395             break;
1396         }
1397     }
1398 
1399     return 0;
1400 }
1401 
libxl__mark_domid_recent(libxl__gc * gc,uint32_t domid)1402 static int libxl__mark_domid_recent(libxl__gc *gc, uint32_t domid)
1403 {
1404     libxl__flock *lock;
1405     struct libxl__domid_history ctxt = {};
1406     char *new;
1407     FILE *nf = NULL;
1408     int r, rc;
1409 
1410     lock = libxl__lock_domid_history(gc);
1411     if (!lock) {
1412         LOGED(ERROR, domid, "failed to acquire lock");
1413         rc = ERROR_FAIL;
1414         goto out;
1415     }
1416 
1417     rc = libxl__open_domid_history(gc, &ctxt);
1418     if (rc) goto out;
1419 
1420     new = libxl__domid_history_path(gc, ".new");
1421     nf = fopen(new, "a");
1422     if (!nf) {
1423         LOGED(ERROR, domid, "failed to open '%s'", new);
1424         goto out;
1425     }
1426 
1427     for (;;) {
1428         unsigned long sec;
1429         unsigned int val;
1430 
1431         rc = libxl__read_recent(gc, &ctxt, &sec, &val);
1432         if (rc) goto out;
1433 
1434         if (val == INVALID_DOMID) /* EOF */
1435             break;
1436 
1437         r = fprintf(nf, "%lu %u\n", sec, val);
1438         if (r < 0) {
1439             LOGED(ERROR, domid, "failed to write to '%s'", new);
1440             goto out;
1441         }
1442     }
1443 
1444     r = fprintf(nf, "%jd %u\n", (intmax_t)ctxt.ts.tv_sec, domid);
1445     if (r < 0) {
1446         LOGED(ERROR, domid, "failed to write to '%s'", new);
1447         goto out;
1448     }
1449 
1450     r = fclose(nf);
1451     nf = NULL;
1452     if (r == EOF) {
1453         LOGED(ERROR, domid, "failed to close '%s'", new);
1454         goto out;
1455     }
1456 
1457     rc = libxl__close_domid_history(gc, &ctxt);
1458     if (rc) goto out;
1459 
1460     r = rename(new, ctxt.path);
1461     if (r) {
1462         LOGE(ERROR, "failed to rename '%s' -> '%s'", new, ctxt.path);
1463         return ERROR_FAIL;
1464     }
1465 
1466 out:
1467     if (nf) fclose(nf);
1468     libxl__domid_history_dispose(&ctxt);
1469     if (lock) libxl__unlock_file(lock);
1470 
1471     return rc;
1472 }
1473 
libxl__is_domid_recent(libxl__gc * gc,uint32_t domid,bool * recent)1474 int libxl__is_domid_recent(libxl__gc *gc, uint32_t domid, bool *recent)
1475 {
1476     struct libxl__domid_history ctxt = {};
1477     int rc;
1478 
1479     rc = libxl__open_domid_history(gc, &ctxt);
1480     if (rc) goto out;
1481 
1482     *recent = false;
1483     for (;;) {
1484         unsigned long sec;
1485         unsigned int val;
1486 
1487         rc = libxl__read_recent(gc, &ctxt, &sec, &val);
1488         if (rc) goto out;
1489 
1490         if (val == INVALID_DOMID) /* EOF */
1491             break;
1492 
1493         if (val == domid && ctxt.ts.tv_sec - sec <= ctxt.timeout) {
1494             *recent = true;
1495             break;
1496         }
1497     }
1498 
1499     rc = libxl__close_domid_history(gc, &ctxt);
1500 
1501 out:
1502     libxl__domid_history_dispose(&ctxt);
1503 
1504     return rc;
1505 }
1506 
devices_destroy_cb(libxl__egc * egc,libxl__devices_remove_state * drs,int rc)1507 static void devices_destroy_cb(libxl__egc *egc,
1508                                libxl__devices_remove_state *drs,
1509                                int rc)
1510 {
1511     STATE_AO_GC(drs->ao);
1512     libxl__destroy_domid_state *dis = CONTAINER_OF(drs, *dis, drs);
1513     libxl_ctx *ctx = CTX;
1514     uint32_t domid = dis->domid;
1515     char *dom_path;
1516     char *vm_path;
1517     libxl__flock *lock;
1518 
1519     dom_path = libxl__xs_get_dompath(gc, domid);
1520     if (!dom_path) {
1521         rc = ERROR_FAIL;
1522         goto out;
1523     }
1524 
1525     if (rc < 0)
1526         LOGD(ERROR, domid, "libxl__devices_destroy failed");
1527 
1528     /* Remove the file after the hotplug scripts have run.  The scripts won't
1529      * run if the file doesn't exist when they are run.  */
1530     libxl__remove_file(gc, GCSPRINTF(LIBXL_STUBDOM_EMPTY_CDROM ".%u", domid));
1531 
1532     vm_path = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/vm", dom_path));
1533     if (vm_path)
1534         if (!xs_rm(ctx->xsh, XBT_NULL, vm_path))
1535             LOGED(ERROR, domid, "xs_rm failed for %s", vm_path);
1536 
1537     if (!xs_rm(ctx->xsh, XBT_NULL, dom_path))
1538         LOGED(ERROR, domid, "xs_rm failed for %s", dom_path);
1539 
1540     xs_rm(ctx->xsh, XBT_NULL, libxl__xs_libxl_path(gc, domid));
1541     xs_rm(ctx->xsh, XBT_NULL, GCSPRINTF( "/local/domain/%d/hvmloader", domid));
1542 
1543     /* This is async operation, we already hold CTX lock */
1544     lock = libxl__lock_domain_userdata(gc, domid);
1545     if (!lock) {
1546         rc = ERROR_LOCK_FAIL;
1547         goto out;
1548     }
1549     libxl__userdata_destroyall(gc, domid);
1550 
1551     libxl__unlock_file(lock);
1552 
1553     /* Clean up qemu-save and qemu-resume files. They are
1554      * intermediate files created by libxc. Unfortunately they
1555      * don't fit in existing userdata scheme very well. In soft reset
1556      * case we need to keep the file.
1557      */
1558     if (!dis->soft_reset) {
1559         rc = libxl__remove_file(gc,
1560                                 libxl__device_model_savefile(gc, domid));
1561         if (rc < 0) goto out;
1562     }
1563     rc = libxl__remove_file(gc,
1564              GCSPRINTF(LIBXL_DEVICE_MODEL_RESTORE_FILE".%u", domid));
1565     if (rc < 0) goto out;
1566 
1567     rc = libxl__ev_child_fork(gc, &dis->destroyer, domain_destroy_domid_cb);
1568     if (rc < 0) goto out;
1569     if (!rc) { /* child */
1570         ctx->xch = xc_interface_open(ctx->lg,0,0);
1571         if (!ctx->xch) goto badchild;
1572 
1573         if (!dis->soft_reset) {
1574             rc = libxl__mark_domid_recent(gc, domid);
1575             if (rc) goto badchild;
1576             rc = xc_domain_destroy(ctx->xch, domid);
1577         } else {
1578             rc = xc_domain_pause(ctx->xch, domid);
1579             if (rc < 0) goto badchild;
1580             rc = xc_domain_soft_reset(ctx->xch, domid);
1581             if (rc < 0) goto badchild;
1582             rc = xc_domain_unpause(ctx->xch, domid);
1583         }
1584         if (rc < 0) goto badchild;
1585         _exit(0);
1586 
1587     badchild:
1588         if (errno > 0  && errno < 126) {
1589             _exit(errno);
1590         } else {
1591             LOGED(ERROR, domid,
1592  "xc_domain_destroy failed (with difficult errno value %d)",
1593                   errno);
1594             _exit(-1);
1595         }
1596     }
1597     LOGD(DEBUG, domid, "Forked pid %ld for destroy of domain", (long)rc);
1598 
1599     return;
1600 
1601 out:
1602     dis->callback(egc, dis, rc);
1603     return;
1604 }
1605 
domain_destroy_domid_cb(libxl__egc * egc,libxl__ev_child * destroyer,pid_t pid,int status)1606 static void domain_destroy_domid_cb(libxl__egc *egc,
1607                                     libxl__ev_child *destroyer,
1608                                     pid_t pid, int status)
1609 {
1610     libxl__destroy_domid_state *dis = CONTAINER_OF(destroyer, *dis, destroyer);
1611     STATE_AO_GC(dis->ao);
1612     int rc;
1613 
1614     if (status) {
1615         if (WIFEXITED(status) && WEXITSTATUS(status)<126) {
1616             LOGEVD(ERROR, WEXITSTATUS(status), dis->domid,
1617                    "xc_domain_destroy failed");
1618         } else {
1619             libxl_report_child_exitstatus(CTX, XTL_ERROR,
1620                                           "async domain destroy", pid, status);
1621         }
1622         rc = ERROR_FAIL;
1623         goto out;
1624     }
1625     rc = 0;
1626 
1627  out:
1628     dis->callback(egc, dis, rc);
1629 }
1630 
libxl__get_domid(libxl__gc * gc,uint32_t * domid)1631 int libxl__get_domid(libxl__gc *gc, uint32_t *domid)
1632 {
1633     int rc;
1634     const char *xs_domid;
1635 
1636     rc = libxl__xs_read_checked(gc, XBT_NULL, DOMID_XS_PATH, &xs_domid);
1637     if (rc) goto out;
1638     if (!xs_domid) {
1639         LOG(ERROR, "failed to get own domid (%s)", DOMID_XS_PATH);
1640         rc = ERROR_FAIL;
1641         goto out;
1642     }
1643 
1644     *domid = atoi(xs_domid);
1645 
1646 out:
1647     return rc;
1648 }
1649 
libxl__resolve_domid(libxl__gc * gc,const char * name,uint32_t * domid)1650 int libxl__resolve_domid(libxl__gc *gc, const char *name, uint32_t *domid)
1651 {
1652     if (!name)
1653         return 0;
1654     return libxl_domain_qualifier_to_domid(CTX, name, domid);
1655 }
1656 
libxl_list_vcpu(libxl_ctx * ctx,uint32_t domid,int * nr_vcpus_out,int * nr_cpus_out)1657 libxl_vcpuinfo *libxl_list_vcpu(libxl_ctx *ctx, uint32_t domid,
1658                                        int *nr_vcpus_out, int *nr_cpus_out)
1659 {
1660     int r;
1661     GC_INIT(ctx);
1662     libxl_vcpuinfo *ptr, *ret;
1663     xc_domaininfo_t domaininfo;
1664     xc_vcpuinfo_t vcpuinfo;
1665     unsigned int nr_vcpus;
1666 
1667     r = xc_domain_getinfo_single(ctx->xch, domid, &domaininfo);
1668     if (r < 0) {
1669         LOGED(ERROR, domid, "Getting dominfo");
1670         GC_FREE;
1671         return NULL;
1672     }
1673 
1674     if (domaininfo.max_vcpu_id == XEN_INVALID_MAX_VCPU_ID) {
1675         GC_FREE;
1676         return NULL;
1677     }
1678 
1679     *nr_cpus_out = libxl_get_max_cpus(ctx);
1680     ret = ptr = libxl__calloc(NOGC, domaininfo.max_vcpu_id + 1,
1681                               sizeof(libxl_vcpuinfo));
1682 
1683     for (nr_vcpus = 0;
1684          nr_vcpus <= domaininfo.max_vcpu_id;
1685          ++nr_vcpus, ++ptr) {
1686         libxl_bitmap_init(&ptr->cpumap);
1687         if (libxl_cpu_bitmap_alloc(ctx, &ptr->cpumap, 0))
1688             goto err;
1689         libxl_bitmap_init(&ptr->cpumap_soft);
1690         if (libxl_cpu_bitmap_alloc(ctx, &ptr->cpumap_soft, 0))
1691             goto err;
1692         if (xc_vcpu_getinfo(ctx->xch, domid, nr_vcpus, &vcpuinfo) == -1) {
1693             LOGED(ERROR, domid, "Getting vcpu info");
1694             goto err;
1695         }
1696 
1697         if (xc_vcpu_getaffinity(ctx->xch, domid, nr_vcpus,
1698                                 ptr->cpumap.map, ptr->cpumap_soft.map,
1699                                 XEN_VCPUAFFINITY_SOFT|XEN_VCPUAFFINITY_HARD) == -1) {
1700             LOGED(ERROR, domid, "Getting vcpu affinity");
1701             goto err;
1702         }
1703         ptr->vcpuid = nr_vcpus;
1704         ptr->cpu = vcpuinfo.cpu;
1705         ptr->online = !!vcpuinfo.online;
1706         ptr->blocked = !!vcpuinfo.blocked;
1707         ptr->running = !!vcpuinfo.running;
1708         ptr->vcpu_time = vcpuinfo.cpu_time;
1709     }
1710     *nr_vcpus_out = nr_vcpus;
1711     GC_FREE;
1712     return ret;
1713 
1714 err:
1715     libxl_bitmap_dispose(&ptr->cpumap);
1716     libxl_bitmap_dispose(&ptr->cpumap_soft);
1717     free(ret);
1718     GC_FREE;
1719     return NULL;
1720 }
1721 
libxl__set_vcpuonline_xenstore(libxl__gc * gc,uint32_t domid,const libxl_bitmap * cpumap,const libxl_dominfo * info)1722 static int libxl__set_vcpuonline_xenstore(libxl__gc *gc, uint32_t domid,
1723                                           const libxl_bitmap *cpumap,
1724                                           const libxl_dominfo *info)
1725 {
1726     char *dompath;
1727     xs_transaction_t t;
1728     int i, rc = ERROR_FAIL;
1729 
1730     if (!(dompath = libxl__xs_get_dompath(gc, domid)))
1731         goto out;
1732 
1733 retry_transaction:
1734     t = xs_transaction_start(CTX->xsh);
1735     for (i = 0; i <= info->vcpu_max_id; i++)
1736         libxl__xs_printf(gc, t,
1737                          GCSPRINTF("%s/cpu/%u/availability", dompath, i),
1738                          "%s", libxl_bitmap_test(cpumap, i) ? "online" : "offline");
1739     if (!xs_transaction_end(CTX->xsh, t, 0)) {
1740         if (errno == EAGAIN)
1741             goto retry_transaction;
1742     } else
1743         rc = 0;
1744 out:
1745     return rc;
1746 }
1747 
qmp_parse_query_cpus_fast(libxl__gc * gc,libxl_domid domid,const libxl__json_object * response,libxl_bitmap * const map)1748 static int qmp_parse_query_cpus_fast(libxl__gc *gc,
1749                                      libxl_domid domid,
1750                                      const libxl__json_object *response,
1751                                      libxl_bitmap *const map)
1752 {
1753     int i;
1754     const libxl__json_object *cpu;
1755 
1756     libxl_bitmap_set_none(map);
1757     /* Parse response to QMP command "query-cpus-fast":
1758      * [ { 'cpu-index': 'int',...} ]
1759      */
1760     for (i = 0; (cpu = libxl__json_array_get(response, i)); i++) {
1761         unsigned int cpu_index;
1762         const libxl__json_object *o;
1763 
1764         o = libxl__json_map_get("cpu-index", cpu, JSON_INTEGER);
1765         if (!o) {
1766             LOGD(ERROR, domid, "Failed to retrieve CPU index.");
1767             return ERROR_QEMU_API;
1768         }
1769 
1770         cpu_index = libxl__json_object_get_integer(o);
1771         libxl_bitmap_set(map, cpu_index);
1772     }
1773 
1774     return 0;
1775 }
1776 
qmp_parse_query_cpus(libxl__gc * gc,libxl_domid domid,const libxl__json_object * response,libxl_bitmap * const map)1777 static int qmp_parse_query_cpus(libxl__gc *gc,
1778                                 libxl_domid domid,
1779                                 const libxl__json_object *response,
1780                                 libxl_bitmap *const map)
1781 {
1782     int i;
1783     const libxl__json_object *cpu;
1784 
1785     libxl_bitmap_set_none(map);
1786     /* Parse response to QMP command "query-cpus":
1787      * [ { 'CPU': 'int',...} ]
1788      */
1789     for (i = 0; (cpu = libxl__json_array_get(response, i)); i++) {
1790         unsigned int cpu_index;
1791         const libxl__json_object *o;
1792 
1793         o = libxl__json_map_get("CPU", cpu, JSON_INTEGER);
1794         if (!o) {
1795             LOGD(ERROR, domid, "Failed to retrieve CPU index.");
1796             return ERROR_QEMU_API;
1797         }
1798 
1799         cpu_index = libxl__json_object_get_integer(o);
1800         libxl_bitmap_set(map, cpu_index);
1801     }
1802 
1803     return 0;
1804 }
1805 
1806 typedef struct set_vcpuonline_state {
1807     libxl__ev_qmp qmp;
1808     libxl__ev_time timeout;
1809     const libxl_bitmap *cpumap;
1810     libxl_dominfo info;
1811     libxl_bitmap final_map;
1812     int index; /* for loop on final_map */
1813     const char *cpu_driver;
1814 } set_vcpuonline_state;
1815 
1816 static void set_vcpuonline_qmp_cpus_fast_queried(libxl__egc *,
1817     libxl__ev_qmp *, const libxl__json_object *, int rc);
1818 static void set_vcpuonline_qmp_cpus_queried(libxl__egc *,
1819     libxl__ev_qmp *, const libxl__json_object *, int rc);
1820 static void set_vcpuonline_qmp_query_cpus_parse(libxl__egc *,
1821     libxl__ev_qmp *qmp, const libxl__json_object *,
1822     bool query_cpus_fast, int rc);
1823 static void set_vcpuonline_qmp_query_hotpluggable_cpus(libxl__egc *egc,
1824     libxl__ev_qmp *qmp, const libxl__json_object *response, int rc);
1825 static void set_vcpuonline_qmp_device_add_cpu(libxl__egc *,
1826     libxl__ev_qmp *, const libxl__json_object *response, int rc);
1827 static void set_vcpuonline_qmp_add_cpu(libxl__egc *,
1828     libxl__ev_qmp *, const libxl__json_object *response, int rc);
1829 static void set_vcpuonline_timeout(libxl__egc *egc,
1830     libxl__ev_time *ev, const struct timeval *requested_abs, int rc);
1831 static void set_vcpuonline_done(libxl__egc *egc,
1832     set_vcpuonline_state *svos, int rc);
1833 
libxl_set_vcpuonline(libxl_ctx * ctx,uint32_t domid,libxl_bitmap * cpumap,const libxl_asyncop_how * ao_how)1834 int libxl_set_vcpuonline(libxl_ctx *ctx, uint32_t domid,
1835                          libxl_bitmap *cpumap,
1836                          const libxl_asyncop_how *ao_how)
1837 {
1838     AO_CREATE(ctx, domid, ao_how);
1839     int rc, maxcpus;
1840     set_vcpuonline_state *svos;
1841 
1842     GCNEW(svos);
1843     libxl__ev_qmp_init(&svos->qmp);
1844     svos->qmp.ao = ao;
1845     svos->qmp.domid = domid;
1846     svos->qmp.payload_fd = -1;
1847     libxl__ev_time_init(&svos->timeout);
1848     svos->cpumap = cpumap;
1849     libxl_dominfo_init(&svos->info);
1850     libxl_bitmap_init(&svos->final_map);
1851 
1852     /* Convenience aliases */
1853     libxl_dominfo *info = &svos->info;
1854     libxl__ev_qmp *qmp = &svos->qmp;
1855 
1856     rc = libxl_domain_info(CTX, info, domid);
1857     if (rc < 0) {
1858         LOGED(ERROR, domid, "Getting domain info list");
1859         goto out;
1860     }
1861 
1862     maxcpus = libxl_bitmap_count_set(cpumap);
1863     if (maxcpus == 0)
1864     {
1865         LOGED(ERROR, domid, "Requested 0 VCPUs!");
1866         rc = ERROR_FAIL;
1867         goto out;
1868     }
1869     if (maxcpus > info->vcpu_max_id + 1)
1870     {
1871         LOGED(ERROR, domid, "Requested %d VCPUs, however maxcpus is %d!",
1872               maxcpus, info->vcpu_max_id + 1);
1873         rc = ERROR_FAIL;
1874         goto out;
1875     }
1876 
1877     switch (libxl__domain_type(gc, domid)) {
1878     case LIBXL_DOMAIN_TYPE_HVM:
1879         switch (libxl__device_model_version_running(gc, domid)) {
1880         case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN_TRADITIONAL:
1881             break;
1882         case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN:
1883             rc = libxl__ev_time_register_rel(ao, &svos->timeout,
1884                                              set_vcpuonline_timeout,
1885                                              LIBXL_QMP_CMD_TIMEOUT * 1000);
1886             if (rc) goto out;
1887             qmp->callback = set_vcpuonline_qmp_cpus_fast_queried;
1888             rc = libxl__ev_qmp_send(egc, qmp, "query-cpus-fast", NULL);
1889             if (rc) goto out;
1890             return AO_INPROGRESS;
1891         default:
1892             rc = ERROR_INVAL;
1893         }
1894         break;
1895     case LIBXL_DOMAIN_TYPE_PVH:
1896     case LIBXL_DOMAIN_TYPE_PV:
1897         break;
1898     default:
1899         rc = ERROR_INVAL;
1900     }
1901 
1902 out:
1903     set_vcpuonline_done(egc, svos, rc); /* must be last */
1904     return AO_INPROGRESS;
1905 }
1906 
set_vcpuonline_qmp_cpus_fast_queried(libxl__egc * egc,libxl__ev_qmp * qmp,const libxl__json_object * response,int rc)1907 static void set_vcpuonline_qmp_cpus_fast_queried(libxl__egc *egc,
1908     libxl__ev_qmp *qmp, const libxl__json_object *response, int rc)
1909 {
1910     EGC_GC;
1911     set_vcpuonline_state *svos = CONTAINER_OF(qmp, *svos, qmp);
1912 
1913     if (rc == ERROR_QMP_COMMAND_NOT_FOUND) {
1914         /* Try again, we probably talking to a QEMU older than 2.12 */
1915         qmp->callback = set_vcpuonline_qmp_cpus_queried;
1916         rc = libxl__ev_qmp_send(egc, qmp, "query-cpus", NULL);
1917         if (rc) goto out;
1918         return;
1919     }
1920 
1921 out:
1922     set_vcpuonline_qmp_query_cpus_parse(egc, qmp, response, true, rc);
1923 }
1924 
set_vcpuonline_qmp_cpus_queried(libxl__egc * egc,libxl__ev_qmp * qmp,const libxl__json_object * response,int rc)1925 static void set_vcpuonline_qmp_cpus_queried(libxl__egc *egc,
1926     libxl__ev_qmp *qmp, const libxl__json_object *response, int rc)
1927 {
1928     EGC_GC;
1929     set_vcpuonline_state *svos = CONTAINER_OF(qmp, *svos, qmp);
1930 
1931     set_vcpuonline_qmp_query_cpus_parse(egc, qmp, response, false, rc);
1932 }
1933 
set_vcpuonline_qmp_query_cpus_parse(libxl__egc * egc,libxl__ev_qmp * qmp,const libxl__json_object * response,bool query_cpus_fast,int rc)1934 static void set_vcpuonline_qmp_query_cpus_parse(libxl__egc *egc,
1935     libxl__ev_qmp *qmp, const libxl__json_object *response,
1936     bool query_cpus_fast, int rc)
1937 {
1938     EGC_GC;
1939     set_vcpuonline_state *svos = CONTAINER_OF(qmp, *svos, qmp);
1940     int i;
1941     libxl_bitmap current_map;
1942 
1943     /* Convenience aliases */
1944     libxl_bitmap *final_map = &svos->final_map;
1945 
1946     libxl_bitmap_init(&current_map);
1947 
1948     if (rc) goto out;
1949 
1950     libxl_bitmap_alloc(CTX, &current_map, svos->info.vcpu_max_id + 1);
1951     if (query_cpus_fast) {
1952         rc = qmp_parse_query_cpus_fast(gc, qmp->domid, response, &current_map);
1953     } else {
1954         rc = qmp_parse_query_cpus(gc, qmp->domid, response, &current_map);
1955     }
1956     if (rc) goto out;
1957 
1958     libxl_bitmap_copy_alloc(CTX, final_map, svos->cpumap);
1959 
1960     libxl_for_each_set_bit(i, current_map) {
1961         libxl_bitmap_reset(final_map, i);
1962     }
1963 
1964     qmp->callback = set_vcpuonline_qmp_query_hotpluggable_cpus;
1965     rc = libxl__ev_qmp_send(egc, qmp, "query-hotpluggable-cpus", NULL);
1966 
1967 out:
1968     libxl_bitmap_dispose(&current_map);
1969     if (rc)
1970         set_vcpuonline_done(egc, svos, rc); /* must be last */
1971 }
1972 
set_vcpuonline_qmp_query_hotpluggable_cpus(libxl__egc * egc,libxl__ev_qmp * qmp,const libxl__json_object * response,int rc)1973 static void set_vcpuonline_qmp_query_hotpluggable_cpus(libxl__egc *egc,
1974     libxl__ev_qmp *qmp, const libxl__json_object *response, int rc)
1975 {
1976     set_vcpuonline_state *svos = CONTAINER_OF(qmp, *svos, qmp);
1977     const libxl__json_object *cpu;
1978     const libxl__json_object *cpu_driver;
1979 
1980     if (rc == ERROR_QMP_COMMAND_NOT_FOUND) {
1981         /* We are probably connected to a version of QEMU older than 2.7,
1982          * let's fallback to using "cpu-add" command. */
1983         svos->index = -1;
1984         set_vcpuonline_qmp_add_cpu(egc, qmp, NULL, 0); /* must be last */
1985         return;
1986     }
1987 
1988     if (rc) goto out;
1989 
1990     /* Parse response to QMP command "query-hotpluggable-cpus"
1991      * [ { 'type': 'str', ... ]
1992      *
1993      * We are looking for the driver name for CPU to be hotplug. We'll
1994      * assume that cpus property are core-id=0, thread-id=0 and
1995      * socket-id=$cpu_index, as we start qemu with "-smp %d,maxcpus=%d", so
1996      * we don't parse the properties listed for each hotpluggable cpus.
1997      */
1998 
1999     cpu = libxl__json_array_get(response, 0);
2000     cpu_driver = libxl__json_map_get("type", cpu, JSON_STRING);
2001     svos->cpu_driver = libxl__json_object_get_string(cpu_driver);
2002 
2003     if (!svos->cpu_driver)
2004         rc = ERROR_QEMU_API;
2005 
2006 out:
2007     svos->index = -1;
2008     set_vcpuonline_qmp_device_add_cpu(egc, qmp, NULL, rc); /* must be last */
2009 }
2010 
set_vcpuonline_qmp_device_add_cpu(libxl__egc * egc,libxl__ev_qmp * qmp,const libxl__json_object * response,int rc)2011 static void set_vcpuonline_qmp_device_add_cpu(libxl__egc *egc,
2012     libxl__ev_qmp *qmp, const libxl__json_object *response, int rc)
2013 {
2014     STATE_AO_GC(qmp->ao);
2015     set_vcpuonline_state *svos = CONTAINER_OF(qmp, *svos, qmp);
2016     libxl__json_object *args = NULL;
2017 
2018     /* Convenience aliases */
2019     libxl_bitmap *map = &svos->final_map;
2020 
2021     if (rc) goto out;
2022 
2023     while (libxl_bitmap_cpu_valid(map, ++svos->index)) {
2024         if (libxl_bitmap_test(map, svos->index)) {
2025             qmp->callback = set_vcpuonline_qmp_device_add_cpu;
2026             libxl__qmp_param_add_string(gc, &args, "id", GCSPRINTF("cpu-%d", svos->index));
2027             libxl__qmp_param_add_string(gc, &args, "driver", svos->cpu_driver);
2028             /* We'll assume that we start QEMU with -smp %d,maxcpus=%d, so
2029              * that "core-id" and "thread-id" are always 0 so that
2030              * "socket-id" correspond the cpu index.
2031              * Those properties are otherwise listed by
2032              * "query-hotpluggable-cpus". */
2033             libxl__qmp_param_add_integer(gc, &args, "socket-id", svos->index);
2034             libxl__qmp_param_add_integer(gc, &args, "core-id", 0);
2035             libxl__qmp_param_add_integer(gc, &args, "thread-id", 0);
2036             rc = libxl__ev_qmp_send(egc, qmp, "device_add", args);
2037             if (rc) goto out;
2038             return;
2039         }
2040     }
2041 
2042 out:
2043     set_vcpuonline_done(egc, svos, rc);
2044 }
2045 
2046 /* Fallback function for QEMU older than 2.7, when
2047  * 'query-hotpluggable-cpus' wasn't available and vcpu object couldn't be
2048  * added with 'device_add'. */
set_vcpuonline_qmp_add_cpu(libxl__egc * egc,libxl__ev_qmp * qmp,const libxl__json_object * response,int rc)2049 static void set_vcpuonline_qmp_add_cpu(libxl__egc *egc, libxl__ev_qmp *qmp,
2050                                        const libxl__json_object *response,
2051                                        int rc)
2052 {
2053     STATE_AO_GC(qmp->ao);
2054     set_vcpuonline_state *svos = CONTAINER_OF(qmp, *svos, qmp);
2055     libxl__json_object *args = NULL;
2056 
2057     /* Convenience aliases */
2058     libxl_bitmap *map = &svos->final_map;
2059 
2060     if (rc) goto out;
2061 
2062     while (libxl_bitmap_cpu_valid(map, ++svos->index)) {
2063         if (libxl_bitmap_test(map, svos->index)) {
2064             qmp->callback = set_vcpuonline_qmp_add_cpu;
2065             libxl__qmp_param_add_integer(gc, &args, "id", svos->index);
2066             rc = libxl__ev_qmp_send(egc, qmp, "cpu-add", args);
2067             if (rc) goto out;
2068             return;
2069         }
2070     }
2071 
2072 out:
2073     set_vcpuonline_done(egc, svos, rc);
2074 }
2075 
set_vcpuonline_timeout(libxl__egc * egc,libxl__ev_time * ev,const struct timeval * requested_abs,int rc)2076 static void set_vcpuonline_timeout(libxl__egc *egc, libxl__ev_time *ev,
2077                                    const struct timeval *requested_abs,
2078                                    int rc)
2079 {
2080     EGC_GC;
2081     set_vcpuonline_state *svos = CONTAINER_OF(ev, *svos, timeout);
2082 
2083     if (rc == ERROR_TIMEDOUT)
2084         LOGD(ERROR, svos->qmp.domid,
2085              "Setting CPU online in QEMU timed out");
2086 
2087     set_vcpuonline_done(egc, svos, rc);
2088 }
2089 
set_vcpuonline_done(libxl__egc * egc,set_vcpuonline_state * svos,int rc)2090 static void set_vcpuonline_done(libxl__egc *egc,
2091                                 set_vcpuonline_state *svos,
2092                                 int rc)
2093 {
2094     STATE_AO_GC(svos->qmp.ao);
2095 
2096     /* Convenience aliases */
2097     libxl_domid domid = svos->qmp.domid;
2098 
2099     if (!rc)
2100         rc = libxl__set_vcpuonline_xenstore(gc, domid, svos->cpumap,
2101                                             &svos->info);
2102 
2103     libxl_bitmap_dispose(&svos->final_map);
2104     libxl_dominfo_dispose(&svos->info);
2105     libxl__ev_time_deregister(gc, &svos->timeout);
2106     libxl__ev_qmp_dispose(gc, &svos->qmp);
2107     libxl__ao_complete(egc, ao, rc);
2108 }
2109 
2110 static void domain_s3_resume_done(libxl__egc *egc, libxl__ev_qmp *qmp,
2111                                   const libxl__json_object *response,
2112                                   int rc);
2113 
domain_s3_resume(libxl__ao * ao,libxl__egc * egc,int domid)2114 static void domain_s3_resume(libxl__ao *ao, libxl__egc *egc, int domid)
2115 {
2116     AO_GC;
2117     libxl__ev_qmp *qmp;
2118     int rc = 0;
2119     int r;
2120 
2121     GCNEW(qmp);
2122     libxl__ev_qmp_init(qmp);
2123     qmp->ao = ao;
2124     qmp->domid = domid;
2125     qmp->payload_fd = -1;
2126     qmp->callback = domain_s3_resume_done;
2127 
2128     switch (libxl__domain_type(gc, domid)) {
2129     case LIBXL_DOMAIN_TYPE_HVM:
2130         switch (libxl__device_model_version_running(gc, domid)) {
2131         case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN_TRADITIONAL:
2132             r = xc_hvm_param_set(CTX->xch, domid, HVM_PARAM_ACPI_S_STATE, 0);
2133             if (r) {
2134                 LOGED(ERROR, domid, "Send trigger '%s' failed",
2135                       libxl_trigger_to_string(LIBXL_TRIGGER_S3RESUME));
2136                 rc = ERROR_FAIL;
2137             }
2138             break;
2139         case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN:
2140             rc = libxl__ev_qmp_send(egc, qmp, "system_wakeup", NULL);
2141             if (rc) goto out;
2142             return;
2143         default:
2144             rc = ERROR_INVAL;
2145             break;
2146         }
2147         break;
2148     default:
2149         rc = ERROR_INVAL;
2150         break;
2151     }
2152 
2153 out:
2154     domain_s3_resume_done(egc, qmp, NULL, rc);
2155 }
2156 
domain_s3_resume_done(libxl__egc * egc,libxl__ev_qmp * qmp,const libxl__json_object * response,int rc)2157 static void domain_s3_resume_done(libxl__egc *egc, libxl__ev_qmp *qmp,
2158                                   const libxl__json_object *response,
2159                                   int rc)
2160 {
2161     EGC_GC;
2162 
2163     if (rc)
2164         LOGD(ERROR, qmp->domid, "Send trigger '%s' failed, rc=%d",
2165               libxl_trigger_to_string(LIBXL_TRIGGER_S3RESUME), rc);
2166 
2167     libxl__ev_qmp_dispose(gc, qmp);
2168     libxl__ao_complete(egc, qmp->ao, rc);
2169 }
2170 
libxl_send_trigger(libxl_ctx * ctx,uint32_t domid,libxl_trigger trigger,uint32_t vcpuid,const libxl_asyncop_how * ao_how)2171 int libxl_send_trigger(libxl_ctx *ctx, uint32_t domid,
2172                        libxl_trigger trigger, uint32_t vcpuid,
2173                        const libxl_asyncop_how *ao_how)
2174 {
2175     AO_CREATE(ctx, domid, ao_how);
2176     int rc;
2177 
2178     switch (trigger) {
2179     case LIBXL_TRIGGER_POWER:
2180         rc = xc_domain_send_trigger(ctx->xch, domid,
2181                                     XEN_DOMCTL_SENDTRIGGER_POWER, vcpuid);
2182         break;
2183     case LIBXL_TRIGGER_SLEEP:
2184         rc = xc_domain_send_trigger(ctx->xch, domid,
2185                                     XEN_DOMCTL_SENDTRIGGER_SLEEP, vcpuid);
2186         break;
2187     case LIBXL_TRIGGER_NMI:
2188         rc = xc_domain_send_trigger(ctx->xch, domid,
2189                                     XEN_DOMCTL_SENDTRIGGER_NMI, vcpuid);
2190         break;
2191     case LIBXL_TRIGGER_INIT:
2192         rc = xc_domain_send_trigger(ctx->xch, domid,
2193                                     XEN_DOMCTL_SENDTRIGGER_INIT, vcpuid);
2194         break;
2195     case LIBXL_TRIGGER_RESET:
2196         rc = xc_domain_send_trigger(ctx->xch, domid,
2197                                     XEN_DOMCTL_SENDTRIGGER_RESET, vcpuid);
2198         break;
2199     case LIBXL_TRIGGER_S3RESUME:
2200         domain_s3_resume(ao, egc, domid); /* must be last */
2201         return AO_INPROGRESS;
2202     default:
2203         rc = -1;
2204         errno = EINVAL;
2205         break;
2206     }
2207 
2208     if (rc != 0) {
2209         LOGED(ERROR, domid, "Send trigger '%s' failed",
2210               libxl_trigger_to_string(trigger));
2211         rc = ERROR_FAIL;
2212         goto out;
2213     }
2214 
2215     libxl__ao_complete(egc, ao, rc);
2216     return AO_INPROGRESS;
2217 out:
2218     return AO_CREATE_FAIL(rc);
2219 }
2220 
libxl_vm_get_start_time(libxl_ctx * ctx,uint32_t domid)2221 uint32_t libxl_vm_get_start_time(libxl_ctx *ctx, uint32_t domid)
2222 {
2223     GC_INIT(ctx);
2224     char *dompath = libxl__xs_get_dompath(gc, domid);
2225     char *vm_path, *start_time;
2226     uint32_t ret;
2227 
2228     vm_path = libxl__xs_read(
2229         gc, XBT_NULL, GCSPRINTF("%s/vm", dompath));
2230     start_time = libxl__xs_read(
2231         gc, XBT_NULL, GCSPRINTF("%s/start_time", vm_path));
2232     if (start_time == NULL) {
2233         LOGEVD(ERROR, -1, domid, "Can't get start time of domain");
2234         ret = -1;
2235     }else{
2236         ret = strtoul(start_time, NULL, 10);
2237     }
2238     GC_FREE;
2239     return ret;
2240 }
2241 
libxl__update_avail_vcpus_xenstore(libxl__gc * gc,uint32_t domid,unsigned int max_vcpus,libxl_bitmap * map)2242 static int libxl__update_avail_vcpus_xenstore(libxl__gc *gc, uint32_t domid,
2243                                               unsigned int max_vcpus,
2244                                               libxl_bitmap *map)
2245 {
2246     int rc;
2247     unsigned int i;
2248     const char *dompath;
2249 
2250     dompath = libxl__xs_get_dompath(gc, domid);
2251     if (!dompath) {
2252         rc = ERROR_FAIL;
2253         goto out;
2254     }
2255 
2256     for (i = 0; i < max_vcpus; i++) {
2257         const char *path = GCSPRINTF("%s/cpu/%u/availability", dompath, i);
2258         const char *content;
2259         rc = libxl__xs_read_checked(gc, XBT_NULL, path, &content);
2260         if (rc) goto out;
2261         if (content && !strcmp(content, "online"))
2262             libxl_bitmap_set(map, i);
2263     }
2264 
2265     rc = 0;
2266 out:
2267     return rc;
2268 }
2269 
2270 typedef struct {
2271     libxl__ev_qmp qmp;
2272     libxl__ev_time timeout;
2273     libxl_domain_config *d_config; /* user pointer */
2274     libxl__ev_slowlock devlock;
2275     libxl_bitmap qemuu_cpus;
2276 } retrieve_domain_configuration_state;
2277 
2278 static void retrieve_domain_configuration_lock_acquired(
2279     libxl__egc *egc, libxl__ev_slowlock *, int rc);
2280 static void retrieve_domain_configuration_cpu_fast_queried(
2281     libxl__egc *egc, libxl__ev_qmp *qmp,
2282     const libxl__json_object *response, int rc);
2283 static void retrieve_domain_configuration_cpu_queried(
2284     libxl__egc *egc, libxl__ev_qmp *qmp,
2285     const libxl__json_object *response, int rc);
2286 static void retrieve_domain_configuration_timeout(libxl__egc *egc,
2287     libxl__ev_time *ev, const struct timeval *requested_abs, int rc);
2288 static void retrieve_domain_configuration_end(libxl__egc *egc,
2289     retrieve_domain_configuration_state *rdcs, int rc);
2290 
libxl_retrieve_domain_configuration(libxl_ctx * ctx,uint32_t domid,libxl_domain_config * d_config,const libxl_asyncop_how * ao_how)2291 int libxl_retrieve_domain_configuration(libxl_ctx *ctx, uint32_t domid,
2292                                         libxl_domain_config *d_config,
2293                                         const libxl_asyncop_how *ao_how)
2294 {
2295     AO_CREATE(ctx, domid, ao_how);
2296     retrieve_domain_configuration_state *rdcs;
2297 
2298     GCNEW(rdcs);
2299     libxl__ev_qmp_init(&rdcs->qmp);
2300     rdcs->qmp.ao = ao;
2301     rdcs->qmp.domid = domid;
2302     rdcs->qmp.payload_fd = -1;
2303     libxl__ev_time_init(&rdcs->timeout);
2304     rdcs->d_config = d_config;
2305     libxl_bitmap_init(&rdcs->qemuu_cpus);
2306     libxl__ev_devlock_init(&rdcs->devlock);
2307     rdcs->devlock.ao = ao;
2308     rdcs->devlock.domid = domid;
2309     rdcs->devlock.callback = retrieve_domain_configuration_lock_acquired;
2310     libxl__ev_slowlock_lock(egc, &rdcs->devlock);
2311     return AO_INPROGRESS;
2312 }
2313 
retrieve_domain_configuration_lock_acquired(libxl__egc * egc,libxl__ev_slowlock * devlock,int rc)2314 static void retrieve_domain_configuration_lock_acquired(
2315     libxl__egc *egc, libxl__ev_slowlock *devlock, int rc)
2316 {
2317     retrieve_domain_configuration_state *rdcs =
2318         CONTAINER_OF(devlock, *rdcs, devlock);
2319     STATE_AO_GC(rdcs->qmp.ao);
2320     libxl__flock *lock = NULL;
2321     bool has_callback = false;
2322 
2323     /* Convenience aliases */
2324     libxl_domid domid = rdcs->qmp.domid;
2325     libxl_domain_config *const d_config = rdcs->d_config;
2326 
2327     if (rc) goto out;
2328 
2329     lock = libxl__lock_domain_userdata(gc, domid);
2330     if (!lock) {
2331         rc = ERROR_LOCK_FAIL;
2332         goto out;
2333     }
2334 
2335     rc = libxl__get_domain_configuration(gc, domid, d_config);
2336     if (rc) {
2337         LOGD(ERROR, domid, "Fail to get domain configuration");
2338         rc = ERROR_FAIL;
2339         goto out;
2340     }
2341 
2342     libxl__unlock_file(lock);
2343     lock = NULL;
2344 
2345     /* We start by querying QEMU, if it is running, for its cpumap as this
2346      * is a long operation. */
2347     if (d_config->b_info.type == LIBXL_DOMAIN_TYPE_HVM &&
2348         libxl__device_model_version_running(gc, domid) ==
2349             LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN) {
2350         /* For QEMU upstream we always need to provide the number
2351          * of cpus present to QEMU whether they are online or not;
2352          * otherwise QEMU won't accept the saved state.
2353          */
2354         rc = libxl__ev_time_register_rel(ao, &rdcs->timeout,
2355             retrieve_domain_configuration_timeout,
2356             LIBXL_QMP_CMD_TIMEOUT * 1000);
2357         if (rc) goto out;
2358         libxl_bitmap_alloc(CTX, &rdcs->qemuu_cpus,
2359                            d_config->b_info.max_vcpus);
2360         rdcs->qmp.callback = retrieve_domain_configuration_cpu_fast_queried;
2361         rc = libxl__ev_qmp_send(egc, &rdcs->qmp, "query-cpus-fast", NULL);
2362         if (rc) goto out;
2363         has_callback = true;
2364     }
2365 
2366 out:
2367     if (lock) libxl__unlock_file(lock);
2368     if (!has_callback)
2369         retrieve_domain_configuration_end(egc, rdcs, rc);
2370 }
2371 
retrieve_domain_configuration_cpu_fast_queried(libxl__egc * egc,libxl__ev_qmp * qmp,const libxl__json_object * response,int rc)2372 static void retrieve_domain_configuration_cpu_fast_queried(
2373     libxl__egc *egc, libxl__ev_qmp *qmp,
2374     const libxl__json_object *response, int rc)
2375 {
2376     EGC_GC;
2377     retrieve_domain_configuration_state *rdcs =
2378         CONTAINER_OF(qmp, *rdcs, qmp);
2379 
2380     if (rc == ERROR_QMP_COMMAND_NOT_FOUND) {
2381         /* Try again, we probably talking to a QEMU older than 2.12 */
2382         rdcs->qmp.callback = retrieve_domain_configuration_cpu_queried;
2383         rc = libxl__ev_qmp_send(egc, &rdcs->qmp, "query-cpus", NULL);
2384         if (rc) goto out;
2385         return;
2386     }
2387 
2388     if (rc) goto out;
2389 
2390     rc = qmp_parse_query_cpus_fast(gc, qmp->domid, response, &rdcs->qemuu_cpus);
2391 
2392 out:
2393     retrieve_domain_configuration_end(egc, rdcs, rc);
2394 }
2395 
retrieve_domain_configuration_cpu_queried(libxl__egc * egc,libxl__ev_qmp * qmp,const libxl__json_object * response,int rc)2396 static void retrieve_domain_configuration_cpu_queried(
2397     libxl__egc *egc, libxl__ev_qmp *qmp,
2398     const libxl__json_object *response, int rc)
2399 {
2400     EGC_GC;
2401     retrieve_domain_configuration_state *rdcs =
2402         CONTAINER_OF(qmp, *rdcs, qmp);
2403 
2404     if (rc) goto out;
2405 
2406     rc = qmp_parse_query_cpus(gc, qmp->domid, response, &rdcs->qemuu_cpus);
2407 
2408 out:
2409     retrieve_domain_configuration_end(egc, rdcs, rc);
2410 }
2411 
retrieve_domain_configuration_timeout(libxl__egc * egc,libxl__ev_time * ev,const struct timeval * requested_abs,int rc)2412 static void retrieve_domain_configuration_timeout(libxl__egc *egc,
2413     libxl__ev_time *ev, const struct timeval *requested_abs, int rc)
2414 {
2415     retrieve_domain_configuration_state *rdcs =
2416         CONTAINER_OF(ev, *rdcs, timeout);
2417 
2418     retrieve_domain_configuration_end(egc, rdcs, rc);
2419 }
2420 
retrieve_domain_configuration_end(libxl__egc * egc,retrieve_domain_configuration_state * rdcs,int rc)2421 static void retrieve_domain_configuration_end(libxl__egc *egc,
2422     retrieve_domain_configuration_state *rdcs, int rc)
2423 {
2424     STATE_AO_GC(rdcs->qmp.ao);
2425     libxl__flock *lock = NULL;
2426 
2427     /* Convenience aliases */
2428     libxl_domain_config *const d_config = rdcs->d_config;
2429     libxl_domid domid = rdcs->qmp.domid;
2430 
2431     if (rc) goto out;
2432 
2433     lock = libxl__lock_domain_userdata(gc, domid);
2434     if (!lock) {
2435         rc = ERROR_LOCK_FAIL;
2436         goto out;
2437     }
2438 
2439     /* Domain name */
2440     {
2441         char *domname;
2442         domname = libxl_domid_to_name(CTX, domid);
2443         if (!domname) {
2444             LOGD(ERROR, domid, "Fail to get domain name");
2445             goto out;
2446         }
2447         free(d_config->c_info.name);
2448         d_config->c_info.name = domname; /* steals allocation */
2449     }
2450 
2451     /* Domain UUID */
2452     {
2453         libxl_dominfo info;
2454         libxl_dominfo_init(&info);
2455         rc = libxl_domain_info(CTX, &info, domid);
2456         if (rc) {
2457             LOGD(ERROR, domid, "Fail to get domain info");
2458             libxl_dominfo_dispose(&info);
2459             goto out;
2460         }
2461         libxl_uuid_copy(CTX, &d_config->c_info.uuid, &info.uuid);
2462         libxl_dominfo_dispose(&info);
2463     }
2464 
2465     /* VCPUs */
2466     {
2467         libxl_bitmap *map = &d_config->b_info.avail_vcpus;
2468         unsigned int max_vcpus = d_config->b_info.max_vcpus;
2469         libxl_device_model_version version;
2470 
2471         libxl_bitmap_dispose(map);
2472         libxl_bitmap_init(map);
2473         libxl_bitmap_alloc(CTX, map, max_vcpus);
2474         libxl_bitmap_set_none(map);
2475 
2476         switch (d_config->b_info.type) {
2477         case LIBXL_DOMAIN_TYPE_HVM:
2478             version = libxl__device_model_version_running(gc, domid);
2479             assert(version != LIBXL_DEVICE_MODEL_VERSION_UNKNOWN);
2480             switch (version) {
2481             case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN:
2482                 libxl_bitmap_copy(CTX, map, &rdcs->qemuu_cpus);
2483                 break;
2484             case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN_TRADITIONAL:
2485                 rc = libxl__update_avail_vcpus_xenstore(gc, domid,
2486                                                         max_vcpus, map);
2487                 break;
2488             default:
2489                 abort();
2490             }
2491             break;
2492         case LIBXL_DOMAIN_TYPE_PVH:
2493         case LIBXL_DOMAIN_TYPE_PV:
2494             rc = libxl__update_avail_vcpus_xenstore(gc, domid,
2495                                                     max_vcpus, map);
2496             break;
2497         default:
2498             abort();
2499         }
2500 
2501         if (rc) {
2502             LOGD(ERROR, domid, "Fail to update available cpu map");
2503             goto out;
2504         }
2505     }
2506 
2507 
2508     /* Memory limits:
2509      *
2510      * Currently there are three memory limits:
2511      *  1. "target" in xenstore (originally memory= in config file)
2512      *  2. "static-max" in xenstore (originally maxmem= in config file)
2513      *  3. "max_memkb" in hypervisor
2514      *
2515      * The third one is not visible and currently managed by
2516      * toolstack. In order to rebuild a domain we only need to have
2517      * "target" and "static-max".
2518      */
2519     {
2520         uint64_t target_memkb = 0, max_memkb = 0;
2521 
2522         /* "target" */
2523         rc = libxl__get_memory_target(gc, domid, &target_memkb, &max_memkb);
2524         if (rc) {
2525             LOGD(ERROR, domid, "Fail to get memory target");
2526             goto out;
2527         }
2528 
2529         /* libxl__get_targetmem_fudge() calculates the difference from
2530          * what is in xenstore to what we have in the domain build info.
2531          */
2532         d_config->b_info.target_memkb = target_memkb +
2533             libxl__get_targetmem_fudge(gc, &d_config->b_info);
2534 
2535         d_config->b_info.max_memkb = max_memkb;
2536     }
2537 
2538     /* Scheduler params */
2539     {
2540         libxl_domain_sched_params_dispose(&d_config->b_info.sched_params);
2541         rc = libxl_domain_sched_params_get(CTX, domid,
2542                                            &d_config->b_info.sched_params);
2543         if (rc) {
2544             LOGD(ERROR, domid, "Fail to get scheduler parameters");
2545             goto out;
2546         }
2547     }
2548 
2549     /* Devices: disk, nic, vtpm, pcidev etc. */
2550 
2551     /* The MERGE macro implements following logic:
2552      * 0. retrieve JSON (done by now)
2553      * 1. retrieve list of device from xenstore
2554      * 2. use xenstore entries as primary reference and compare JSON
2555      *    entries with them.
2556      *    a. if a device is present in xenstore and in JSON, merge the
2557      *       two views.
2558      *    b. if a device is not present in xenstore but in JSON, delete
2559      *       it from the result.
2560      *    c. it's impossible to have an entry present in xenstore but
2561      *       not in JSON, because we maintain an invariant that every
2562      *       entry in xenstore must have a corresponding entry in JSON.
2563      * 3. "merge" operates on "src" and "dst". "src" points to the
2564      *    entry retrieved from xenstore while "dst" points to the entry
2565      *    retrieve from JSON.
2566      */
2567     {
2568         const libxl__device_type *dt;
2569         int idx;
2570 
2571         for (idx = 0;; idx++) {
2572             void *p = NULL;
2573             void **devs;
2574             int i, j, num;
2575             int *num_dev;
2576 
2577             dt = device_type_tbl[idx];
2578             if (!dt)
2579                 break;
2580 
2581             if (!dt->compare)
2582                 continue;
2583 
2584             num_dev = libxl__device_type_get_num(dt, d_config);
2585             p = libxl__device_list(gc, dt, domid, &num);
2586             if (p == NULL) {
2587                 LOGD(DEBUG, domid, "No %s from xenstore",
2588                      libxl__device_kind_to_string(dt->type));
2589             }
2590             devs = libxl__device_type_get_ptr(dt, d_config);
2591 
2592             for (i = 0; i < *num_dev; i++) {
2593                 void *q;
2594 
2595                 q = libxl__device_type_get_elem(dt, d_config, i);
2596                 for (j = 0; j < num; j++) {
2597                     if (dt->compare(p + dt->dev_elem_size * j, q))
2598                         break;
2599                 }
2600 
2601                 if (j < num) {         /* found in xenstore */
2602                     if (dt->merge)
2603                         dt->merge(CTX, p + dt->dev_elem_size * j, q);
2604                 } else {                /* not found in xenstore */
2605                     LOGD(WARN, domid,
2606                          "Device present in JSON but not in xenstore, ignored");
2607 
2608                     dt->dispose(q);
2609 
2610                     for (j = i; j < *num_dev - 1; j++)
2611                         memcpy(libxl__device_type_get_elem(dt, d_config, j),
2612                                libxl__device_type_get_elem(dt, d_config, j+1),
2613                                dt->dev_elem_size);
2614 
2615                     /* rewind counters */
2616                     (*num_dev)--;
2617                     i--;
2618 
2619                     *devs = libxl__realloc(NOGC, *devs,
2620                                            dt->dev_elem_size * *num_dev);
2621                 }
2622             }
2623 
2624             for (i = 0; i < num; i++)
2625                 dt->dispose(p + dt->dev_elem_size * i);
2626             free(p);
2627         }
2628     }
2629 
2630 out:
2631     libxl__ev_slowlock_unlock(gc, &rdcs->devlock);
2632     if (lock) libxl__unlock_file(lock);
2633     libxl_bitmap_dispose(&rdcs->qemuu_cpus);
2634     libxl__ev_qmp_dispose(gc, &rdcs->qmp);
2635     libxl__ev_time_deregister(gc, &rdcs->timeout);
2636     libxl__ao_complete(egc, ao, rc);
2637 }
2638 
2639 /*
2640  * Local variables:
2641  * mode: C
2642  * c-basic-offset: 4
2643  * indent-tabs-mode: nil
2644  * End:
2645  */
2646