[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [Xen-changelog] [xen master] libxl: carve out domain specific functions from libxl.c
commit 59164cefd474ec70a7494ccff92d94e1e21692d0 Author: Juergen Gross <jgross@xxxxxxxx> AuthorDate: Wed Feb 8 17:09:31 2017 +0100 Commit: Wei Liu <wei.liu2@xxxxxxxxxx> CommitDate: Mon Feb 13 10:59:19 2017 +0000 libxl: carve out domain specific functions from libxl.c libxl.c has grown to an uncomfortable size. Carve out the domain related functions to libxl_domain.c. Signed-off-by: Juergen Gross <jgross@xxxxxxxx> Reviewed-by: Wei Liu <wei.liu2@xxxxxxxxxx> Acked-by: Ian Jackson <ian.jackson@xxxxxxxxxxxxx> --- tools/libxl/Makefile | 1 + tools/libxl/libxl.c | 1808 ++------------------------------------------ tools/libxl/libxl_domain.c | 1744 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 1788 insertions(+), 1765 deletions(-) diff --git a/tools/libxl/Makefile b/tools/libxl/Makefile index 99ddae1..6cc7b45 100644 --- a/tools/libxl/Makefile +++ b/tools/libxl/Makefile @@ -138,6 +138,7 @@ LIBXL_OBJS = flexarray.o libxl.o libxl_create.o libxl_dm.o libxl_pci.o \ libxl_dom_suspend.o libxl_dom_save.o libxl_usb.o \ libxl_vtpm.o libxl_nic.o libxl_disk.o libxl_console.o \ libxl_cpupool.o libxl_mem.o libxl_sched.o libxl_tmem.o \ + libxl_domain.o \ $(LIBXL_OBJS-y) LIBXL_OBJS += libxl_genid.o LIBXL_OBJS += _libxl_types.o libxl_flask.o _libxl_types_internal.o diff --git a/tools/libxl/libxl.c b/tools/libxl/libxl.c index 101cbb9..0ef8744 100644 --- a/tools/libxl/libxl.c +++ b/tools/libxl/libxl.c @@ -16,8 +16,6 @@ #include "libxl_internal.h" -#define PAGE_TO_MEMKB(pages) ((pages) * 4) - int libxl_ctx_alloc(libxl_ctx **pctx, int version, unsigned flags, xentoollog_logger * lg) { @@ -287,1251 +285,69 @@ void libxl_key_value_list_dispose(libxl_key_value_list *pkvl) *pkvl = NULL; } -void libxl_key_value_list_copy(libxl_ctx *ctx, - libxl_key_value_list *dst, - const libxl_key_value_list *src) -{ - GC_INIT(ctx); - int i, len; - - if (*src == NULL) { - *dst = NULL; - goto out; - } - - len = libxl_key_value_list_length(src); - /* one extra slot for sentinel */ - *dst = libxl__calloc(NOGC, len * 2 + 1, sizeof(char *)); - - for (i = 0; i < len * 2; i += 2) { - (*dst)[i] = libxl__strdup(NOGC, (*src)[i]); - if ((*src)[i+1]) - (*dst)[i+1] = libxl__strdup(NOGC, (*src)[i+1]); - else - (*dst)[i+1] = NULL; - } - -out: - GC_FREE; -} - -void libxl_defbool_set(libxl_defbool *db, bool b) -{ - db->val = b ? LIBXL__DEFBOOL_TRUE : LIBXL__DEFBOOL_FALSE; -} - -void libxl_defbool_unset(libxl_defbool *db) -{ - db->val = LIBXL__DEFBOOL_DEFAULT; -} - -bool libxl_defbool_is_default(libxl_defbool db) -{ - return !db.val; -} - -void libxl_defbool_setdefault(libxl_defbool *db, bool b) -{ - if (libxl_defbool_is_default(*db)) - libxl_defbool_set(db, b); -} - -bool libxl_defbool_val(libxl_defbool db) -{ - assert(!libxl_defbool_is_default(db)); - return db.val > 0; -} - -const char *libxl_defbool_to_string(libxl_defbool b) -{ - if (b.val < 0) - return LIBXL__DEFBOOL_STR_FALSE; - else if (b.val > 0) - return LIBXL__DEFBOOL_STR_TRUE; - else - return LIBXL__DEFBOOL_STR_DEFAULT; -} - -/******************************************************************************/ -int libxl__domain_rename(libxl__gc *gc, uint32_t domid, - const char *old_name, const char *new_name, - xs_transaction_t trans) -{ - libxl_ctx *ctx = libxl__gc_owner(gc); - char *dom_path = 0; - const char *name_path; - char *got_old_name; - unsigned int got_old_len; - xs_transaction_t our_trans = 0; - uint32_t stub_dm_domid; - const char *stub_dm_old_name = NULL, *stub_dm_new_name = NULL; - int rc; - libxl_dominfo info; - char *uuid; - const char *vm_name_path; - - libxl_dominfo_init(&info); - - dom_path = libxl__xs_get_dompath(gc, domid); - if (!dom_path) goto x_nomem; - - name_path= GCSPRINTF("%s/name", dom_path); - if (!name_path) goto x_nomem; - - stub_dm_domid = libxl_get_stubdom_id(CTX, domid); - if (stub_dm_domid) { - stub_dm_old_name = libxl__stub_dm_name(gc, old_name); - stub_dm_new_name = libxl__stub_dm_name(gc, new_name); - } - - retry_transaction: - if (!trans) { - trans = our_trans = xs_transaction_start(ctx->xsh); - if (!our_trans) { - LOGEVD(ERROR, errno, domid, "Create xs transaction for domain (re)name"); - goto x_fail; - } - } - - if (!new_name) { - LOGD(ERROR, domid, "New domain name not specified"); - rc = ERROR_INVAL; - goto x_rc; - } - - if (new_name[0]) { - /* nonempty names must be unique */ - uint32_t domid_e; - rc = libxl_name_to_domid(ctx, new_name, &domid_e); - if (rc == ERROR_INVAL) { - /* no such domain, good */ - } else if (rc != 0) { - LOGD(ERROR, domid, "Unexpected error checking for existing domain"); - goto x_rc; - } else if (domid_e == domid) { - /* domain already has this name, ok (but we do still - * need the rest of the code as we may need to check - * old_name, for example). */ - } else { - LOGD(ERROR, domid, "Domain with name \"%s\" already exists.", new_name); - rc = ERROR_INVAL; - goto x_rc; - } - } - - if (old_name) { - got_old_name = xs_read(ctx->xsh, trans, name_path, &got_old_len); - if (!got_old_name) { - LOGEVD(ERROR, errno, domid, - "Check old name for domain allegedly named `%s'", - old_name); - goto x_fail; - } - if (strcmp(old_name, got_old_name)) { - LOGD(ERROR, domid, - "Allegedly named `%s' is actually named `%s' - racing ?", - old_name, - got_old_name); - free(got_old_name); - goto x_fail; - } - free(got_old_name); - } - if (!xs_write(ctx->xsh, trans, name_path, - new_name, strlen(new_name))) { - LOGD(ERROR, domid, - "Failed to write new name `%s'" - " for domain previously named `%s'", - new_name, - old_name); - goto x_fail; - } - - /* update /vm/<uuid>/name */ - rc = libxl_domain_info(ctx, &info, domid); - if (rc) - goto x_rc; - - uuid = GCSPRINTF(LIBXL_UUID_FMT, LIBXL_UUID_BYTES(info.uuid)); - vm_name_path = GCSPRINTF("/vm/%s/name", uuid); - if (libxl__xs_write_checked(gc, trans, vm_name_path, new_name)) - goto x_fail; - - if (stub_dm_domid) { - rc = libxl__domain_rename(gc, stub_dm_domid, - stub_dm_old_name, - stub_dm_new_name, - trans); - if (rc) { - LOGED(ERROR, domid, "Unable to rename stub-domain"); - goto x_rc; - } - } - - if (our_trans) { - if (!xs_transaction_end(ctx->xsh, our_trans, 0)) { - trans = our_trans = 0; - if (errno != EAGAIN) { - LOGD(ERROR, domid, - "Failed to commit new name `%s'" - " for domain previously named `%s'", - new_name, - old_name); - goto x_fail; - } - LOGD(DEBUG, domid, - "Need to retry rename transaction" - " for domain (name_path=\"%s\", new_name=\"%s\")", - name_path, - new_name); - goto retry_transaction; - } - our_trans = 0; - } - - rc = 0; - x_rc: - if (our_trans) xs_transaction_end(ctx->xsh, our_trans, 1); - libxl_dominfo_dispose(&info); - return rc; - - x_fail: rc = ERROR_FAIL; goto x_rc; - x_nomem: rc = ERROR_NOMEM; goto x_rc; -} - -int libxl_domain_rename(libxl_ctx *ctx, uint32_t domid, - const char *old_name, const char *new_name) -{ - GC_INIT(ctx); - int rc; - rc = libxl__domain_rename(gc, domid, old_name, new_name, XBT_NULL); - GC_FREE; - return rc; -} - -int libxl_domain_resume(libxl_ctx *ctx, uint32_t domid, int suspend_cancel, - const libxl_asyncop_how *ao_how) -{ - AO_CREATE(ctx, domid, ao_how); - int rc = libxl__domain_resume(gc, domid, suspend_cancel); - libxl__ao_complete(egc, ao, rc); - return AO_INPROGRESS; -} - -/* - * Preserves a domain but rewrites xenstore etc to make it unique so - * that the domain can be restarted. - * - * Does not modify info so that it may be reused. - */ -int libxl_domain_preserve(libxl_ctx *ctx, uint32_t domid, - libxl_domain_create_info *info, const char *name_suffix, libxl_uuid new_uuid) -{ - GC_INIT(ctx); - struct xs_permissions roperm[2]; - xs_transaction_t t; - char *preserved_name; - char *uuid_string; - char *vm_path; - char *dom_path; - - int rc; - - preserved_name = GCSPRINTF("%s%s", info->name, name_suffix); - if (!preserved_name) { - GC_FREE; - return ERROR_NOMEM; - } - - uuid_string = libxl__uuid2string(gc, new_uuid); - if (!uuid_string) { - GC_FREE; - return ERROR_NOMEM; - } - - dom_path = libxl__xs_get_dompath(gc, domid); - if (!dom_path) { - GC_FREE; - return ERROR_FAIL; - } - - vm_path = GCSPRINTF("/vm/%s", uuid_string); - if (!vm_path) { - GC_FREE; - return ERROR_FAIL; - } - - roperm[0].id = 0; - roperm[0].perms = XS_PERM_NONE; - roperm[1].id = domid; - roperm[1].perms = XS_PERM_READ; - - retry_transaction: - t = xs_transaction_start(ctx->xsh); - - xs_rm(ctx->xsh, t, vm_path); - xs_mkdir(ctx->xsh, t, vm_path); - xs_set_permissions(ctx->xsh, t, vm_path, roperm, ARRAY_SIZE(roperm)); - - xs_write(ctx->xsh, t, GCSPRINTF("%s/vm", dom_path), vm_path, strlen(vm_path)); - rc = libxl__domain_rename(gc, domid, info->name, preserved_name, t); - if (rc) { - GC_FREE; - return rc; - } - - xs_write(ctx->xsh, t, GCSPRINTF("%s/uuid", vm_path), uuid_string, strlen(uuid_string)); - - if (!xs_transaction_end(ctx->xsh, t, 0)) - if (errno == EAGAIN) - goto retry_transaction; - - GC_FREE; - return 0; -} - -void libxl__xcinfo2xlinfo(libxl_ctx *ctx, - const xc_domaininfo_t *xcinfo, - libxl_dominfo *xlinfo) -{ - size_t size; - - memcpy(&(xlinfo->uuid), xcinfo->handle, sizeof(xen_domain_handle_t)); - xlinfo->domid = xcinfo->domain; - xlinfo->ssidref = xcinfo->ssidref; - if (libxl_flask_sid_to_context(ctx, xlinfo->ssidref, - &xlinfo->ssid_label, &size) < 0) - xlinfo->ssid_label = NULL; - - xlinfo->dying = !!(xcinfo->flags&XEN_DOMINF_dying); - xlinfo->shutdown = !!(xcinfo->flags&XEN_DOMINF_shutdown); - xlinfo->paused = !!(xcinfo->flags&XEN_DOMINF_paused); - xlinfo->blocked = !!(xcinfo->flags&XEN_DOMINF_blocked); - xlinfo->running = !!(xcinfo->flags&XEN_DOMINF_running); - xlinfo->never_stop = !!(xcinfo->flags&XEN_DOMINF_xs_domain); - - if (xlinfo->shutdown) - xlinfo->shutdown_reason = (xcinfo->flags>>XEN_DOMINF_shutdownshift) & XEN_DOMINF_shutdownmask; - else - xlinfo->shutdown_reason = LIBXL_SHUTDOWN_REASON_UNKNOWN; - - xlinfo->outstanding_memkb = PAGE_TO_MEMKB(xcinfo->outstanding_pages); - xlinfo->current_memkb = PAGE_TO_MEMKB(xcinfo->tot_pages); - xlinfo->shared_memkb = PAGE_TO_MEMKB(xcinfo->shr_pages); - xlinfo->paged_memkb = PAGE_TO_MEMKB(xcinfo->paged_pages); - xlinfo->max_memkb = PAGE_TO_MEMKB(xcinfo->max_pages); - xlinfo->cpu_time = xcinfo->cpu_time; - xlinfo->vcpu_max_id = xcinfo->max_vcpu_id; - xlinfo->vcpu_online = xcinfo->nr_online_vcpus; - xlinfo->cpupool = xcinfo->cpupool; - xlinfo->domain_type = (xcinfo->flags & XEN_DOMINF_hvm_guest) ? - LIBXL_DOMAIN_TYPE_HVM : LIBXL_DOMAIN_TYPE_PV; -} - -libxl_dominfo * libxl_list_domain(libxl_ctx *ctx, int *nb_domain_out) -{ - libxl_dominfo *ptr = NULL; - int i, ret; - xc_domaininfo_t info[1024]; - int size = 0; - uint32_t domid = 0; - GC_INIT(ctx); - - while ((ret = xc_domain_getinfolist(ctx->xch, domid, 1024, info)) > 0) { - ptr = libxl__realloc(NOGC, ptr, (size + ret) * sizeof(libxl_dominfo)); - for (i = 0; i < ret; i++) { - libxl__xcinfo2xlinfo(ctx, &info[i], &ptr[size + i]); - } - domid = info[ret - 1].domain + 1; - size += ret; - } - - if (ret < 0) { - LOGE(ERROR, "getting domain info list"); - free(ptr); - GC_FREE; - return NULL; - } - - *nb_domain_out = size; - GC_FREE; - return ptr; -} - -int libxl_domain_info(libxl_ctx *ctx, libxl_dominfo *info_r, - uint32_t domid) { - xc_domaininfo_t xcinfo; - int ret; - GC_INIT(ctx); - - ret = xc_domain_getinfolist(ctx->xch, domid, 1, &xcinfo); - if (ret<0) { - LOGED(ERROR, domid, "Getting domain info list"); - GC_FREE; - return ERROR_FAIL; - } - if (ret==0 || xcinfo.domain != domid) { - GC_FREE; - return ERROR_DOMAIN_NOTFOUND; - } - - if (info_r) - libxl__xcinfo2xlinfo(ctx, &xcinfo, info_r); - GC_FREE; - return 0; -} - -/* this API call only list VM running on this host. A VM can - * be an aggregate of multiple domains. */ -libxl_vminfo * libxl_list_vm(libxl_ctx *ctx, int *nb_vm_out) -{ - GC_INIT(ctx); - libxl_dominfo *info; - libxl_vminfo *ptr = NULL; - int idx, i, n_doms; - - info = libxl_list_domain(ctx, &n_doms); - if (!info) - goto out; - - /* - * Always make sure to allocate at least one element; if we don't and we - * request zero, libxl__calloc (might) think its internal call to calloc - * has failed (if it returns null), if so it would kill our process. - */ - ptr = libxl__calloc(NOGC, n_doms ? n_doms : 1, sizeof(libxl_vminfo)); - - for (idx = i = 0; i < n_doms; i++) { - if (libxl_is_stubdom(ctx, info[i].domid, NULL)) - continue; - ptr[idx].uuid = info[i].uuid; - ptr[idx].domid = info[i].domid; - - idx++; - } - *nb_vm_out = idx; - libxl_dominfo_list_free(info, n_doms); - -out: - GC_FREE; - return ptr; -} - -static void remus_failover_cb(libxl__egc *egc, - libxl__domain_save_state *dss, int rc); - -int 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) -{ - AO_CREATE(ctx, domid, ao_how); - libxl__domain_save_state *dss; - int rc; - - libxl_domain_type type = libxl__domain_type(gc, domid); - if (type == LIBXL_DOMAIN_TYPE_INVALID) { - rc = ERROR_FAIL; - goto out; - } - - /* The caller must set this defbool */ - if (libxl_defbool_is_default(info->colo)) { - LOGD(ERROR, domid, "Colo mode must be enabled/disabled"); - rc = ERROR_FAIL; - goto out; - } - - libxl_defbool_setdefault(&info->allow_unsafe, false); - libxl_defbool_setdefault(&info->blackhole, false); - libxl_defbool_setdefault(&info->compression, - !libxl_defbool_val(info->colo)); - libxl_defbool_setdefault(&info->netbuf, true); - libxl_defbool_setdefault(&info->diskbuf, true); - - if (libxl_defbool_val(info->colo) && - libxl_defbool_val(info->compression)) { - LOGD(ERROR, domid, "Cannot use memory checkpoint " - "compression in COLO mode"); - rc = ERROR_FAIL; - goto out; - } - - if (!libxl_defbool_val(info->allow_unsafe) && - (libxl_defbool_val(info->blackhole) || - !libxl_defbool_val(info->netbuf) || - !libxl_defbool_val(info->diskbuf))) { - LOGD(ERROR, domid, "Unsafe mode must be enabled to replicate to /dev/null," - "disable network buffering and disk replication"); - rc = ERROR_FAIL; - goto out; - } - - - GCNEW(dss); - dss->ao = ao; - dss->callback = remus_failover_cb; - dss->domid = domid; - dss->fd = send_fd; - dss->recv_fd = recv_fd; - dss->type = type; - dss->live = 1; - dss->debug = 0; - dss->remus = info; - if (libxl_defbool_val(info->colo)) - dss->checkpointed_stream = LIBXL_CHECKPOINTED_STREAM_COLO; - else - dss->checkpointed_stream = LIBXL_CHECKPOINTED_STREAM_REMUS; - - assert(info); - - /* Point of no return */ - if (libxl_defbool_val(info->colo)) - libxl__colo_save_setup(egc, &dss->css); - else - libxl__remus_setup(egc, &dss->rs); - return AO_INPROGRESS; - - out: - return AO_CREATE_FAIL(rc); -} - -static void remus_failover_cb(libxl__egc *egc, - libxl__domain_save_state *dss, int rc) -{ - STATE_AO_GC(dss->ao); - /* - * With Remus, if we reach this point, it means either - * backup died or some network error occurred preventing us - * from sending checkpoints. - */ - libxl__ao_complete(egc, ao, rc); -} - -static void domain_suspend_cb(libxl__egc *egc, - libxl__domain_save_state *dss, int rc) -{ - STATE_AO_GC(dss->ao); - int flrc; - - flrc = libxl__fd_flags_restore(gc, dss->fd, dss->fdfl); - /* If suspend has failed already then report that error not this one. */ - if (flrc && !rc) rc = flrc; - - libxl__ao_complete(egc,ao,rc); - -} - -int libxl_domain_suspend(libxl_ctx *ctx, uint32_t domid, int fd, int flags, - const libxl_asyncop_how *ao_how) -{ - AO_CREATE(ctx, domid, ao_how); - int rc; - - libxl_domain_type type = libxl__domain_type(gc, domid); - if (type == LIBXL_DOMAIN_TYPE_INVALID) { - rc = ERROR_FAIL; - goto out_err; - } - - libxl__domain_save_state *dss; - GCNEW(dss); - - dss->ao = ao; - dss->callback = domain_suspend_cb; - - dss->domid = domid; - dss->fd = fd; - dss->type = type; - dss->live = flags & LIBXL_SUSPEND_LIVE; - dss->debug = flags & LIBXL_SUSPEND_DEBUG; - dss->checkpointed_stream = LIBXL_CHECKPOINTED_STREAM_NONE; - - rc = libxl__fd_flags_modify_save(gc, dss->fd, - ~(O_NONBLOCK|O_NDELAY), 0, - &dss->fdfl); - if (rc < 0) goto out_err; - - libxl__domain_save(egc, dss); - return AO_INPROGRESS; - - out_err: - return AO_CREATE_FAIL(rc); -} - -int libxl_domain_pause(libxl_ctx *ctx, uint32_t domid) -{ - int ret; - GC_INIT(ctx); - ret = xc_domain_pause(ctx->xch, domid); - if (ret<0) { - LOGED(ERROR, domid, "Pausing domain"); - GC_FREE; - return ERROR_FAIL; - } - GC_FREE; - return 0; -} - -int libxl_domain_core_dump(libxl_ctx *ctx, uint32_t domid, - const char *filename, - const libxl_asyncop_how *ao_how) -{ - AO_CREATE(ctx, domid, ao_how); - int ret, rc; - - ret = xc_domain_dumpcore(ctx->xch, domid, filename); - if (ret<0) { - LOGED(ERROR, domid, "Core dumping domain to %s", filename); - rc = ERROR_FAIL; - goto out; - } - - rc = 0; -out: - - libxl__ao_complete(egc, ao, rc); - - return AO_INPROGRESS; -} - -int libxl_domain_unpause(libxl_ctx *ctx, uint32_t domid) -{ - GC_INIT(ctx); - int ret, rc = 0; - - libxl_domain_type type = libxl__domain_type(gc, domid); - if (type == LIBXL_DOMAIN_TYPE_INVALID) { - rc = ERROR_FAIL; - goto out; - } - - if (type == LIBXL_DOMAIN_TYPE_HVM) { - if (libxl__device_model_version_running(gc, domid) != - LIBXL_DEVICE_MODEL_VERSION_NONE) { - rc = libxl__domain_resume_device_model(gc, domid); - if (rc < 0) { - LOGD(ERROR, domid, "Failed to unpause device model for domain:%d", - rc); - goto out; - } - } - } - ret = xc_domain_unpause(ctx->xch, domid); - if (ret<0) { - LOGED(ERROR, domid, "Unpausing domain"); - rc = ERROR_FAIL; - } - out: - GC_FREE; - return rc; -} - -int libxl__domain_pvcontrol_available(libxl__gc *gc, uint32_t domid) -{ - libxl_ctx *ctx = libxl__gc_owner(gc); - - uint64_t pvdriver = 0; - int ret; - - libxl_domain_type domtype = libxl__domain_type(gc, domid); - if (domtype == LIBXL_DOMAIN_TYPE_INVALID) - return ERROR_FAIL; - - if (domtype == LIBXL_DOMAIN_TYPE_PV) - return 1; - - ret = xc_hvm_param_get(ctx->xch, domid, HVM_PARAM_CALLBACK_IRQ, &pvdriver); - if (ret<0) { - LOGED(ERROR, domid, "Getting HVM callback IRQ"); - return ERROR_FAIL; - } - return !!pvdriver; -} - -const char *libxl__domain_pvcontrol_xspath(libxl__gc *gc, uint32_t domid) -{ - const char *dom_path; - - dom_path = libxl__xs_get_dompath(gc, domid); - if (!dom_path) - return NULL; - - return GCSPRINTF("%s/control/shutdown", dom_path); -} - -char * libxl__domain_pvcontrol_read(libxl__gc *gc, xs_transaction_t t, - uint32_t domid) -{ - const char *shutdown_path; - - shutdown_path = libxl__domain_pvcontrol_xspath(gc, domid); - if (!shutdown_path) - return NULL; - - return libxl__xs_read(gc, t, shutdown_path); -} - -int libxl__domain_pvcontrol_write(libxl__gc *gc, xs_transaction_t t, - uint32_t domid, const char *cmd) -{ - const char *shutdown_path; - - shutdown_path = libxl__domain_pvcontrol_xspath(gc, domid); - if (!shutdown_path) - return ERROR_FAIL; - - return libxl__xs_printf(gc, t, shutdown_path, "%s", cmd); -} - -static int libxl__domain_pvcontrol(libxl__gc *gc, uint32_t domid, - const char *cmd) -{ - int ret; - - ret = libxl__domain_pvcontrol_available(gc, domid); - if (ret < 0) - return ret; - - if (!ret) - return ERROR_NOPARAVIRT; - - return libxl__domain_pvcontrol_write(gc, XBT_NULL, domid, cmd); -} - -int libxl_domain_shutdown(libxl_ctx *ctx, uint32_t domid) -{ - GC_INIT(ctx); - int ret; - ret = libxl__domain_pvcontrol(gc, domid, "poweroff"); - GC_FREE; - return ret; -} - -int libxl_domain_reboot(libxl_ctx *ctx, uint32_t domid) -{ - GC_INIT(ctx); - int ret; - ret = libxl__domain_pvcontrol(gc, domid, "reboot"); - GC_FREE; - return ret; -} - -static void domain_death_occurred(libxl__egc *egc, - libxl_evgen_domain_death **evg_upd, - const char *why) { - /* Removes **evg_upd from death_list and puts it on death_reported - * and advances *evg_upd to the next entry. - * Call sites in domain_death_xswatch_callback must use "continue". */ - EGC_GC; - libxl_evgen_domain_death *const evg = *evg_upd; - - LOGD(DEBUG, evg->domid, "%s", why); - - libxl_evgen_domain_death *evg_next = LIBXL_TAILQ_NEXT(evg, entry); - *evg_upd = evg_next; - - libxl_event *ev = NEW_EVENT(egc, DOMAIN_DEATH, evg->domid, evg->user); - - libxl__event_occurred(egc, ev); - - evg->death_reported = 1; - LIBXL_TAILQ_REMOVE(&CTX->death_list, evg, entry); - LIBXL_TAILQ_INSERT_HEAD(&CTX->death_reported, evg, entry); -} - -static void domain_death_xswatch_callback(libxl__egc *egc, libxl__ev_xswatch *w, - const char *wpath, const char *epath) { - EGC_GC; - libxl_evgen_domain_death *evg; - int rc; - - CTX_LOCK; - - evg = LIBXL_TAILQ_FIRST(&CTX->death_list); - - for (;;) { - if (!evg) goto out; - - int nentries = LIBXL_TAILQ_NEXT(evg, entry) ? 200 : 1; - xc_domaininfo_t domaininfos[nentries]; - const xc_domaininfo_t *got = domaininfos, *gotend; - - rc = xc_domain_getinfolist(CTX->xch, evg->domid, nentries, domaininfos); - if (rc == -1) { - LIBXL__EVENT_DISASTER(egc, "xc_domain_getinfolist failed while" - " processing @releaseDomain watch event", - errno, 0); - goto out; - } - gotend = &domaininfos[rc]; - - LOGD(DEBUG, evg->domid, "[evg=%p] nentries=%d rc=%d %ld..%ld", - evg, nentries, rc, - rc>0 ? (long)domaininfos[0].domain : 0, - rc>0 ? (long)domaininfos[rc-1].domain : 0); - - for (;;) { - if (!evg) { - LOG(DEBUG, "[evg=0] all reported"); - goto all_reported; - } - - LOGD(DEBUG, evg->domid, "[evg=%p]" - " got=domaininfos[%d] got->domain=%ld", - evg, (int)(got - domaininfos), - got < gotend ? (long)got->domain : -1L); - - if (!rc) { - domain_death_occurred(egc, &evg, "empty list"); - continue; - } - - if (got == gotend) { - LOG(DEBUG, " got==gotend"); - break; - } - - if (got->domain > evg->domid) { - /* ie, the list doesn't contain evg->domid any more so - * the domain has been destroyed */ - domain_death_occurred(egc, &evg, "missing from list"); - continue; - } - - if (got->domain < evg->domid) { - got++; - continue; - } - - assert(evg->domid == got->domain); - LOGD(DEBUG, evg->domid, "Exists shutdown_reported=%d"" dominf.flags=%x", - evg->shutdown_reported, got->flags); - - if (got->flags & XEN_DOMINF_dying) { - domain_death_occurred(egc, &evg, "dying"); - continue; - } - - if (!evg->shutdown_reported && - (got->flags & XEN_DOMINF_shutdown)) { - libxl_event *ev = NEW_EVENT(egc, DOMAIN_SHUTDOWN, - got->domain, evg->user); - - LOG(DEBUG, " shutdown reporting"); - - ev->u.domain_shutdown.shutdown_reason = - (got->flags >> XEN_DOMINF_shutdownshift) & - XEN_DOMINF_shutdownmask; - libxl__event_occurred(egc, ev); - - evg->shutdown_reported = 1; - } - evg = LIBXL_TAILQ_NEXT(evg, entry); - } - - assert(rc); /* rc==0 results in us eating all evgs and quitting */ - } - all_reported: - out: - - LOG(DEBUG, "domain death search done"); - - CTX_UNLOCK; -} - -int libxl_evenable_domain_death(libxl_ctx *ctx, uint32_t domid, - libxl_ev_user user, libxl_evgen_domain_death **evgen_out) { - GC_INIT(ctx); - libxl_evgen_domain_death *evg, *evg_search; - int rc; - - CTX_LOCK; - - evg = malloc(sizeof(*evg)); if (!evg) { rc = ERROR_NOMEM; goto out; } - memset(evg, 0, sizeof(*evg)); - evg->domid = domid; - evg->user = user; - - LIBXL_TAILQ_INSERT_SORTED(&ctx->death_list, entry, evg, evg_search, , - evg->domid > evg_search->domid); - - if (!libxl__ev_xswatch_isregistered(&ctx->death_watch)) { - rc = libxl__ev_xswatch_register(gc, &ctx->death_watch, - domain_death_xswatch_callback, "@releaseDomain"); - if (rc) { libxl__evdisable_domain_death(gc, evg); goto out; } - } - - *evgen_out = evg; - rc = 0; - - out: - CTX_UNLOCK; - GC_FREE; - return rc; -}; - -void libxl__evdisable_domain_death(libxl__gc *gc, - libxl_evgen_domain_death *evg) { - CTX_LOCK; - - if (!evg->death_reported) - LIBXL_TAILQ_REMOVE(&CTX->death_list, evg, entry); - else - LIBXL_TAILQ_REMOVE(&CTX->death_reported, evg, entry); - - free(evg); - - if (!LIBXL_TAILQ_FIRST(&CTX->death_list) && - libxl__ev_xswatch_isregistered(&CTX->death_watch)) - libxl__ev_xswatch_deregister(gc, &CTX->death_watch); - - CTX_UNLOCK; -} - -void libxl_evdisable_domain_death(libxl_ctx *ctx, - libxl_evgen_domain_death *evg) { - GC_INIT(ctx); - libxl__evdisable_domain_death(gc, evg); - GC_FREE; -} - -/* Callbacks for libxl_domain_destroy */ - -static void domain_destroy_cb(libxl__egc *egc, libxl__domain_destroy_state *dds, - int rc); - -int libxl_domain_destroy(libxl_ctx *ctx, uint32_t domid, - const libxl_asyncop_how *ao_how) -{ - AO_CREATE(ctx, domid, ao_how); - libxl__domain_destroy_state *dds; - - GCNEW(dds); - dds->ao = ao; - dds->domid = domid; - dds->callback = domain_destroy_cb; - libxl__domain_destroy(egc, dds); - - return AO_INPROGRESS; -} - -static void domain_destroy_cb(libxl__egc *egc, libxl__domain_destroy_state *dds, - int rc) -{ - STATE_AO_GC(dds->ao); - - if (rc) - LOGD(ERROR, dds->domid, "Destruction of domain failed"); - - libxl__ao_complete(egc, ao, rc); -} - -/* Callbacks for libxl__domain_destroy */ - -static void stubdom_destroy_callback(libxl__egc *egc, - libxl__destroy_domid_state *dis, - int rc); - -static void domain_destroy_callback(libxl__egc *egc, - libxl__destroy_domid_state *dis, - int rc); - -static void destroy_finish_check(libxl__egc *egc, - libxl__domain_destroy_state *dds); - -void libxl__domain_destroy(libxl__egc *egc, libxl__domain_destroy_state *dds) -{ - STATE_AO_GC(dds->ao); - uint32_t stubdomid = libxl_get_stubdom_id(CTX, dds->domid); - - if (stubdomid) { - dds->stubdom.ao = ao; - dds->stubdom.domid = stubdomid; - dds->stubdom.callback = stubdom_destroy_callback; - dds->stubdom.soft_reset = false; - libxl__destroy_domid(egc, &dds->stubdom); - } else { - dds->stubdom_finished = 1; - } - - dds->domain.ao = ao; - dds->domain.domid = dds->domid; - dds->domain.callback = domain_destroy_callback; - dds->domain.soft_reset = dds->soft_reset; - libxl__destroy_domid(egc, &dds->domain); -} - -static void stubdom_destroy_callback(libxl__egc *egc, - libxl__destroy_domid_state *dis, - int rc) -{ - STATE_AO_GC(dis->ao); - libxl__domain_destroy_state *dds = CONTAINER_OF(dis, *dds, stubdom); - const char *savefile; - - if (rc) { - LOGD(ERROR, dds->domain.domid, "Unable to destroy stubdom with domid %u", - dis->domid); - dds->rc = rc; - } - - dds->stubdom_finished = 1; - savefile = libxl__device_model_savefile(gc, dis->domid); - rc = libxl__remove_file(gc, savefile); - if (rc) { - LOGD(ERROR, dds->domain.domid, "Failed to remove device-model savefile %s", - savefile); - } - - destroy_finish_check(egc, dds); -} - -static void domain_destroy_callback(libxl__egc *egc, - libxl__destroy_domid_state *dis, - int rc) -{ - STATE_AO_GC(dis->ao); - libxl__domain_destroy_state *dds = CONTAINER_OF(dis, *dds, domain); - - if (rc) { - LOGD(ERROR, dis->domid, "Unable to destroy guest"); - dds->rc = rc; - } - - dds->domain_finished = 1; - destroy_finish_check(egc, dds); -} - -static void destroy_finish_check(libxl__egc *egc, - libxl__domain_destroy_state *dds) -{ - if (!(dds->domain_finished && dds->stubdom_finished)) - return; - - dds->callback(egc, dds, dds->rc); -} - -/* Callbacks for libxl__destroy_domid */ -static void devices_destroy_cb(libxl__egc *egc, - libxl__devices_remove_state *drs, - int rc); - -static void domain_destroy_domid_cb(libxl__egc *egc, - libxl__ev_child *destroyer, - pid_t pid, int status); - -void libxl__destroy_domid(libxl__egc *egc, libxl__destroy_domid_state *dis) -{ - STATE_AO_GC(dis->ao); - libxl_ctx *ctx = CTX; - uint32_t domid = dis->domid; - char *dom_path; - int rc, dm_present; - - libxl__ev_child_init(&dis->destroyer); - - rc = libxl_domain_info(ctx, NULL, domid); - switch(rc) { - case 0: - break; - case ERROR_DOMAIN_NOTFOUND: - LOGD(ERROR, domid, "Non-existant domain"); - default: - goto out; - } - - switch (libxl__domain_type(gc, domid)) { - case LIBXL_DOMAIN_TYPE_HVM: - if (libxl_get_stubdom_id(CTX, domid)) { - dm_present = 0; - break; - } - /* fall through */ - case LIBXL_DOMAIN_TYPE_PV: - dm_present = libxl__dm_active(gc, domid); - break; - case LIBXL_DOMAIN_TYPE_INVALID: - rc = ERROR_FAIL; - goto out; - default: - abort(); - } - - dom_path = libxl__xs_get_dompath(gc, domid); - if (!dom_path) { - rc = ERROR_FAIL; - goto out; - } - - if (libxl__device_pci_destroy_all(gc, domid) < 0) - LOGD(ERROR, domid, "Pci shutdown failed"); - rc = xc_domain_pause(ctx->xch, domid); - if (rc < 0) { - LOGEVD(ERROR, rc, domid, "xc_domain_pause failed"); - } - if (dm_present) { - if (libxl__destroy_device_model(gc, domid) < 0) - LOGD(ERROR, domid, "libxl__destroy_device_model failed"); - - libxl__qmp_cleanup(gc, domid); - } - dis->drs.ao = ao; - dis->drs.domid = domid; - dis->drs.callback = devices_destroy_cb; - dis->drs.force = 1; - libxl__devices_destroy(egc, &dis->drs); - return; - -out: - assert(rc); - dis->callback(egc, dis, rc); - return; -} - -static void devices_destroy_cb(libxl__egc *egc, - libxl__devices_remove_state *drs, - int rc) +void libxl_key_value_list_copy(libxl_ctx *ctx, + libxl_key_value_list *dst, + const libxl_key_value_list *src) { - STATE_AO_GC(drs->ao); - libxl__destroy_domid_state *dis = CONTAINER_OF(drs, *dis, drs); - libxl_ctx *ctx = CTX; - uint32_t domid = dis->domid; - char *dom_path; - char *vm_path; - libxl__domain_userdata_lock *lock; - - dom_path = libxl__xs_get_dompath(gc, domid); - if (!dom_path) { - rc = ERROR_FAIL; + GC_INIT(ctx); + int i, len; + + if (*src == NULL) { + *dst = NULL; goto out; } - if (rc < 0) - LOGD(ERROR, domid, "libxl__devices_destroy failed"); - - vm_path = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/vm", dom_path)); - if (vm_path) - if (!xs_rm(ctx->xsh, XBT_NULL, vm_path)) - LOGED(ERROR, domid, "xs_rm failed for %s", vm_path); - - if (!xs_rm(ctx->xsh, XBT_NULL, dom_path)) - LOGED(ERROR, domid, "xs_rm failed for %s", dom_path); - - xs_rm(ctx->xsh, XBT_NULL, libxl__xs_libxl_path(gc, domid)); - xs_rm(ctx->xsh, XBT_NULL, GCSPRINTF( "/local/domain/%d/hvmloader", domid)); + len = libxl_key_value_list_length(src); + /* one extra slot for sentinel */ + *dst = libxl__calloc(NOGC, len * 2 + 1, sizeof(char *)); - /* This is async operation, we already hold CTX lock */ - lock = libxl__lock_domain_userdata(gc, domid); - if (!lock) { - rc = ERROR_LOCK_FAIL; - goto out; - } - libxl__userdata_destroyall(gc, domid); - - libxl__unlock_domain_userdata(lock); - - /* Clean up qemu-save and qemu-resume files. They are - * intermediate files created by libxc. Unfortunately they - * don't fit in existing userdata scheme very well. In soft reset - * case we need to keep the file. - */ - if (!dis->soft_reset) { - rc = libxl__remove_file(gc, - libxl__device_model_savefile(gc, domid)); - if (rc < 0) goto out; - } - rc = libxl__remove_file(gc, - GCSPRINTF(LIBXL_DEVICE_MODEL_RESTORE_FILE".%u", domid)); - if (rc < 0) goto out; - - rc = libxl__ev_child_fork(gc, &dis->destroyer, domain_destroy_domid_cb); - if (rc < 0) goto out; - if (!rc) { /* child */ - ctx->xch = xc_interface_open(ctx->lg,0,0); - if (!ctx->xch) goto badchild; - - if (!dis->soft_reset) { - rc = xc_domain_destroy(ctx->xch, domid); - } else { - rc = xc_domain_pause(ctx->xch, domid); - if (rc < 0) goto badchild; - rc = xc_domain_soft_reset(ctx->xch, domid); - if (rc < 0) goto badchild; - rc = xc_domain_unpause(ctx->xch, domid); - } - if (rc < 0) goto badchild; - _exit(0); - - badchild: - if (errno > 0 && errno < 126) { - _exit(errno); - } else { - LOGED(ERROR, domid, - "xc_domain_destroy failed (with difficult errno value %d)", - errno); - _exit(-1); - } + for (i = 0; i < len * 2; i += 2) { + (*dst)[i] = libxl__strdup(NOGC, (*src)[i]); + if ((*src)[i+1]) + (*dst)[i+1] = libxl__strdup(NOGC, (*src)[i+1]); + else + (*dst)[i+1] = NULL; } - LOGD(DEBUG, domid, "Forked pid %ld for destroy of domain", (long)rc); - - return; out: - dis->callback(egc, dis, rc); - return; + GC_FREE; } -static void domain_destroy_domid_cb(libxl__egc *egc, - libxl__ev_child *destroyer, - pid_t pid, int status) +void libxl_defbool_set(libxl_defbool *db, bool b) { - libxl__destroy_domid_state *dis = CONTAINER_OF(destroyer, *dis, destroyer); - STATE_AO_GC(dis->ao); - int rc; - - if (status) { - if (WIFEXITED(status) && WEXITSTATUS(status)<126) { - LOGEVD(ERROR, WEXITSTATUS(status), dis->domid, - "xc_domain_destroy failed"); - } else { - libxl_report_child_exitstatus(CTX, XTL_ERROR, - "async domain destroy", pid, status); - } - rc = ERROR_FAIL; - goto out; - } - rc = 0; - - out: - dis->callback(egc, dis, rc); + db->val = b ? LIBXL__DEFBOOL_TRUE : LIBXL__DEFBOOL_FALSE; } -int libxl__get_domid(libxl__gc *gc, uint32_t *domid) +void libxl_defbool_unset(libxl_defbool *db) { - int rc; - const char *xs_domid; - - rc = libxl__xs_read_checked(gc, XBT_NULL, DOMID_XS_PATH, &xs_domid); - if (rc) goto out; - if (!xs_domid) { - LOG(ERROR, "failed to get own domid (%s)", DOMID_XS_PATH); - rc = ERROR_FAIL; - goto out; - } + db->val = LIBXL__DEFBOOL_DEFAULT; +} - *domid = atoi(xs_domid); +bool libxl_defbool_is_default(libxl_defbool db) +{ + return !db.val; +} -out: - return rc; +void libxl_defbool_setdefault(libxl_defbool *db, bool b) +{ + if (libxl_defbool_is_default(*db)) + libxl_defbool_set(db, b); } -/******************************************************************************/ +bool libxl_defbool_val(libxl_defbool db) +{ + assert(!libxl_defbool_is_default(db)); + return db.val > 0; +} -int libxl__resolve_domid(libxl__gc *gc, const char *name, uint32_t *domid) +const char *libxl_defbool_to_string(libxl_defbool b) { - if (!name) - return 0; - return libxl_domain_qualifier_to_domid(CTX, name, domid); + if (b.val < 0) + return LIBXL__DEFBOOL_STR_FALSE; + else if (b.val > 0) + return LIBXL__DEFBOOL_STR_TRUE; + else + return LIBXL__DEFBOOL_STR_DEFAULT; } /******************************************************************************/ @@ -1810,254 +626,6 @@ const libxl_version_info* libxl_get_version_info(libxl_ctx *ctx) return info; } -libxl_vcpuinfo *libxl_list_vcpu(libxl_ctx *ctx, uint32_t domid, - int *nr_vcpus_out, int *nr_cpus_out) -{ - GC_INIT(ctx); - libxl_vcpuinfo *ptr, *ret; - xc_domaininfo_t domaininfo; - xc_vcpuinfo_t vcpuinfo; - - if (xc_domain_getinfolist(ctx->xch, domid, 1, &domaininfo) != 1) { - LOGED(ERROR, domid, "Getting infolist"); - GC_FREE; - return NULL; - } - - if (domaininfo.max_vcpu_id == XEN_INVALID_MAX_VCPU_ID) { - GC_FREE; - return NULL; - } - - *nr_cpus_out = libxl_get_max_cpus(ctx); - ret = ptr = libxl__calloc(NOGC, domaininfo.max_vcpu_id + 1, - sizeof(libxl_vcpuinfo)); - - for (*nr_vcpus_out = 0; - *nr_vcpus_out <= domaininfo.max_vcpu_id; - ++*nr_vcpus_out, ++ptr) { - libxl_bitmap_init(&ptr->cpumap); - if (libxl_cpu_bitmap_alloc(ctx, &ptr->cpumap, 0)) - goto err; - libxl_bitmap_init(&ptr->cpumap_soft); - if (libxl_cpu_bitmap_alloc(ctx, &ptr->cpumap_soft, 0)) - goto err; - if (xc_vcpu_getinfo(ctx->xch, domid, *nr_vcpus_out, &vcpuinfo) == -1) { - LOGED(ERROR, domid, "Getting vcpu info"); - goto err; - } - - if (xc_vcpu_getaffinity(ctx->xch, domid, *nr_vcpus_out, - ptr->cpumap.map, ptr->cpumap_soft.map, - XEN_VCPUAFFINITY_SOFT|XEN_VCPUAFFINITY_HARD) == -1) { - LOGED(ERROR, domid, "Getting vcpu affinity"); - goto err; - } - ptr->vcpuid = *nr_vcpus_out; - ptr->cpu = vcpuinfo.cpu; - ptr->online = !!vcpuinfo.online; - ptr->blocked = !!vcpuinfo.blocked; - ptr->running = !!vcpuinfo.running; - ptr->vcpu_time = vcpuinfo.cpu_time; - } - GC_FREE; - return ret; - -err: - libxl_bitmap_dispose(&ptr->cpumap); - libxl_bitmap_dispose(&ptr->cpumap_soft); - free(ret); - GC_FREE; - return NULL; -} - -static int libxl__set_vcpuonline_xenstore(libxl__gc *gc, uint32_t domid, - libxl_bitmap *cpumap, - const libxl_dominfo *info) -{ - char *dompath; - xs_transaction_t t; - int i, rc = ERROR_FAIL; - - if (!(dompath = libxl__xs_get_dompath(gc, domid))) - goto out; - -retry_transaction: - t = xs_transaction_start(CTX->xsh); - for (i = 0; i <= info->vcpu_max_id; i++) - libxl__xs_printf(gc, t, - GCSPRINTF("%s/cpu/%u/availability", dompath, i), - "%s", libxl_bitmap_test(cpumap, i) ? "online" : "offline"); - if (!xs_transaction_end(CTX->xsh, t, 0)) { - if (errno == EAGAIN) - goto retry_transaction; - } else - rc = 0; -out: - return rc; -} - -static int libxl__set_vcpuonline_qmp(libxl__gc *gc, uint32_t domid, - libxl_bitmap *cpumap, - const libxl_dominfo *info) -{ - int i, rc; - libxl_bitmap current_map, final_map; - - libxl_bitmap_init(¤t_map); - libxl_bitmap_init(&final_map); - - libxl_bitmap_alloc(CTX, ¤t_map, info->vcpu_max_id + 1); - libxl_bitmap_set_none(¤t_map); - rc = libxl__qmp_query_cpus(gc, domid, ¤t_map); - if (rc) { - LOGD(ERROR, domid, "Failed to query cpus"); - goto out; - } - - libxl_bitmap_copy_alloc(CTX, &final_map, cpumap); - - libxl_for_each_set_bit(i, current_map) - libxl_bitmap_reset(&final_map, i); - - libxl_for_each_set_bit(i, final_map) { - rc = libxl__qmp_cpu_add(gc, domid, i); - if (rc) { - LOGD(ERROR, domid, "Failed to add cpu %d", i); - goto out; - } - } - - rc = 0; -out: - libxl_bitmap_dispose(¤t_map); - libxl_bitmap_dispose(&final_map); - return rc; -} - -int libxl_set_vcpuonline(libxl_ctx *ctx, uint32_t domid, libxl_bitmap *cpumap) -{ - GC_INIT(ctx); - int rc, maxcpus; - libxl_dominfo info; - - libxl_dominfo_init(&info); - - rc = libxl_domain_info(CTX, &info, domid); - if (rc < 0) { - LOGED(ERROR, domid, "Getting domain info list"); - goto out; - } - - maxcpus = libxl_bitmap_count_set(cpumap); - if (maxcpus > info.vcpu_max_id + 1) - { - LOGED(ERROR, domid, "Requested %d VCPUs, however maxcpus is %d!", - maxcpus, info.vcpu_max_id + 1); - rc = ERROR_FAIL; - goto out; - } - - switch (libxl__domain_type(gc, domid)) { - case LIBXL_DOMAIN_TYPE_HVM: - switch (libxl__device_model_version_running(gc, domid)) { - case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN_TRADITIONAL: - case LIBXL_DEVICE_MODEL_VERSION_NONE: - break; - case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN: - rc = libxl__set_vcpuonline_qmp(gc, domid, cpumap, &info); - break; - default: - rc = ERROR_INVAL; - } - break; - case LIBXL_DOMAIN_TYPE_PV: - break; - default: - rc = ERROR_INVAL; - } - - if (!rc) - rc = libxl__set_vcpuonline_xenstore(gc, domid, cpumap, &info); - -out: - libxl_dominfo_dispose(&info); - GC_FREE; - return rc; -} - -static int libxl__domain_s3_resume(libxl__gc *gc, int domid) -{ - int rc = 0; - - switch (libxl__domain_type(gc, domid)) { - case LIBXL_DOMAIN_TYPE_HVM: - switch (libxl__device_model_version_running(gc, domid)) { - case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN_TRADITIONAL: - rc = xc_hvm_param_set(CTX->xch, domid, HVM_PARAM_ACPI_S_STATE, 0); - break; - case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN: - rc = libxl__qmp_system_wakeup(gc, domid); - break; - default: - rc = ERROR_INVAL; - break; - } - break; - default: - rc = ERROR_INVAL; - break; - } - - return rc; -} - -int libxl_send_trigger(libxl_ctx *ctx, uint32_t domid, - libxl_trigger trigger, uint32_t vcpuid) -{ - int rc; - GC_INIT(ctx); - - switch (trigger) { - case LIBXL_TRIGGER_POWER: - rc = xc_domain_send_trigger(ctx->xch, domid, - XEN_DOMCTL_SENDTRIGGER_POWER, vcpuid); - break; - case LIBXL_TRIGGER_SLEEP: - rc = xc_domain_send_trigger(ctx->xch, domid, - XEN_DOMCTL_SENDTRIGGER_SLEEP, vcpuid); - break; - case LIBXL_TRIGGER_NMI: - rc = xc_domain_send_trigger(ctx->xch, domid, - XEN_DOMCTL_SENDTRIGGER_NMI, vcpuid); - break; - case LIBXL_TRIGGER_INIT: - rc = xc_domain_send_trigger(ctx->xch, domid, - XEN_DOMCTL_SENDTRIGGER_INIT, vcpuid); - break; - case LIBXL_TRIGGER_RESET: - rc = xc_domain_send_trigger(ctx->xch, domid, - XEN_DOMCTL_SENDTRIGGER_RESET, vcpuid); - break; - case LIBXL_TRIGGER_S3RESUME: - rc = libxl__domain_s3_resume(gc, domid); - break; - default: - rc = -1; - errno = EINVAL; - break; - } - - if (rc != 0) { - LOGED(ERROR, domid, "Send trigger '%s' failed", - libxl_trigger_to_string(trigger)); - rc = ERROR_FAIL; - } - - GC_FREE; - return rc; -} - int libxl_send_sysrq(libxl_ctx *ctx, uint32_t domid, char sysrq) { GC_INIT(ctx); @@ -2084,27 +652,6 @@ int libxl_send_debug_keys(libxl_ctx *ctx, char *keys) return 0; } -uint32_t libxl_vm_get_start_time(libxl_ctx *ctx, uint32_t domid) -{ - GC_INIT(ctx); - char *dompath = libxl__xs_get_dompath(gc, domid); - char *vm_path, *start_time; - uint32_t ret; - - vm_path = libxl__xs_read( - gc, XBT_NULL, GCSPRINTF("%s/vm", dompath)); - start_time = libxl__xs_read( - gc, XBT_NULL, GCSPRINTF("%s/start_time", vm_path)); - if (start_time == NULL) { - LOGEVD(ERROR, -1, domid, "Can't get start time of domain"); - ret = -1; - }else{ - ret = strtoul(start_time, NULL, 10); - } - GC_FREE; - return ret; -} - static int fd_set_flags(libxl_ctx *ctx, int fd, int fcntlgetop, int fcntlsetop, const char *fl, int flagmask, int set_p) @@ -2212,275 +759,6 @@ void libxl_mac_copy(libxl_ctx *ctx, libxl_mac *dst, const libxl_mac *src) (*dst)[i] = (*src)[i]; } -/* For QEMU upstream we always need to provide the number of cpus present to - * QEMU whether they are online or not; otherwise QEMU won't accept the saved - * state. See implementation of libxl__qmp_query_cpus. - */ -static int libxl__update_avail_vcpus_qmp(libxl__gc *gc, uint32_t domid, - unsigned int max_vcpus, - libxl_bitmap *map) -{ - int rc; - - rc = libxl__qmp_query_cpus(gc, domid, map); - if (rc) { - LOGD(ERROR, domid, "Fail to get number of cpus"); - goto out; - } - - rc = 0; -out: - return rc; -} - -static int libxl__update_avail_vcpus_xenstore(libxl__gc *gc, uint32_t domid, - unsigned int max_vcpus, - libxl_bitmap *map) -{ - int rc; - unsigned int i; - const char *dompath; - - dompath = libxl__xs_get_dompath(gc, domid); - if (!dompath) { - rc = ERROR_FAIL; - goto out; - } - - for (i = 0; i < max_vcpus; i++) { - const char *path = GCSPRINTF("%s/cpu/%u/availability", dompath, i); - const char *content; - rc = libxl__xs_read_checked(gc, XBT_NULL, path, &content); - if (rc) goto out; - if (content && !strcmp(content, "online")) - libxl_bitmap_set(map, i); - } - - rc = 0; -out: - return rc; -} - -int libxl_retrieve_domain_configuration(libxl_ctx *ctx, uint32_t domid, - libxl_domain_config *d_config) -{ - GC_INIT(ctx); - int rc; - libxl__domain_userdata_lock *lock = NULL; - - CTX_LOCK; - - lock = libxl__lock_domain_userdata(gc, domid); - if (!lock) { - rc = ERROR_LOCK_FAIL; - goto out; - } - - rc = libxl__get_domain_configuration(gc, domid, d_config); - if (rc) { - LOGD(ERROR, domid, "Fail to get domain configuration"); - rc = ERROR_FAIL; - goto out; - } - - /* Domain name */ - { - char *domname; - domname = libxl_domid_to_name(ctx, domid); - if (!domname) { - LOGD(ERROR, domid, "Fail to get domain name"); - goto out; - } - free(d_config->c_info.name); - d_config->c_info.name = domname; /* steals allocation */ - } - - /* Domain UUID */ - { - libxl_dominfo info; - libxl_dominfo_init(&info); - rc = libxl_domain_info(ctx, &info, domid); - if (rc) { - LOGD(ERROR, domid, "Fail to get domain info"); - libxl_dominfo_dispose(&info); - goto out; - } - libxl_uuid_copy(ctx, &d_config->c_info.uuid, &info.uuid); - libxl_dominfo_dispose(&info); - } - - /* VCPUs */ - { - libxl_bitmap *map = &d_config->b_info.avail_vcpus; - unsigned int max_vcpus = d_config->b_info.max_vcpus; - libxl_device_model_version version; - - libxl_bitmap_dispose(map); - libxl_bitmap_init(map); - libxl_bitmap_alloc(CTX, map, max_vcpus); - libxl_bitmap_set_none(map); - - switch (d_config->b_info.type) { - case LIBXL_DOMAIN_TYPE_HVM: - version = libxl__device_model_version_running(gc, domid); - assert(version != LIBXL_DEVICE_MODEL_VERSION_UNKNOWN); - switch (version) { - case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN: - rc = libxl__update_avail_vcpus_qmp(gc, domid, - max_vcpus, map); - break; - case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN_TRADITIONAL: - case LIBXL_DEVICE_MODEL_VERSION_NONE: - rc = libxl__update_avail_vcpus_xenstore(gc, domid, - max_vcpus, map); - break; - default: - abort(); - } - break; - case LIBXL_DOMAIN_TYPE_PV: - rc = libxl__update_avail_vcpus_xenstore(gc, domid, - max_vcpus, map); - break; - default: - abort(); - } - - if (rc) { - LOGD(ERROR, domid, "Fail to update available cpu map"); - goto out; - } - } - - /* Memory limits: - * - * Currently there are three memory limits: - * 1. "target" in xenstore (originally memory= in config file) - * 2. "static-max" in xenstore (originally maxmem= in config file) - * 3. "max_memkb" in hypervisor - * - * The third one is not visible and currently managed by - * toolstack. In order to rebuild a domain we only need to have - * "target" and "static-max". - */ - { - uint64_t target_memkb = 0, max_memkb = 0; - - /* "target" */ - rc = libxl__get_memory_target(gc, domid, &target_memkb, &max_memkb); - if (rc) { - LOGD(ERROR, domid, "Fail to get memory target"); - goto out; - } - - /* libxl__get_targetmem_fudge() calculates the difference from - * what is in xenstore to what we have in the domain build info. - */ - d_config->b_info.target_memkb = target_memkb + - libxl__get_targetmem_fudge(gc, &d_config->b_info); - - d_config->b_info.max_memkb = max_memkb; - } - - /* Scheduler params */ - { - libxl_domain_sched_params_dispose(&d_config->b_info.sched_params); - rc = libxl_domain_sched_params_get(ctx, domid, - &d_config->b_info.sched_params); - if (rc) { - LOGD(ERROR, domid, "Fail to get scheduler parameters"); - goto out; - } - } - - /* Devices: disk, nic, vtpm, pcidev etc. */ - - /* The MERGE macro implements following logic: - * 0. retrieve JSON (done by now) - * 1. retrieve list of device from xenstore - * 2. use xenstore entries as primary reference and compare JSON - * entries with them. - * a. if a device is present in xenstore and in JSON, merge the - * two views. - * b. if a device is not present in xenstore but in JSON, delete - * it from the result. - * c. it's impossible to have an entry present in xenstore but - * not in JSON, because we maintain an invariant that every - * entry in xenstore must have a corresponding entry in JSON. - * 3. "merge" operates on "src" and "dst". "src" points to the - * entry retrieved from xenstore while "dst" points to the entry - * retrieve from JSON. - */ - { - const struct libxl_device_type *dt; - int idx; - - for (idx = 0;; idx++) { - void *p = NULL; - void **devs; - int i, j, num; - int *num_dev; - - dt = device_type_tbl[idx]; - if (!dt) - break; - - if (!dt->list || !dt->compare) - continue; - - num_dev = libxl__device_type_get_num(dt, d_config); - p = dt->list(CTX, domid, &num); - if (p == NULL) { - LOGD(DEBUG, domid, "No %s from xenstore", - dt->type); - } - devs = libxl__device_type_get_ptr(dt, d_config); - - for (i = 0; i < *num_dev; i++) { - void *q; - - q = libxl__device_type_get_elem(dt, d_config, i); - for (j = 0; j < num; j++) { - if (dt->compare(p + dt->dev_elem_size * j, q)) - break; - } - - if (j < num) { /* found in xenstore */ - if (dt->merge) - dt->merge(ctx, p + dt->dev_elem_size * j, q); - } else { /* not found in xenstore */ - LOGD(WARN, domid, - "Device present in JSON but not in xenstore, ignored"); - - dt->dispose(q); - - for (j = i; j < *num_dev - 1; j++) - memcpy(libxl__device_type_get_elem(dt, d_config, j), - libxl__device_type_get_elem(dt, d_config, j+1), - dt->dev_elem_size); - - /* rewind counters */ - (*num_dev)--; - i--; - - *devs = libxl__realloc(NOGC, *devs, - dt->dev_elem_size * *num_dev); - } - } - - for (i = 0; i < num; i++) - dt->dispose(p + dt->dev_elem_size * i); - free(p); - } - } - -out: - if (lock) libxl__unlock_domain_userdata(lock); - CTX_UNLOCK; - GC_FREE; - return rc; -} - /* * Local variables: * mode: C diff --git a/tools/libxl/libxl_domain.c b/tools/libxl/libxl_domain.c new file mode 100644 index 0000000..08eccd0 --- /dev/null +++ b/tools/libxl/libxl_domain.c @@ -0,0 +1,1744 @@ +/* + * Copyright 2009-2017 Citrix Ltd and other contributors + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation; version 2.1 only. with the special + * exception on linking described in file LICENSE. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + */ + +#include "libxl_osdeps.h" + +#include "libxl_internal.h" + +#define PAGE_TO_MEMKB(pages) ((pages) * 4) + +int libxl__domain_rename(libxl__gc *gc, uint32_t domid, + const char *old_name, const char *new_name, + xs_transaction_t trans) +{ + libxl_ctx *ctx = libxl__gc_owner(gc); + char *dom_path = 0; + const char *name_path; + char *got_old_name; + unsigned int got_old_len; + xs_transaction_t our_trans = 0; + uint32_t stub_dm_domid; + const char *stub_dm_old_name = NULL, *stub_dm_new_name = NULL; + int rc; + libxl_dominfo info; + char *uuid; + const char *vm_name_path; + + libxl_dominfo_init(&info); + + dom_path = libxl__xs_get_dompath(gc, domid); + if (!dom_path) goto x_nomem; + + name_path= GCSPRINTF("%s/name", dom_path); + if (!name_path) goto x_nomem; + + stub_dm_domid = libxl_get_stubdom_id(CTX, domid); + if (stub_dm_domid) { + stub_dm_old_name = libxl__stub_dm_name(gc, old_name); + stub_dm_new_name = libxl__stub_dm_name(gc, new_name); + } + + retry_transaction: + if (!trans) { + trans = our_trans = xs_transaction_start(ctx->xsh); + if (!our_trans) { + LOGEVD(ERROR, errno, domid, "Create xs transaction for domain (re)name"); + goto x_fail; + } + } + + if (!new_name) { + LOGD(ERROR, domid, "New domain name not specified"); + rc = ERROR_INVAL; + goto x_rc; + } + + if (new_name[0]) { + /* nonempty names must be unique */ + uint32_t domid_e; + rc = libxl_name_to_domid(ctx, new_name, &domid_e); + if (rc == ERROR_INVAL) { + /* no such domain, good */ + } else if (rc != 0) { + LOGD(ERROR, domid, "Unexpected error checking for existing domain"); + goto x_rc; + } else if (domid_e == domid) { + /* domain already has this name, ok (but we do still + * need the rest of the code as we may need to check + * old_name, for example). */ + } else { + LOGD(ERROR, domid, "Domain with name \"%s\" already exists.", new_name); + rc = ERROR_INVAL; + goto x_rc; + } + } + + if (old_name) { + got_old_name = xs_read(ctx->xsh, trans, name_path, &got_old_len); + if (!got_old_name) { + LOGEVD(ERROR, errno, domid, + "Check old name for domain allegedly named `%s'", + old_name); + goto x_fail; + } + if (strcmp(old_name, got_old_name)) { + LOGD(ERROR, domid, + "Allegedly named `%s' is actually named `%s' - racing ?", + old_name, + got_old_name); + free(got_old_name); + goto x_fail; + } + free(got_old_name); + } + if (!xs_write(ctx->xsh, trans, name_path, + new_name, strlen(new_name))) { + LOGD(ERROR, domid, + "Failed to write new name `%s'" + " for domain previously named `%s'", + new_name, + old_name); + goto x_fail; + } + + /* update /vm/<uuid>/name */ + rc = libxl_domain_info(ctx, &info, domid); + if (rc) + goto x_rc; + + uuid = GCSPRINTF(LIBXL_UUID_FMT, LIBXL_UUID_BYTES(info.uuid)); + vm_name_path = GCSPRINTF("/vm/%s/name", uuid); + if (libxl__xs_write_checked(gc, trans, vm_name_path, new_name)) + goto x_fail; + + if (stub_dm_domid) { + rc = libxl__domain_rename(gc, stub_dm_domid, + stub_dm_old_name, + stub_dm_new_name, + trans); + if (rc) { + LOGED(ERROR, domid, "Unable to rename stub-domain"); + goto x_rc; + } + } + + if (our_trans) { + if (!xs_transaction_end(ctx->xsh, our_trans, 0)) { + trans = our_trans = 0; + if (errno != EAGAIN) { + LOGD(ERROR, domid, + "Failed to commit new name `%s'" + " for domain previously named `%s'", + new_name, + old_name); + goto x_fail; + } + LOGD(DEBUG, domid, + "Need to retry rename transaction" + " for domain (name_path=\"%s\", new_name=\"%s\")", + name_path, + new_name); + goto retry_transaction; + } + our_trans = 0; + } + + rc = 0; + x_rc: + if (our_trans) xs_transaction_end(ctx->xsh, our_trans, 1); + libxl_dominfo_dispose(&info); + return rc; + + x_fail: rc = ERROR_FAIL; goto x_rc; + x_nomem: rc = ERROR_NOMEM; goto x_rc; +} + +int libxl_domain_rename(libxl_ctx *ctx, uint32_t domid, + const char *old_name, const char *new_name) +{ + GC_INIT(ctx); + int rc; + rc = libxl__domain_rename(gc, domid, old_name, new_name, XBT_NULL); + GC_FREE; + return rc; +} + +int libxl_domain_resume(libxl_ctx *ctx, uint32_t domid, int suspend_cancel, + const libxl_asyncop_how *ao_how) +{ + AO_CREATE(ctx, domid, ao_how); + int rc = libxl__domain_resume(gc, domid, suspend_cancel); + libxl__ao_complete(egc, ao, rc); + return AO_INPROGRESS; +} + +/* + * Preserves a domain but rewrites xenstore etc to make it unique so + * that the domain can be restarted. + * + * Does not modify info so that it may be reused. + */ +int libxl_domain_preserve(libxl_ctx *ctx, uint32_t domid, + libxl_domain_create_info *info, const char *name_suffix, libxl_uuid new_uuid) +{ + GC_INIT(ctx); + struct xs_permissions roperm[2]; + xs_transaction_t t; + char *preserved_name; + char *uuid_string; + char *vm_path; + char *dom_path; + + int rc; + + preserved_name = GCSPRINTF("%s%s", info->name, name_suffix); + if (!preserved_name) { + GC_FREE; + return ERROR_NOMEM; + } + + uuid_string = libxl__uuid2string(gc, new_uuid); + if (!uuid_string) { + GC_FREE; + return ERROR_NOMEM; + } + + dom_path = libxl__xs_get_dompath(gc, domid); + if (!dom_path) { + GC_FREE; + return ERROR_FAIL; + } + + vm_path = GCSPRINTF("/vm/%s", uuid_string); + if (!vm_path) { + GC_FREE; + return ERROR_FAIL; + } + + roperm[0].id = 0; + roperm[0].perms = XS_PERM_NONE; + roperm[1].id = domid; + roperm[1].perms = XS_PERM_READ; + + retry_transaction: + t = xs_transaction_start(ctx->xsh); + + xs_rm(ctx->xsh, t, vm_path); + xs_mkdir(ctx->xsh, t, vm_path); + xs_set_permissions(ctx->xsh, t, vm_path, roperm, ARRAY_SIZE(roperm)); + + xs_write(ctx->xsh, t, GCSPRINTF("%s/vm", dom_path), vm_path, strlen(vm_path)); + rc = libxl__domain_rename(gc, domid, info->name, preserved_name, t); + if (rc) { + GC_FREE; + return rc; + } + + xs_write(ctx->xsh, t, GCSPRINTF("%s/uuid", vm_path), uuid_string, strlen(uuid_string)); + + if (!xs_transaction_end(ctx->xsh, t, 0)) + if (errno == EAGAIN) + goto retry_transaction; + + GC_FREE; + return 0; +} + +void libxl__xcinfo2xlinfo(libxl_ctx *ctx, + const xc_domaininfo_t *xcinfo, + libxl_dominfo *xlinfo) +{ + size_t size; + + memcpy(&(xlinfo->uuid), xcinfo->handle, sizeof(xen_domain_handle_t)); + xlinfo->domid = xcinfo->domain; + xlinfo->ssidref = xcinfo->ssidref; + if (libxl_flask_sid_to_context(ctx, xlinfo->ssidref, + &xlinfo->ssid_label, &size) < 0) + xlinfo->ssid_label = NULL; + + xlinfo->dying = !!(xcinfo->flags&XEN_DOMINF_dying); + xlinfo->shutdown = !!(xcinfo->flags&XEN_DOMINF_shutdown); + xlinfo->paused = !!(xcinfo->flags&XEN_DOMINF_paused); + xlinfo->blocked = !!(xcinfo->flags&XEN_DOMINF_blocked); + xlinfo->running = !!(xcinfo->flags&XEN_DOMINF_running); + xlinfo->never_stop = !!(xcinfo->flags&XEN_DOMINF_xs_domain); + + if (xlinfo->shutdown) + xlinfo->shutdown_reason = (xcinfo->flags>>XEN_DOMINF_shutdownshift) & XEN_DOMINF_shutdownmask; + else + xlinfo->shutdown_reason = LIBXL_SHUTDOWN_REASON_UNKNOWN; + + xlinfo->outstanding_memkb = PAGE_TO_MEMKB(xcinfo->outstanding_pages); + xlinfo->current_memkb = PAGE_TO_MEMKB(xcinfo->tot_pages); + xlinfo->shared_memkb = PAGE_TO_MEMKB(xcinfo->shr_pages); + xlinfo->paged_memkb = PAGE_TO_MEMKB(xcinfo->paged_pages); + xlinfo->max_memkb = PAGE_TO_MEMKB(xcinfo->max_pages); + xlinfo->cpu_time = xcinfo->cpu_time; + xlinfo->vcpu_max_id = xcinfo->max_vcpu_id; + xlinfo->vcpu_online = xcinfo->nr_online_vcpus; + xlinfo->cpupool = xcinfo->cpupool; + xlinfo->domain_type = (xcinfo->flags & XEN_DOMINF_hvm_guest) ? + LIBXL_DOMAIN_TYPE_HVM : LIBXL_DOMAIN_TYPE_PV; +} + +libxl_dominfo * libxl_list_domain(libxl_ctx *ctx, int *nb_domain_out) +{ + libxl_dominfo *ptr = NULL; + int i, ret; + xc_domaininfo_t info[1024]; + int size = 0; + uint32_t domid = 0; + GC_INIT(ctx); + + while ((ret = xc_domain_getinfolist(ctx->xch, domid, 1024, info)) > 0) { + ptr = libxl__realloc(NOGC, ptr, (size + ret) * sizeof(libxl_dominfo)); + for (i = 0; i < ret; i++) { + libxl__xcinfo2xlinfo(ctx, &info[i], &ptr[size + i]); + } + domid = info[ret - 1].domain + 1; + size += ret; + } + + if (ret < 0) { + LOGE(ERROR, "getting domain info list"); + free(ptr); + GC_FREE; + return NULL; + } + + *nb_domain_out = size; + GC_FREE; + return ptr; +} + +int libxl_domain_info(libxl_ctx *ctx, libxl_dominfo *info_r, + uint32_t domid) { + xc_domaininfo_t xcinfo; + int ret; + GC_INIT(ctx); + + ret = xc_domain_getinfolist(ctx->xch, domid, 1, &xcinfo); + if (ret<0) { + LOGED(ERROR, domid, "Getting domain info list"); + GC_FREE; + return ERROR_FAIL; + } + if (ret==0 || xcinfo.domain != domid) { + GC_FREE; + return ERROR_DOMAIN_NOTFOUND; + } + + if (info_r) + libxl__xcinfo2xlinfo(ctx, &xcinfo, info_r); + GC_FREE; + return 0; +} + +/* this API call only list VM running on this host. A VM can + * be an aggregate of multiple domains. */ +libxl_vminfo * libxl_list_vm(libxl_ctx *ctx, int *nb_vm_out) +{ + GC_INIT(ctx); + libxl_dominfo *info; + libxl_vminfo *ptr = NULL; + int idx, i, n_doms; + + info = libxl_list_domain(ctx, &n_doms); + if (!info) + goto out; + + /* + * Always make sure to allocate at least one element; if we don't and we + * request zero, libxl__calloc (might) think its internal call to calloc + * has failed (if it returns null), if so it would kill our process. + */ + ptr = libxl__calloc(NOGC, n_doms ? n_doms : 1, sizeof(libxl_vminfo)); + + for (idx = i = 0; i < n_doms; i++) { + if (libxl_is_stubdom(ctx, info[i].domid, NULL)) + continue; + ptr[idx].uuid = info[i].uuid; + ptr[idx].domid = info[i].domid; + + idx++; + } + *nb_vm_out = idx; + libxl_dominfo_list_free(info, n_doms); + +out: + GC_FREE; + return ptr; +} + +static void remus_failover_cb(libxl__egc *egc, + libxl__domain_save_state *dss, int rc); + +int 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) +{ + AO_CREATE(ctx, domid, ao_how); + libxl__domain_save_state *dss; + int rc; + + libxl_domain_type type = libxl__domain_type(gc, domid); + if (type == LIBXL_DOMAIN_TYPE_INVALID) { + rc = ERROR_FAIL; + goto out; + } + + /* The caller must set this defbool */ + if (libxl_defbool_is_default(info->colo)) { + LOGD(ERROR, domid, "Colo mode must be enabled/disabled"); + rc = ERROR_FAIL; + goto out; + } + + libxl_defbool_setdefault(&info->allow_unsafe, false); + libxl_defbool_setdefault(&info->blackhole, false); + libxl_defbool_setdefault(&info->compression, + !libxl_defbool_val(info->colo)); + libxl_defbool_setdefault(&info->netbuf, true); + libxl_defbool_setdefault(&info->diskbuf, true); + + if (libxl_defbool_val(info->colo) && + libxl_defbool_val(info->compression)) { + LOGD(ERROR, domid, "Cannot use memory checkpoint " + "compression in COLO mode"); + rc = ERROR_FAIL; + goto out; + } + + if (!libxl_defbool_val(info->allow_unsafe) && + (libxl_defbool_val(info->blackhole) || + !libxl_defbool_val(info->netbuf) || + !libxl_defbool_val(info->diskbuf))) { + LOGD(ERROR, domid, "Unsafe mode must be enabled to replicate to /dev/null," + "disable network buffering and disk replication"); + rc = ERROR_FAIL; + goto out; + } + + + GCNEW(dss); + dss->ao = ao; + dss->callback = remus_failover_cb; + dss->domid = domid; + dss->fd = send_fd; + dss->recv_fd = recv_fd; + dss->type = type; + dss->live = 1; + dss->debug = 0; + dss->remus = info; + if (libxl_defbool_val(info->colo)) + dss->checkpointed_stream = LIBXL_CHECKPOINTED_STREAM_COLO; + else + dss->checkpointed_stream = LIBXL_CHECKPOINTED_STREAM_REMUS; + + assert(info); + + /* Point of no return */ + if (libxl_defbool_val(info->colo)) + libxl__colo_save_setup(egc, &dss->css); + else + libxl__remus_setup(egc, &dss->rs); + return AO_INPROGRESS; + + out: + return AO_CREATE_FAIL(rc); +} + +static void remus_failover_cb(libxl__egc *egc, + libxl__domain_save_state *dss, int rc) +{ + STATE_AO_GC(dss->ao); + /* + * With Remus, if we reach this point, it means either + * backup died or some network error occurred preventing us + * from sending checkpoints. + */ + libxl__ao_complete(egc, ao, rc); +} + +static void domain_suspend_cb(libxl__egc *egc, + libxl__domain_save_state *dss, int rc) +{ + STATE_AO_GC(dss->ao); + int flrc; + + flrc = libxl__fd_flags_restore(gc, dss->fd, dss->fdfl); + /* If suspend has failed already then report that error not this one. */ + if (flrc && !rc) rc = flrc; + + libxl__ao_complete(egc,ao,rc); + +} + +int libxl_domain_suspend(libxl_ctx *ctx, uint32_t domid, int fd, int flags, + const libxl_asyncop_how *ao_how) +{ + AO_CREATE(ctx, domid, ao_how); + int rc; + + libxl_domain_type type = libxl__domain_type(gc, domid); + if (type == LIBXL_DOMAIN_TYPE_INVALID) { + rc = ERROR_FAIL; + goto out_err; + } + + libxl__domain_save_state *dss; + GCNEW(dss); + + dss->ao = ao; + dss->callback = domain_suspend_cb; + + dss->domid = domid; + dss->fd = fd; + dss->type = type; + dss->live = flags & LIBXL_SUSPEND_LIVE; + dss->debug = flags & LIBXL_SUSPEND_DEBUG; + dss->checkpointed_stream = LIBXL_CHECKPOINTED_STREAM_NONE; + + rc = libxl__fd_flags_modify_save(gc, dss->fd, + ~(O_NONBLOCK|O_NDELAY), 0, + &dss->fdfl); + if (rc < 0) goto out_err; + + libxl__domain_save(egc, dss); + return AO_INPROGRESS; + + out_err: + return AO_CREATE_FAIL(rc); +} + +int libxl_domain_pause(libxl_ctx *ctx, uint32_t domid) +{ + int ret; + GC_INIT(ctx); + ret = xc_domain_pause(ctx->xch, domid); + if (ret<0) { + LOGED(ERROR, domid, "Pausing domain"); + GC_FREE; + return ERROR_FAIL; + } + GC_FREE; + return 0; +} + +int libxl_domain_core_dump(libxl_ctx *ctx, uint32_t domid, + const char *filename, + const libxl_asyncop_how *ao_how) +{ + AO_CREATE(ctx, domid, ao_how); + int ret, rc; + + ret = xc_domain_dumpcore(ctx->xch, domid, filename); + if (ret<0) { + LOGED(ERROR, domid, "Core dumping domain to %s", filename); + rc = ERROR_FAIL; + goto out; + } + + rc = 0; +out: + + libxl__ao_complete(egc, ao, rc); + + return AO_INPROGRESS; +} + +int libxl_domain_unpause(libxl_ctx *ctx, uint32_t domid) +{ + GC_INIT(ctx); + int ret, rc = 0; + + libxl_domain_type type = libxl__domain_type(gc, domid); + if (type == LIBXL_DOMAIN_TYPE_INVALID) { + rc = ERROR_FAIL; + goto out; + } + + if (type == LIBXL_DOMAIN_TYPE_HVM) { + if (libxl__device_model_version_running(gc, domid) != + LIBXL_DEVICE_MODEL_VERSION_NONE) { + rc = libxl__domain_resume_device_model(gc, domid); + if (rc < 0) { + LOGD(ERROR, domid, "Failed to unpause device model for domain:%d", + rc); + goto out; + } + } + } + ret = xc_domain_unpause(ctx->xch, domid); + if (ret<0) { + LOGED(ERROR, domid, "Unpausing domain"); + rc = ERROR_FAIL; + } + out: + GC_FREE; + return rc; +} + +int libxl__domain_pvcontrol_available(libxl__gc *gc, uint32_t domid) +{ + libxl_ctx *ctx = libxl__gc_owner(gc); + + uint64_t pvdriver = 0; + int ret; + + libxl_domain_type domtype = libxl__domain_type(gc, domid); + if (domtype == LIBXL_DOMAIN_TYPE_INVALID) + return ERROR_FAIL; + + if (domtype == LIBXL_DOMAIN_TYPE_PV) + return 1; + + ret = xc_hvm_param_get(ctx->xch, domid, HVM_PARAM_CALLBACK_IRQ, &pvdriver); + if (ret<0) { + LOGED(ERROR, domid, "Getting HVM callback IRQ"); + return ERROR_FAIL; + } + return !!pvdriver; +} + +const char *libxl__domain_pvcontrol_xspath(libxl__gc *gc, uint32_t domid) +{ + const char *dom_path; + + dom_path = libxl__xs_get_dompath(gc, domid); + if (!dom_path) + return NULL; + + return GCSPRINTF("%s/control/shutdown", dom_path); +} + +char * libxl__domain_pvcontrol_read(libxl__gc *gc, xs_transaction_t t, + uint32_t domid) +{ + const char *shutdown_path; + + shutdown_path = libxl__domain_pvcontrol_xspath(gc, domid); + if (!shutdown_path) + return NULL; + + return libxl__xs_read(gc, t, shutdown_path); +} + +int libxl__domain_pvcontrol_write(libxl__gc *gc, xs_transaction_t t, + uint32_t domid, const char *cmd) +{ + const char *shutdown_path; + + shutdown_path = libxl__domain_pvcontrol_xspath(gc, domid); + if (!shutdown_path) + return ERROR_FAIL; + + return libxl__xs_printf(gc, t, shutdown_path, "%s", cmd); +} + +static int libxl__domain_pvcontrol(libxl__gc *gc, uint32_t domid, + const char *cmd) +{ + int ret; + + ret = libxl__domain_pvcontrol_available(gc, domid); + if (ret < 0) + return ret; + + if (!ret) + return ERROR_NOPARAVIRT; + + return libxl__domain_pvcontrol_write(gc, XBT_NULL, domid, cmd); +} + +int libxl_domain_shutdown(libxl_ctx *ctx, uint32_t domid) +{ + GC_INIT(ctx); + int ret; + ret = libxl__domain_pvcontrol(gc, domid, "poweroff"); + GC_FREE; + return ret; +} + +int libxl_domain_reboot(libxl_ctx *ctx, uint32_t domid) +{ + GC_INIT(ctx); + int ret; + ret = libxl__domain_pvcontrol(gc, domid, "reboot"); + GC_FREE; + return ret; +} + +static void domain_death_occurred(libxl__egc *egc, + libxl_evgen_domain_death **evg_upd, + const char *why) { + /* Removes **evg_upd from death_list and puts it on death_reported + * and advances *evg_upd to the next entry. + * Call sites in domain_death_xswatch_callback must use "continue". */ + EGC_GC; + libxl_evgen_domain_death *const evg = *evg_upd; + + LOGD(DEBUG, evg->domid, "%s", why); + + libxl_evgen_domain_death *evg_next = LIBXL_TAILQ_NEXT(evg, entry); + *evg_upd = evg_next; + + libxl_event *ev = NEW_EVENT(egc, DOMAIN_DEATH, evg->domid, evg->user); + + libxl__event_occurred(egc, ev); + + evg->death_reported = 1; + LIBXL_TAILQ_REMOVE(&CTX->death_list, evg, entry); + LIBXL_TAILQ_INSERT_HEAD(&CTX->death_reported, evg, entry); +} + +static void domain_death_xswatch_callback(libxl__egc *egc, libxl__ev_xswatch *w, + const char *wpath, const char *epath) { + EGC_GC; + libxl_evgen_domain_death *evg; + int rc; + + CTX_LOCK; + + evg = LIBXL_TAILQ_FIRST(&CTX->death_list); + + for (;;) { + if (!evg) goto out; + + int nentries = LIBXL_TAILQ_NEXT(evg, entry) ? 200 : 1; + xc_domaininfo_t domaininfos[nentries]; + const xc_domaininfo_t *got = domaininfos, *gotend; + + rc = xc_domain_getinfolist(CTX->xch, evg->domid, nentries, domaininfos); + if (rc == -1) { + LIBXL__EVENT_DISASTER(egc, "xc_domain_getinfolist failed while" + " processing @releaseDomain watch event", + errno, 0); + goto out; + } + gotend = &domaininfos[rc]; + + LOGD(DEBUG, evg->domid, "[evg=%p] nentries=%d rc=%d %ld..%ld", + evg, nentries, rc, + rc>0 ? (long)domaininfos[0].domain : 0, + rc>0 ? (long)domaininfos[rc-1].domain : 0); + + for (;;) { + if (!evg) { + LOG(DEBUG, "[evg=0] all reported"); + goto all_reported; + } + + LOGD(DEBUG, evg->domid, "[evg=%p]" + " got=domaininfos[%d] got->domain=%ld", + evg, (int)(got - domaininfos), + got < gotend ? (long)got->domain : -1L); + + if (!rc) { + domain_death_occurred(egc, &evg, "empty list"); + continue; + } + + if (got == gotend) { + LOG(DEBUG, " got==gotend"); + break; + } + + if (got->domain > evg->domid) { + /* ie, the list doesn't contain evg->domid any more so + * the domain has been destroyed */ + domain_death_occurred(egc, &evg, "missing from list"); + continue; + } + + if (got->domain < evg->domid) { + got++; + continue; + } + + assert(evg->domid == got->domain); + LOGD(DEBUG, evg->domid, "Exists shutdown_reported=%d"" dominf.flags=%x", + evg->shutdown_reported, got->flags); + + if (got->flags & XEN_DOMINF_dying) { + domain_death_occurred(egc, &evg, "dying"); + continue; + } + + if (!evg->shutdown_reported && + (got->flags & XEN_DOMINF_shutdown)) { + libxl_event *ev = NEW_EVENT(egc, DOMAIN_SHUTDOWN, + got->domain, evg->user); + + LOG(DEBUG, " shutdown reporting"); + + ev->u.domain_shutdown.shutdown_reason = + (got->flags >> XEN_DOMINF_shutdownshift) & + XEN_DOMINF_shutdownmask; + libxl__event_occurred(egc, ev); + + evg->shutdown_reported = 1; + } + evg = LIBXL_TAILQ_NEXT(evg, entry); + } + + assert(rc); /* rc==0 results in us eating all evgs and quitting */ + } + all_reported: + out: + + LOG(DEBUG, "domain death search done"); + + CTX_UNLOCK; +} + +int libxl_evenable_domain_death(libxl_ctx *ctx, uint32_t domid, + libxl_ev_user user, libxl_evgen_domain_death **evgen_out) { + GC_INIT(ctx); + libxl_evgen_domain_death *evg, *evg_search; + int rc; + + CTX_LOCK; + + evg = malloc(sizeof(*evg)); if (!evg) { rc = ERROR_NOMEM; goto out; } + memset(evg, 0, sizeof(*evg)); + evg->domid = domid; + evg->user = user; + + LIBXL_TAILQ_INSERT_SORTED(&ctx->death_list, entry, evg, evg_search, , + evg->domid > evg_search->domid); + + if (!libxl__ev_xswatch_isregistered(&ctx->death_watch)) { + rc = libxl__ev_xswatch_register(gc, &ctx->death_watch, + domain_death_xswatch_callback, "@releaseDomain"); + if (rc) { libxl__evdisable_domain_death(gc, evg); goto out; } + } + + *evgen_out = evg; + rc = 0; + + out: + CTX_UNLOCK; + GC_FREE; + return rc; +}; + +void libxl__evdisable_domain_death(libxl__gc *gc, + libxl_evgen_domain_death *evg) { + CTX_LOCK; + + if (!evg->death_reported) + LIBXL_TAILQ_REMOVE(&CTX->death_list, evg, entry); + else + LIBXL_TAILQ_REMOVE(&CTX->death_reported, evg, entry); + + free(evg); + + if (!LIBXL_TAILQ_FIRST(&CTX->death_list) && + libxl__ev_xswatch_isregistered(&CTX->death_watch)) + libxl__ev_xswatch_deregister(gc, &CTX->death_watch); + + CTX_UNLOCK; +} + +void libxl_evdisable_domain_death(libxl_ctx *ctx, + libxl_evgen_domain_death *evg) { + GC_INIT(ctx); + libxl__evdisable_domain_death(gc, evg); + GC_FREE; +} + +/* Callbacks for libxl_domain_destroy */ + +static void domain_destroy_cb(libxl__egc *egc, libxl__domain_destroy_state *dds, + int rc); + +int libxl_domain_destroy(libxl_ctx *ctx, uint32_t domid, + const libxl_asyncop_how *ao_how) +{ + AO_CREATE(ctx, domid, ao_how); + libxl__domain_destroy_state *dds; + + GCNEW(dds); + dds->ao = ao; + dds->domid = domid; + dds->callback = domain_destroy_cb; + libxl__domain_destroy(egc, dds); + + return AO_INPROGRESS; +} + +static void domain_destroy_cb(libxl__egc *egc, libxl__domain_destroy_state *dds, + int rc) +{ + STATE_AO_GC(dds->ao); + + if (rc) + LOGD(ERROR, dds->domid, "Destruction of domain failed"); + + libxl__ao_complete(egc, ao, rc); +} + +/* Callbacks for libxl__domain_destroy */ + +static void stubdom_destroy_callback(libxl__egc *egc, + libxl__destroy_domid_state *dis, + int rc); + +static void domain_destroy_callback(libxl__egc *egc, + libxl__destroy_domid_state *dis, + int rc); + +static void destroy_finish_check(libxl__egc *egc, + libxl__domain_destroy_state *dds); + +void libxl__domain_destroy(libxl__egc *egc, libxl__domain_destroy_state *dds) +{ + STATE_AO_GC(dds->ao); + uint32_t stubdomid = libxl_get_stubdom_id(CTX, dds->domid); + + if (stubdomid) { + dds->stubdom.ao = ao; + dds->stubdom.domid = stubdomid; + dds->stubdom.callback = stubdom_destroy_callback; + dds->stubdom.soft_reset = false; + libxl__destroy_domid(egc, &dds->stubdom); + } else { + dds->stubdom_finished = 1; + } + + dds->domain.ao = ao; + dds->domain.domid = dds->domid; + dds->domain.callback = domain_destroy_callback; + dds->domain.soft_reset = dds->soft_reset; + libxl__destroy_domid(egc, &dds->domain); +} + +static void stubdom_destroy_callback(libxl__egc *egc, + libxl__destroy_domid_state *dis, + int rc) +{ + STATE_AO_GC(dis->ao); + libxl__domain_destroy_state *dds = CONTAINER_OF(dis, *dds, stubdom); + const char *savefile; + + if (rc) { + LOGD(ERROR, dds->domain.domid, "Unable to destroy stubdom with domid %u", + dis->domid); + dds->rc = rc; + } + + dds->stubdom_finished = 1; + savefile = libxl__device_model_savefile(gc, dis->domid); + rc = libxl__remove_file(gc, savefile); + if (rc) { + LOGD(ERROR, dds->domain.domid, "Failed to remove device-model savefile %s", + savefile); + } + + destroy_finish_check(egc, dds); +} + +static void domain_destroy_callback(libxl__egc *egc, + libxl__destroy_domid_state *dis, + int rc) +{ + STATE_AO_GC(dis->ao); + libxl__domain_destroy_state *dds = CONTAINER_OF(dis, *dds, domain); + + if (rc) { + LOGD(ERROR, dis->domid, "Unable to destroy guest"); + dds->rc = rc; + } + + dds->domain_finished = 1; + destroy_finish_check(egc, dds); +} + +static void destroy_finish_check(libxl__egc *egc, + libxl__domain_destroy_state *dds) +{ + if (!(dds->domain_finished && dds->stubdom_finished)) + return; + + dds->callback(egc, dds, dds->rc); +} + +/* Callbacks for libxl__destroy_domid */ +static void devices_destroy_cb(libxl__egc *egc, + libxl__devices_remove_state *drs, + int rc); + +static void domain_destroy_domid_cb(libxl__egc *egc, + libxl__ev_child *destroyer, + pid_t pid, int status); + +void libxl__destroy_domid(libxl__egc *egc, libxl__destroy_domid_state *dis) +{ + STATE_AO_GC(dis->ao); + libxl_ctx *ctx = CTX; + uint32_t domid = dis->domid; + char *dom_path; + int rc, dm_present; + + libxl__ev_child_init(&dis->destroyer); + + rc = libxl_domain_info(ctx, NULL, domid); + switch(rc) { + case 0: + break; + case ERROR_DOMAIN_NOTFOUND: + LOGD(ERROR, domid, "Non-existant domain"); + default: + goto out; + } + + switch (libxl__domain_type(gc, domid)) { + case LIBXL_DOMAIN_TYPE_HVM: + if (libxl_get_stubdom_id(CTX, domid)) { + dm_present = 0; + break; + } + /* fall through */ + case LIBXL_DOMAIN_TYPE_PV: + dm_present = libxl__dm_active(gc, domid); + break; + case LIBXL_DOMAIN_TYPE_INVALID: + rc = ERROR_FAIL; + goto out; + default: + abort(); + } + + dom_path = libxl__xs_get_dompath(gc, domid); + if (!dom_path) { + rc = ERROR_FAIL; + goto out; + } + + if (libxl__device_pci_destroy_all(gc, domid) < 0) + LOGD(ERROR, domid, "Pci shutdown failed"); + rc = xc_domain_pause(ctx->xch, domid); + if (rc < 0) { + LOGEVD(ERROR, rc, domid, "xc_domain_pause failed"); + } + if (dm_present) { + if (libxl__destroy_device_model(gc, domid) < 0) + LOGD(ERROR, domid, "libxl__destroy_device_model failed"); + + libxl__qmp_cleanup(gc, domid); + } + dis->drs.ao = ao; + dis->drs.domid = domid; + dis->drs.callback = devices_destroy_cb; + dis->drs.force = 1; + libxl__devices_destroy(egc, &dis->drs); + return; + +out: + assert(rc); + dis->callback(egc, dis, rc); + return; +} + +static void devices_destroy_cb(libxl__egc *egc, + libxl__devices_remove_state *drs, + int rc) +{ + STATE_AO_GC(drs->ao); + libxl__destroy_domid_state *dis = CONTAINER_OF(drs, *dis, drs); + libxl_ctx *ctx = CTX; + uint32_t domid = dis->domid; + char *dom_path; + char *vm_path; + libxl__domain_userdata_lock *lock; + + dom_path = libxl__xs_get_dompath(gc, domid); + if (!dom_path) { + rc = ERROR_FAIL; + goto out; + } + + if (rc < 0) + LOGD(ERROR, domid, "libxl__devices_destroy failed"); + + vm_path = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/vm", dom_path)); + if (vm_path) + if (!xs_rm(ctx->xsh, XBT_NULL, vm_path)) + LOGED(ERROR, domid, "xs_rm failed for %s", vm_path); + + if (!xs_rm(ctx->xsh, XBT_NULL, dom_path)) + LOGED(ERROR, domid, "xs_rm failed for %s", dom_path); + + xs_rm(ctx->xsh, XBT_NULL, libxl__xs_libxl_path(gc, domid)); + xs_rm(ctx->xsh, XBT_NULL, GCSPRINTF( "/local/domain/%d/hvmloader", domid)); + + /* This is async operation, we already hold CTX lock */ + lock = libxl__lock_domain_userdata(gc, domid); + if (!lock) { _______________________________________________ Xen-changelog mailing list Xen-changelog@xxxxxxxxxxxxx https://lists.xenproject.org/xen-changelog
|
Lists.xenproject.org is hosted with RackSpace, monitoring our |