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(¤t_map);
1947
1948 if (rc) goto out;
1949
1950 libxl_bitmap_alloc(CTX, ¤t_map, svos->info.vcpu_max_id + 1);
1951 if (query_cpus_fast) {
1952 rc = qmp_parse_query_cpus_fast(gc, qmp->domid, response, ¤t_map);
1953 } else {
1954 rc = qmp_parse_query_cpus(gc, qmp->domid, response, ¤t_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(¤t_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