libxl.c has grown to an uncomfortable size. Carve out the disk related functions to libxl_disk.c.
Signed-off-by: Juergen Gross <jgr...@suse.com> --- tools/libxl/Makefile | 2 +- tools/libxl/libxl.c | 1234 --------------------------------------------- tools/libxl/libxl_disk.c | 1258 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 1259 insertions(+), 1235 deletions(-) create mode 100644 tools/libxl/libxl_disk.c diff --git a/tools/libxl/Makefile b/tools/libxl/Makefile index 7514cc2..baf591d 100644 --- a/tools/libxl/Makefile +++ b/tools/libxl/Makefile @@ -136,7 +136,7 @@ LIBXL_OBJS = flexarray.o libxl.o libxl_create.o libxl_dm.o libxl_pci.o \ libxl_save_callout.o _libxl_save_msgs_callout.o \ libxl_qmp.o libxl_event.o libxl_fork.o \ libxl_dom_suspend.o libxl_dom_save.o libxl_usb.o \ - libxl_vtpm.o libxl_nic.o \ + libxl_vtpm.o libxl_nic.o libxl_disk.o \ libxl_cpupool.o libxl_sched.o \ $(LIBXL_OBJS-y) LIBXL_OBJS += libxl_genid.o diff --git a/tools/libxl/libxl.c b/tools/libxl/libxl.c index 3f7aa52..043c3d6 100644 --- a/tools/libxl/libxl.c +++ b/tools/libxl/libxl.c @@ -18,7 +18,6 @@ #include "libxl_arch.h" #define PAGE_TO_MEMKB(pages) ((pages) * 4) -#define BACKEND_STRING_SIZE 5 int libxl_ctx_alloc(libxl_ctx **pctx, int version, unsigned flags, xentoollog_logger * lg) @@ -1199,140 +1198,6 @@ void libxl_evdisable_domain_death(libxl_ctx *ctx, GC_FREE; } -static void disk_eject_xswatch_callback(libxl__egc *egc, libxl__ev_xswatch *w, - const char *wpath, const char *epath) { - EGC_GC; - libxl_evgen_disk_eject *evg = (void*)w; - const char *backend; - char *value; - char backend_type[BACKEND_STRING_SIZE+1]; - int rc; - - value = libxl__xs_read(gc, XBT_NULL, wpath); - - if (!value || strcmp(value, "eject")) - return; - - if (libxl__xs_printf(gc, XBT_NULL, wpath, "")) { - LIBXL__EVENT_DISASTER(egc, "xs_write failed acknowledging eject", - errno, LIBXL_EVENT_TYPE_DISK_EJECT); - return; - } - - libxl_event *ev = NEW_EVENT(egc, DISK_EJECT, evg->domid, evg->user); - libxl_device_disk *disk = &ev->u.disk_eject.disk; - - rc = libxl__xs_read_checked(gc, XBT_NULL, evg->be_ptr_path, &backend); - if (rc) { - LIBXL__EVENT_DISASTER(egc, "xs_read failed reading be_ptr_path", - errno, LIBXL_EVENT_TYPE_DISK_EJECT); - return; - } - if (!backend) { - /* device has been removed, not simply ejected */ - return; - } - - sscanf(backend, - "/local/domain/%d/backend/%" TOSTRING(BACKEND_STRING_SIZE) - "[a-z]/%*d/%*d", - &disk->backend_domid, backend_type); - if (!strcmp(backend_type, "tap") || !strcmp(backend_type, "vbd")) { - disk->backend = LIBXL_DISK_BACKEND_TAP; - } else if (!strcmp(backend_type, "qdisk")) { - disk->backend = LIBXL_DISK_BACKEND_QDISK; - } else { - disk->backend = LIBXL_DISK_BACKEND_UNKNOWN; - } - - disk->pdev_path = strdup(""); /* xxx fixme malloc failure */ - disk->format = LIBXL_DISK_FORMAT_EMPTY; - /* this value is returned to the user: do not free right away */ - disk->vdev = libxl__strdup(NOGC, evg->vdev); - disk->removable = 1; - disk->readwrite = 0; - disk->is_cdrom = 1; - - libxl__event_occurred(egc, ev); -} - -int libxl_evenable_disk_eject(libxl_ctx *ctx, uint32_t guest_domid, - const char *vdev, libxl_ev_user user, - libxl_evgen_disk_eject **evgen_out) { - GC_INIT(ctx); - CTX_LOCK; - int rc; - char *path; - libxl_evgen_disk_eject *evg = NULL; - - evg = malloc(sizeof(*evg)); if (!evg) { rc = ERROR_NOMEM; goto out; } - memset(evg, 0, sizeof(*evg)); - evg->user = user; - evg->domid = guest_domid; - LIBXL_LIST_INSERT_HEAD(&CTX->disk_eject_evgens, evg, entry); - - uint32_t domid = libxl_get_stubdom_id(ctx, guest_domid); - - if (!domid) - domid = guest_domid; - - int devid = libxl__device_disk_dev_number(vdev, NULL, NULL); - - path = GCSPRINTF("%s/device/vbd/%d/eject", - libxl__xs_get_dompath(gc, domid), - devid); - if (!path) { rc = ERROR_NOMEM; goto out; } - - const char *libxl_path = GCSPRINTF("%s/device/vbd/%d", - libxl__xs_libxl_path(gc, domid), - devid); - evg->be_ptr_path = libxl__sprintf(NOGC, "%s/backend", libxl_path); - - const char *configured_vdev; - rc = libxl__xs_read_checked(gc, XBT_NULL, - GCSPRINTF("%s/dev", libxl_path), &configured_vdev); - if (rc) goto out; - - evg->vdev = libxl__strdup(NOGC, configured_vdev); - - rc = libxl__ev_xswatch_register(gc, &evg->watch, - disk_eject_xswatch_callback, path); - if (rc) goto out; - - *evgen_out = evg; - CTX_UNLOCK; - GC_FREE; - return 0; - - out: - if (evg) - libxl__evdisable_disk_eject(gc, evg); - CTX_UNLOCK; - GC_FREE; - return rc; -} - -void libxl__evdisable_disk_eject(libxl__gc *gc, libxl_evgen_disk_eject *evg) { - CTX_LOCK; - - LIBXL_LIST_REMOVE(evg, entry); - - if (libxl__ev_xswatch_isregistered(&evg->watch)) - libxl__ev_xswatch_deregister(gc, &evg->watch); - - free(evg->vdev); - free(evg->be_ptr_path); - free(evg); - - CTX_UNLOCK; -} - -void libxl_evdisable_disk_eject(libxl_ctx *ctx, libxl_evgen_disk_eject *evg) { - GC_INIT(ctx); - libxl__evdisable_disk_eject(gc, evg); - GC_FREE; -} - /* Callbacks for libxl_domain_destroy */ static void domain_destroy_cb(libxl__egc *egc, libxl__domain_destroy_state *dds, @@ -1950,1045 +1815,6 @@ int libxl__resolve_domid(libxl__gc *gc, const char *name, uint32_t *domid) } /******************************************************************************/ - -int libxl__device_disk_setdefault(libxl__gc *gc, libxl_device_disk *disk, - uint32_t domid) -{ - int rc; - - libxl_defbool_setdefault(&disk->discard_enable, !!disk->readwrite); - libxl_defbool_setdefault(&disk->colo_enable, false); - libxl_defbool_setdefault(&disk->colo_restore_enable, false); - - rc = libxl__resolve_domid(gc, disk->backend_domname, &disk->backend_domid); - if (rc < 0) return rc; - - /* Force Qdisk backend for CDROM devices of guests with a device model. */ - if (disk->is_cdrom != 0 && - libxl__domain_type(gc, domid) == LIBXL_DOMAIN_TYPE_HVM && - libxl__device_model_version_running(gc, domid) != - LIBXL_DEVICE_MODEL_VERSION_NONE) { - if (!(disk->backend == LIBXL_DISK_BACKEND_QDISK || - disk->backend == LIBXL_DISK_BACKEND_UNKNOWN)) { - LOGD(ERROR, domid, "Backend for CD devices on HVM guests must be Qdisk"); - return ERROR_FAIL; - } - disk->backend = LIBXL_DISK_BACKEND_QDISK; - } - - rc = libxl__device_disk_set_backend(gc, disk); - return rc; -} - -int libxl__device_from_disk(libxl__gc *gc, uint32_t domid, - const libxl_device_disk *disk, - libxl__device *device) -{ - int devid; - - devid = libxl__device_disk_dev_number(disk->vdev, NULL, NULL); - if (devid==-1) { - LOGD(ERROR, domid, "Invalid or unsupported"" virtual disk identifier %s", - disk->vdev); - return ERROR_INVAL; - } - - device->backend_domid = disk->backend_domid; - device->backend_devid = devid; - - switch (disk->backend) { - case LIBXL_DISK_BACKEND_PHY: - device->backend_kind = LIBXL__DEVICE_KIND_VBD; - break; - case LIBXL_DISK_BACKEND_TAP: - device->backend_kind = LIBXL__DEVICE_KIND_VBD; - break; - case LIBXL_DISK_BACKEND_QDISK: - device->backend_kind = LIBXL__DEVICE_KIND_QDISK; - break; - default: - LOGD(ERROR, domid, "Unrecognized disk backend type: %d", - disk->backend); - return ERROR_INVAL; - } - - device->domid = domid; - device->devid = devid; - device->kind = LIBXL__DEVICE_KIND_VBD; - - return 0; -} - -/* Specific function called directly only by local disk attach, - * all other users should instead use the regular - * libxl__device_disk_add wrapper - * - * The (optionally) passed function get_vdev will be used to - * set the vdev the disk should be attached to. When it is set the caller - * must also pass get_vdev_user, which will be passed to get_vdev. - * - * The passed get_vdev function is also in charge of printing - * the corresponding error message when appropiate. - */ -static void device_disk_add(libxl__egc *egc, uint32_t domid, - libxl_device_disk *disk, - libxl__ao_device *aodev, - char *get_vdev(libxl__gc *, void *, - xs_transaction_t), - void *get_vdev_user) -{ - STATE_AO_GC(aodev->ao); - flexarray_t *front = NULL; - flexarray_t *back = NULL; - char *dev = NULL, *script; - libxl__device *device; - int rc; - libxl_ctx *ctx = gc->owner; - xs_transaction_t t = XBT_NULL; - libxl_domain_config d_config; - libxl_device_disk disk_saved; - libxl__domain_userdata_lock *lock = NULL; - - libxl_domain_config_init(&d_config); - libxl_device_disk_init(&disk_saved); - libxl_device_disk_copy(ctx, &disk_saved, disk); - - libxl_domain_type type = libxl__domain_type(gc, domid); - if (type == LIBXL_DOMAIN_TYPE_INVALID) { - rc = ERROR_FAIL; - goto out; - } - - /* - * get_vdev != NULL -> local attach - * get_vdev == NULL -> block attach - * - * We don't care about local attach state because it's only - * intermediate state. - */ - if (!get_vdev && aodev->update_json) { - 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) goto out; - - DEVICE_ADD(disk, disks, domid, &disk_saved, COMPARE_DISK, &d_config); - - rc = libxl__dm_check_start(gc, &d_config, domid); - if (rc) goto out; - } - - for (;;) { - rc = libxl__xs_transaction_start(gc, &t); - if (rc) goto out; - - if (get_vdev) { - assert(get_vdev_user); - disk->vdev = get_vdev(gc, get_vdev_user, t); - if (disk->vdev == NULL) { - rc = ERROR_FAIL; - goto out; - } - } - - rc = libxl__device_disk_setdefault(gc, disk, domid); - if (rc) goto out; - - front = flexarray_make(gc, 16, 1); - back = flexarray_make(gc, 16, 1); - - GCNEW(device); - rc = libxl__device_from_disk(gc, domid, disk, device); - if (rc != 0) { - LOGD(ERROR, domid, "Invalid or unsupported"" virtual disk identifier %s", - disk->vdev); - goto out; - } - - rc = libxl__device_exists(gc, t, device); - if (rc < 0) goto out; - if (rc == 1) { /* already exists in xenstore */ - LOGD(ERROR, domid, "device already exists in xenstore"); - aodev->action = LIBXL__DEVICE_ACTION_ADD; /* for error message */ - rc = ERROR_DEVICE_EXISTS; - goto out; - } - - switch (disk->backend) { - case LIBXL_DISK_BACKEND_PHY: - dev = disk->pdev_path; - - do_backend_phy: - flexarray_append(back, "params"); - flexarray_append(back, dev); - - script = libxl__abs_path(gc, disk->script?: "block", - libxl__xen_script_dir_path()); - flexarray_append_pair(back, "script", script); - - assert(device->backend_kind == LIBXL__DEVICE_KIND_VBD); - break; - - case LIBXL_DISK_BACKEND_TAP: - if (dev == NULL) { - dev = libxl__blktap_devpath(gc, disk->pdev_path, - disk->format); - if (!dev) { - LOGD(ERROR, domid, "Failed to get blktap devpath for %p", - disk->pdev_path); - rc = ERROR_FAIL; - goto out; - } - } - flexarray_append(back, "tapdisk-params"); - flexarray_append(back, GCSPRINTF("%s:%s", - libxl__device_disk_string_of_format(disk->format), - disk->pdev_path)); - - /* tap backends with scripts are rejected by - * libxl__device_disk_set_backend */ - assert(!disk->script); - - /* now create a phy device to export the device to the guest */ - goto do_backend_phy; - case LIBXL_DISK_BACKEND_QDISK: - flexarray_append(back, "params"); - flexarray_append(back, GCSPRINTF("%s:%s", - libxl__device_disk_string_of_format(disk->format), disk->pdev_path)); - if (libxl_defbool_val(disk->colo_enable)) { - flexarray_append(back, "colo-host"); - flexarray_append(back, libxl__sprintf(gc, "%s", disk->colo_host)); - flexarray_append(back, "colo-port"); - flexarray_append(back, libxl__sprintf(gc, "%d", disk->colo_port)); - flexarray_append(back, "colo-export"); - flexarray_append(back, libxl__sprintf(gc, "%s", disk->colo_export)); - flexarray_append(back, "active-disk"); - flexarray_append(back, libxl__sprintf(gc, "%s", disk->active_disk)); - flexarray_append(back, "hidden-disk"); - flexarray_append(back, libxl__sprintf(gc, "%s", disk->hidden_disk)); - } - assert(device->backend_kind == LIBXL__DEVICE_KIND_QDISK); - break; - default: - LOGD(ERROR, domid, "Unrecognized disk backend type: %d", - disk->backend); - rc = ERROR_INVAL; - goto out; - } - - flexarray_append(back, "frontend-id"); - flexarray_append(back, GCSPRINTF("%d", domid)); - flexarray_append(back, "online"); - flexarray_append(back, "1"); - flexarray_append(back, "removable"); - flexarray_append(back, GCSPRINTF("%d", (disk->removable) ? 1 : 0)); - flexarray_append(back, "bootable"); - flexarray_append(back, GCSPRINTF("%d", 1)); - flexarray_append(back, "state"); - flexarray_append(back, GCSPRINTF("%d", XenbusStateInitialising)); - flexarray_append(back, "dev"); - flexarray_append(back, disk->vdev); - flexarray_append(back, "type"); - flexarray_append(back, libxl__device_disk_string_of_backend(disk->backend)); - flexarray_append(back, "mode"); - flexarray_append(back, disk->readwrite ? "w" : "r"); - flexarray_append(back, "device-type"); - flexarray_append(back, disk->is_cdrom ? "cdrom" : "disk"); - if (disk->direct_io_safe) { - flexarray_append(back, "direct-io-safe"); - flexarray_append(back, "1"); - } - flexarray_append_pair(back, "discard-enable", - libxl_defbool_val(disk->discard_enable) ? - "1" : "0"); - - flexarray_append(front, "backend-id"); - flexarray_append(front, GCSPRINTF("%d", disk->backend_domid)); - flexarray_append(front, "state"); - flexarray_append(front, GCSPRINTF("%d", XenbusStateInitialising)); - flexarray_append(front, "virtual-device"); - flexarray_append(front, GCSPRINTF("%d", device->devid)); - flexarray_append(front, "device-type"); - flexarray_append(front, disk->is_cdrom ? "cdrom" : "disk"); - - /* - * Old PV kernel disk frontends before 2.6.26 rely on tool stack to - * write disk native protocol to frontend node. Xend does this, port - * this behaviour to xl. - * - * New kernels write this node themselves. In that case it just - * overwrites an existing node which is OK. - */ - if (type == LIBXL_DOMAIN_TYPE_PV) { - const char *protocol = - xc_domain_get_native_protocol(ctx->xch, domid); - if (protocol) { - flexarray_append(front, "protocol"); - flexarray_append(front, libxl__strdup(gc, protocol)); - } - } - - if (!get_vdev && aodev->update_json) { - rc = libxl__set_domain_configuration(gc, domid, &d_config); - if (rc) goto out; - } - - libxl__device_generic_add(gc, t, device, - libxl__xs_kvs_of_flexarray(gc, back), - libxl__xs_kvs_of_flexarray(gc, front), - NULL); - - rc = libxl__xs_transaction_commit(gc, &t); - if (!rc) break; - if (rc < 0) goto out; - } - - aodev->dev = device; - aodev->action = LIBXL__DEVICE_ACTION_ADD; - libxl__wait_device_connection(egc, aodev); - - rc = 0; - -out: - libxl__xs_transaction_abort(gc, &t); - if (lock) libxl__unlock_domain_userdata(lock); - libxl_device_disk_dispose(&disk_saved); - libxl_domain_config_dispose(&d_config); - aodev->rc = rc; - if (rc) aodev->callback(egc, aodev); - return; -} - -static void libxl__device_disk_add(libxl__egc *egc, uint32_t domid, - libxl_device_disk *disk, - libxl__ao_device *aodev) -{ - device_disk_add(egc, domid, disk, aodev, NULL, NULL); -} - -static int libxl__device_disk_from_xenstore(libxl__gc *gc, - const char *libxl_path, - libxl_device_disk *disk) -{ - libxl_ctx *ctx = libxl__gc_owner(gc); - unsigned int len; - char *tmp; - int rc; - - libxl_device_disk_init(disk); - - const char *backend_path; - rc = libxl__xs_read_checked(gc, XBT_NULL, - GCSPRINTF("%s/backend", libxl_path), - &backend_path); - if (rc) goto out; - - if (!backend_path) { - LOG(ERROR, "disk %s does not exist (no backend path", libxl_path); - rc = ERROR_FAIL; - goto out; - } - - rc = libxl__backendpath_parse_domid(gc, backend_path, &disk->backend_domid); - if (rc) { - LOG(ERROR, "Unable to fetch device backend domid from %s", backend_path); - goto out; - } - - /* - * "params" may not be present; but everything else must be. - * colo releated entries(colo-host, colo-port, colo-export, - * active-disk and hidden-disk) are present only if colo is - * enabled. - */ - tmp = xs_read(ctx->xsh, XBT_NULL, - GCSPRINTF("%s/params", libxl_path), &len); - if (tmp && strchr(tmp, ':')) { - disk->pdev_path = strdup(strchr(tmp, ':') + 1); - free(tmp); - } else { - disk->pdev_path = tmp; - } - - tmp = xs_read(ctx->xsh, XBT_NULL, - GCSPRINTF("%s/colo-host", libxl_path), &len); - if (tmp) { - libxl_defbool_set(&disk->colo_enable, true); - disk->colo_host = tmp; - - tmp = xs_read(ctx->xsh, XBT_NULL, - GCSPRINTF("%s/colo-port", libxl_path), &len); - if (!tmp) { - LOG(ERROR, "Missing xenstore node %s/colo-port", libxl_path); - goto cleanup; - } - disk->colo_port = atoi(tmp); - -#define XS_READ_COLO(param, item) do { \ - tmp = xs_read(ctx->xsh, XBT_NULL, \ - GCSPRINTF("%s/"#param"", libxl_path), &len); \ - if (!tmp) { \ - LOG(ERROR, "Missing xenstore node %s/"#param"", libxl_path); \ - goto cleanup; \ - } \ - disk->item = tmp; \ -} while (0) - XS_READ_COLO(colo-export, colo_export); - XS_READ_COLO(active-disk, active_disk); - XS_READ_COLO(hidden-disk, hidden_disk); -#undef XS_READ_COLO - } else { - libxl_defbool_set(&disk->colo_enable, false); - } - - tmp = libxl__xs_read(gc, XBT_NULL, - GCSPRINTF("%s/type", libxl_path)); - if (!tmp) { - LOG(ERROR, "Missing xenstore node %s/type", libxl_path); - goto cleanup; - } - libxl_string_to_backend(ctx, tmp, &(disk->backend)); - - disk->vdev = xs_read(ctx->xsh, XBT_NULL, - GCSPRINTF("%s/dev", libxl_path), &len); - if (!disk->vdev) { - LOG(ERROR, "Missing xenstore node %s/dev", libxl_path); - goto cleanup; - } - - tmp = libxl__xs_read(gc, XBT_NULL, libxl__sprintf - (gc, "%s/removable", libxl_path)); - if (!tmp) { - LOG(ERROR, "Missing xenstore node %s/removable", libxl_path); - goto cleanup; - } - disk->removable = atoi(tmp); - - tmp = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/mode", libxl_path)); - if (!tmp) { - LOG(ERROR, "Missing xenstore node %s/mode", libxl_path); - goto cleanup; - } - if (!strcmp(tmp, "w")) - disk->readwrite = 1; - else - disk->readwrite = 0; - - tmp = libxl__xs_read(gc, XBT_NULL, - GCSPRINTF("%s/device-type", libxl_path)); - if (!tmp) { - LOG(ERROR, "Missing xenstore node %s/device-type", libxl_path); - goto cleanup; - } - disk->is_cdrom = !strcmp(tmp, "cdrom"); - - disk->format = LIBXL_DISK_FORMAT_UNKNOWN; - - return 0; -cleanup: - rc = ERROR_FAIL; - out: - libxl_device_disk_dispose(disk); - return rc; -} - -int libxl_vdev_to_device_disk(libxl_ctx *ctx, uint32_t domid, - const char *vdev, libxl_device_disk *disk) -{ - GC_INIT(ctx); - char *dom_xl_path, *libxl_path; - int devid = libxl__device_disk_dev_number(vdev, NULL, NULL); - int rc = ERROR_FAIL; - - if (devid < 0) - return ERROR_INVAL; - - libxl_device_disk_init(disk); - - dom_xl_path = libxl__xs_libxl_path(gc, domid); - if (!dom_xl_path) { - goto out; - } - libxl_path = GCSPRINTF("%s/device/vbd/%d", dom_xl_path, devid); - - rc = libxl__device_disk_from_xenstore(gc, libxl_path, disk); -out: - GC_FREE; - return rc; -} - - -static int libxl__append_disk_list(libxl__gc *gc, - uint32_t domid, - libxl_device_disk **disks, - int *ndisks) -{ - char *libxl_dir_path = NULL; - char **dir = NULL; - unsigned int n = 0; - libxl_device_disk *pdisk = NULL, *pdisk_end = NULL; - int rc=0; - int initial_disks = *ndisks; - - libxl_dir_path = GCSPRINTF("%s/device/vbd", - libxl__xs_libxl_path(gc, domid)); - dir = libxl__xs_directory(gc, XBT_NULL, libxl_dir_path, &n); - if (dir && n) { - libxl_device_disk *tmp; - tmp = realloc(*disks, sizeof (libxl_device_disk) * (*ndisks + n)); - if (tmp == NULL) - return ERROR_NOMEM; - *disks = tmp; - pdisk = *disks + initial_disks; - pdisk_end = *disks + initial_disks + n; - for (; pdisk < pdisk_end; pdisk++, dir++) { - const char *p; - p = GCSPRINTF("%s/%s", libxl_dir_path, *dir); - if ((rc=libxl__device_disk_from_xenstore(gc, p, pdisk))) - goto out; - *ndisks += 1; - } - } -out: - return rc; -} - -libxl_device_disk *libxl_device_disk_list(libxl_ctx *ctx, uint32_t domid, int *num) -{ - GC_INIT(ctx); - libxl_device_disk *disks = NULL; - int rc; - - *num = 0; - - rc = libxl__append_disk_list(gc, domid, &disks, num); - if (rc) goto out_err; - - GC_FREE; - return disks; - -out_err: - LOG(ERROR, "Unable to list disks"); - while (disks && *num) { - (*num)--; - libxl_device_disk_dispose(&disks[*num]); - } - free(disks); - return NULL; -} - -int libxl_device_disk_getinfo(libxl_ctx *ctx, uint32_t domid, - libxl_device_disk *disk, libxl_diskinfo *diskinfo) -{ - GC_INIT(ctx); - char *dompath, *fe_path, *libxl_path; - char *val; - int rc; - - diskinfo->backend = NULL; - - dompath = libxl__xs_get_dompath(gc, domid); - diskinfo->devid = libxl__device_disk_dev_number(disk->vdev, NULL, NULL); - - /* tap devices entries in xenstore are written as vbd devices. */ - fe_path = GCSPRINTF("%s/device/vbd/%d", dompath, diskinfo->devid); - libxl_path = GCSPRINTF("%s/device/vbd/%d", - libxl__xs_libxl_path(gc, domid), diskinfo->devid); - diskinfo->backend = xs_read(ctx->xsh, XBT_NULL, - GCSPRINTF("%s/backend", libxl_path), NULL); - if (!diskinfo->backend) { - GC_FREE; - return ERROR_FAIL; - } - rc = libxl__backendpath_parse_domid(gc, diskinfo->backend, - &diskinfo->backend_id); - if (rc) goto out; - - val = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/state", fe_path)); - diskinfo->state = val ? strtoul(val, NULL, 10) : -1; - val = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/event-channel", fe_path)); - diskinfo->evtch = val ? strtoul(val, NULL, 10) : -1; - val = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/ring-ref", fe_path)); - diskinfo->rref = val ? strtoul(val, NULL, 10) : -1; - diskinfo->frontend = xs_read(ctx->xsh, XBT_NULL, - GCSPRINTF("%s/frontend", libxl_path), NULL); - diskinfo->frontend_id = domid; - - GC_FREE; - return 0; - - out: - free(diskinfo->backend); - return rc; -} - -int libxl_cdrom_insert(libxl_ctx *ctx, uint32_t domid, libxl_device_disk *disk, - const libxl_asyncop_how *ao_how) -{ - AO_CREATE(ctx, domid, ao_how); - int num = 0, i; - libxl_device_disk *disks = NULL, disk_saved, disk_empty; - libxl_domain_config d_config; - int rc, dm_ver; - libxl__device device; - const char *be_path, *libxl_path; - char * tmp; - libxl__domain_userdata_lock *lock = NULL; - xs_transaction_t t = XBT_NULL; - flexarray_t *insert = NULL, *empty = NULL; - - libxl_domain_config_init(&d_config); - libxl_device_disk_init(&disk_empty); - libxl_device_disk_init(&disk_saved); - libxl_device_disk_copy(ctx, &disk_saved, disk); - - disk_empty.format = LIBXL_DISK_FORMAT_EMPTY; - disk_empty.vdev = libxl__strdup(NOGC, disk->vdev); - disk_empty.pdev_path = libxl__strdup(NOGC, ""); - disk_empty.is_cdrom = 1; - libxl__device_disk_setdefault(gc, &disk_empty, domid); - - 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) { - LOGD(ERROR, domid, "cdrom-insert requires an HVM domain"); - rc = ERROR_INVAL; - goto out; - } - - if (libxl_get_stubdom_id(ctx, domid) != 0) { - LOGD(ERROR, domid, "cdrom-insert doesn't work for stub domains"); - rc = ERROR_INVAL; - goto out; - } - - dm_ver = libxl__device_model_version_running(gc, domid); - if (dm_ver == -1) { - LOGD(ERROR, domid, "Cannot determine device model version"); - rc = ERROR_FAIL; - goto out; - } - - if (dm_ver == LIBXL_DEVICE_MODEL_VERSION_NONE) { - LOGD(ERROR, domid, "Guests without a device model cannot use cd-insert"); - rc = ERROR_FAIL; - goto out; - } - - disks = libxl_device_disk_list(ctx, domid, &num); - for (i = 0; i < num; i++) { - if (disks[i].is_cdrom && !strcmp(disk->vdev, disks[i].vdev)) - { - /* Found. Set backend type appropriately. */ - disk->backend=disks[i].backend; - break; - } - } - if (i == num) { - LOGD(ERROR, domid, "Virtual device not found"); - rc = ERROR_FAIL; - goto out; - } - - rc = libxl__device_disk_setdefault(gc, disk, domid); - if (rc) goto out; - - if (!disk->pdev_path) { - disk->pdev_path = libxl__strdup(NOGC, ""); - disk->format = LIBXL_DISK_FORMAT_EMPTY; - } - - rc = libxl__device_from_disk(gc, domid, disk, &device); - if (rc) goto out; - - be_path = libxl__device_backend_path(gc, &device); - libxl_path = libxl__device_libxl_path(gc, &device); - - insert = flexarray_make(gc, 4, 1); - - flexarray_append_pair(insert, "type", - libxl__device_disk_string_of_backend(disk->backend)); - if (disk->format != LIBXL_DISK_FORMAT_EMPTY) - flexarray_append_pair(insert, "params", - GCSPRINTF("%s:%s", - libxl__device_disk_string_of_format(disk->format), - disk->pdev_path)); - else - flexarray_append_pair(insert, "params", ""); - - empty = flexarray_make(gc, 4, 1); - flexarray_append_pair(empty, "type", - libxl__device_disk_string_of_backend(disk->backend)); - flexarray_append_pair(empty, "params", ""); - - /* Note: CTX lock is already held at this point so lock hierarchy - * is maintained. - */ - lock = libxl__lock_domain_userdata(gc, domid); - if (!lock) { - rc = ERROR_LOCK_FAIL; - goto out; - } - - /* We need to eject the original image first. This is implemented - * by inserting empty media. JSON is not updated. - */ - if (dm_ver == LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN) { - rc = libxl__qmp_insert_cdrom(gc, domid, &disk_empty); - if (rc) goto out; - } - - for (;;) { - rc = libxl__xs_transaction_start(gc, &t); - if (rc) goto out; - /* Sanity check: make sure the device exists before writing here */ - tmp = libxl__xs_read(gc, t, GCSPRINTF("%s/frontend", libxl_path)); - if (!tmp) - { - LOGD(ERROR, domid, "Internal error: %s does not exist", - GCSPRINTF("%s/frontend", libxl_path)); - rc = ERROR_FAIL; - goto out; - } - - char **kvs = libxl__xs_kvs_of_flexarray(gc, empty); - - rc = libxl__xs_writev(gc, t, be_path, kvs); - if (rc) goto out; - - rc = libxl__xs_writev(gc, t, libxl_path, kvs); - if (rc) goto out; - - rc = libxl__xs_transaction_commit(gc, &t); - if (!rc) break; - if (rc < 0) goto out; - } - - rc = libxl__get_domain_configuration(gc, domid, &d_config); - if (rc) goto out; - - DEVICE_ADD(disk, disks, domid, &disk_saved, COMPARE_DISK, &d_config); - - rc = libxl__dm_check_start(gc, &d_config, domid); - if (rc) goto out; - - if (dm_ver == LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN) { - rc = libxl__qmp_insert_cdrom(gc, domid, disk); - if (rc) goto out; - } - - for (;;) { - rc = libxl__xs_transaction_start(gc, &t); - if (rc) goto out; - /* Sanity check: make sure the device exists before writing here */ - tmp = libxl__xs_read(gc, t, GCSPRINTF("%s/frontend", libxl_path)); - if (!tmp) - { - LOGD(ERROR, domid, "Internal error: %s does not exist", - GCSPRINTF("%s/frontend", libxl_path)); - rc = ERROR_FAIL; - goto out; - } - - rc = libxl__set_domain_configuration(gc, domid, &d_config); - if (rc) goto out; - - char **kvs = libxl__xs_kvs_of_flexarray(gc, insert); - - rc = libxl__xs_writev(gc, t, be_path, kvs); - if (rc) goto out; - - rc = libxl__xs_writev(gc, t, libxl_path, kvs); - if (rc) goto out; - - rc = libxl__xs_transaction_commit(gc, &t); - if (!rc) break; - if (rc < 0) goto out; - } - - /* success, no actual async */ - libxl__ao_complete(egc, ao, 0); - - rc = 0; - -out: - libxl__xs_transaction_abort(gc, &t); - for (i = 0; i < num; i++) - libxl_device_disk_dispose(&disks[i]); - free(disks); - libxl_device_disk_dispose(&disk_empty); - libxl_device_disk_dispose(&disk_saved); - libxl_domain_config_dispose(&d_config); - - if (lock) libxl__unlock_domain_userdata(lock); - - if (rc) return AO_CREATE_FAIL(rc); - return AO_INPROGRESS; -} - -/* libxl__alloc_vdev only works on the local domain, that is the domain - * where the toolstack is running */ -static char * libxl__alloc_vdev(libxl__gc *gc, void *get_vdev_user, - xs_transaction_t t) -{ - const char *blkdev_start = (const char *) get_vdev_user; - int devid = 0, disk = 0, part = 0; - char *libxl_dom_path = libxl__xs_libxl_path(gc, LIBXL_TOOLSTACK_DOMID); - - libxl__device_disk_dev_number(blkdev_start, &disk, &part); - if (part != 0) { - LOG(ERROR, "blkdev_start is invalid"); - return NULL; - } - - do { - devid = libxl__device_disk_dev_number(GCSPRINTF("d%dp0", disk), - NULL, NULL); - if (devid < 0) - return NULL; - if (libxl__xs_read(gc, t, - GCSPRINTF("%s/device/vbd/%d/backend", - libxl_dom_path, devid)) == NULL) { - if (errno == ENOENT) - return libxl__devid_to_vdev(gc, devid); - else - return NULL; - } - disk++; - } while (1); - return NULL; -} - -/* Callbacks */ - -char *libxl__device_disk_find_local_path(libxl__gc *gc, - libxl_domid guest_domid, - const libxl_device_disk *disk, - bool qdisk_direct) -{ - char *path = NULL; - - /* No local paths for driver domains */ - if (disk->backend_domname != NULL) { - LOG(DEBUG, "Non-local backend, can't access locally.\n"); - goto out; - } - - /* - * If this is in raw format, and we're not using a script or a - * driver domain, we can access the target path directly. - */ - if (disk->format == LIBXL_DISK_FORMAT_RAW - && disk->script == NULL) { - path = libxl__strdup(gc, disk->pdev_path); - LOG(DEBUG, "Directly accessing local RAW disk %s", path); - goto out; - } - - /* - * If we're being called for a qemu path, we can pass the target - * string directly as well - */ - if (qdisk_direct && disk->backend == LIBXL_DISK_BACKEND_QDISK) { - path = libxl__strdup(gc, disk->pdev_path); - LOG(DEBUG, "Directly accessing local QDISK target %s", path); - goto out; - } - - /* - * If the format isn't raw and / or we're using a script, then see - * if the script has written a path to the "cooked" node - */ - if (disk->script && guest_domid != INVALID_DOMID) { - libxl__device device; - char *be_path, *pdpath; - int rc; - - LOGD(DEBUG, guest_domid, - "Run from a script; checking for physical-device-path (vdev %s)", - disk->vdev); - - rc = libxl__device_from_disk(gc, guest_domid, disk, &device); - if (rc < 0) - goto out; - - be_path = libxl__device_backend_path(gc, &device); - - pdpath = libxl__sprintf(gc, "%s/physical-device-path", be_path); - - LOGD(DEBUG, guest_domid, "Attempting to read node %s", pdpath); - path = libxl__xs_read(gc, XBT_NULL, pdpath); - - if (path) - LOGD(DEBUG, guest_domid, "Accessing cooked block device %s", path); - else - LOGD(DEBUG, guest_domid, "No physical-device-path, can't access locally."); - - goto out; - } - - out: - return path; -} - -static void local_device_attach_cb(libxl__egc *egc, libxl__ao_device *aodev); - -void libxl__device_disk_local_initiate_attach(libxl__egc *egc, - libxl__disk_local_state *dls) -{ - STATE_AO_GC(dls->ao); - int rc; - const libxl_device_disk *in_disk = dls->in_disk; - libxl_device_disk *disk = &dls->disk; - const char *blkdev_start = dls->blkdev_start; - - assert(in_disk->pdev_path); - - disk->vdev = NULL; - - if (dls->diskpath) - LOG(DEBUG, "Strange, dls->diskpath already set: %s", dls->diskpath); - - LOG(DEBUG, "Trying to find local path"); - - dls->diskpath = libxl__device_disk_find_local_path(gc, INVALID_DOMID, - in_disk, false); - if (dls->diskpath) { - LOG(DEBUG, "Local path found, executing callback."); - dls->callback(egc, dls, 0); - } else { - LOG(DEBUG, "Local path not found, initiating attach."); - - memcpy(disk, in_disk, sizeof(libxl_device_disk)); - disk->pdev_path = libxl__strdup(gc, in_disk->pdev_path); - if (in_disk->script != NULL) - disk->script = libxl__strdup(gc, in_disk->script); - disk->vdev = NULL; - - rc = libxl__device_disk_setdefault(gc, disk, LIBXL_TOOLSTACK_DOMID); - if (rc) goto out; - - libxl__prepare_ao_device(ao, &dls->aodev); - dls->aodev.callback = local_device_attach_cb; - device_disk_add(egc, LIBXL_TOOLSTACK_DOMID, disk, &dls->aodev, - libxl__alloc_vdev, (void *) blkdev_start); - } - - return; - - out: - assert(rc); - dls->rc = rc; - libxl__device_disk_local_initiate_detach(egc, dls); - dls->callback(egc, dls, rc); -} - -static void local_device_attach_cb(libxl__egc *egc, libxl__ao_device *aodev) -{ - STATE_AO_GC(aodev->ao); - libxl__disk_local_state *dls = CONTAINER_OF(aodev, *dls, aodev); - char *be_path = NULL; - int rc; - libxl__device device; - libxl_device_disk *disk = &dls->disk; - - rc = aodev->rc; - if (rc) { - LOGE(ERROR, "unable locally attach device: %s", disk->pdev_path); - goto out; - } - - rc = libxl__device_from_disk(gc, LIBXL_TOOLSTACK_DOMID, disk, &device); - if (rc < 0) - goto out; - be_path = libxl__device_backend_path(gc, &device); - rc = libxl__wait_for_backend(gc, be_path, GCSPRINTF("%d", XenbusStateConnected)); - if (rc < 0) - goto out; - - dls->diskpath = GCSPRINTF("/dev/%s", - libxl__devid_to_localdev(gc, device.devid)); - LOG(DEBUG, "locally attached disk %s", dls->diskpath); - - dls->callback(egc, dls, 0); - return; - - out: - assert(rc); - dls->rc = rc; - libxl__device_disk_local_initiate_detach(egc, dls); - return; -} - -/* Callbacks for local detach */ - -static void local_device_detach_cb(libxl__egc *egc, libxl__ao_device *aodev); - -void libxl__device_disk_local_initiate_detach(libxl__egc *egc, - libxl__disk_local_state *dls) -{ - STATE_AO_GC(dls->ao); - int rc = 0; - libxl_device_disk *disk = &dls->disk; - libxl__device *device; - libxl__ao_device *aodev = &dls->aodev; - libxl__prepare_ao_device(ao, aodev); - - if (!dls->diskpath) goto out; - - if (disk->vdev != NULL) { - GCNEW(device); - rc = libxl__device_from_disk(gc, LIBXL_TOOLSTACK_DOMID, - disk, device); - if (rc != 0) goto out; - - aodev->action = LIBXL__DEVICE_ACTION_REMOVE; - aodev->dev = device; - aodev->callback = local_device_detach_cb; - aodev->force = 0; - libxl__initiate_device_generic_remove(egc, aodev); - return; - } - -out: - aodev->rc = rc; - local_device_detach_cb(egc, aodev); - return; -} - -static void local_device_detach_cb(libxl__egc *egc, libxl__ao_device *aodev) -{ - STATE_AO_GC(aodev->ao); - libxl__disk_local_state *dls = CONTAINER_OF(aodev, *dls, aodev); - int rc; - - if (aodev->rc) { - LOGED(ERROR, aodev->dev->domid, "Unable to %s %s with id %u", - libxl__device_action_to_string(aodev->action), - libxl__device_kind_to_string(aodev->dev->kind), - aodev->dev->devid); - goto out; - } - -out: - /* - * If there was an error in dls->rc, it means we have been called from - * a failed execution of libxl__device_disk_local_initiate_attach, - * so return the original error. - */ - rc = dls->rc ? dls->rc : aodev->rc; - dls->callback(egc, dls, rc); - return; -} - -/******************************************************************************/ int libxl__device_console_add(libxl__gc *gc, uint32_t domid, libxl__device_console *console, libxl__domain_build_state *state, @@ -3523,10 +2349,6 @@ out: /******************************************************************************/ /* The following functions are defined: - * libxl_device_disk_add - * libxl__add_disks - * libxl_device_disk_remove - * libxl_device_disk_destroy * libxl_device_vkb_remove * libxl_device_vkb_destroy * libxl_device_vfb_remove @@ -3537,11 +2359,6 @@ out: * 1. add support for secondary consoles to xenconsoled * 2. dynamically add/remove qemu chardevs via qmp messages. */ -/* disk */ -LIBXL_DEFINE_DEVICE_ADD(disk) -LIBXL_DEFINE_DEVICES_ADD(disk) -LIBXL_DEFINE_DEVICE_REMOVE(disk) - /* vkb */ LIBXL_DEFINE_DEVICE_REMOVE(vkb) @@ -5647,57 +4464,6 @@ out: return rc; } -static int libxl_device_disk_compare(libxl_device_disk *d1, - libxl_device_disk *d2) -{ - return COMPARE_DISK(d1, d2); -} - -/* Take care of removable device. We maintain invariant in the - * insert / remove operation so that: - * 1. if xenstore is "empty" while JSON is not, the result - * is "empty" - * 2. if xenstore has a different media than JSON, use the - * one in JSON - * 3. if xenstore and JSON have the same media, well, you - * know the answer :-) - * - * Currently there is only one removable device -- CDROM. - * Look for libxl_cdrom_insert for reference. - */ -static void libxl_device_disk_merge(libxl_ctx *ctx, void *d1, void *d2) -{ - GC_INIT(ctx); - libxl_device_disk *src = d1; - libxl_device_disk *dst = d2; - - if (src->removable) { - if (!src->pdev_path || *src->pdev_path == '\0') { - /* 1, no media in drive */ - free(dst->pdev_path); - dst->pdev_path = libxl__strdup(NOGC, ""); - dst->format = LIBXL_DISK_FORMAT_EMPTY; - } else { - /* 2 and 3, use JSON, no need to touch anything */ - ; - } - } -} - -static int libxl_device_disk_dm_needed(void *e, unsigned domid) -{ - libxl_device_disk *elem = e; - - return elem->backend == LIBXL_DISK_BACKEND_QDISK && - elem->backend_domid == domid; -} - -DEFINE_DEVICE_TYPE_STRUCT(disk, - .merge = libxl_device_disk_merge, - .dm_needed = libxl_device_disk_dm_needed, - .skip_attach = 1 -); - /* * Local variables: * mode: C diff --git a/tools/libxl/libxl_disk.c b/tools/libxl/libxl_disk.c new file mode 100644 index 0000000..ac49df2 --- /dev/null +++ b/tools/libxl/libxl_disk.c @@ -0,0 +1,1258 @@ +/* + * 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 BACKEND_STRING_SIZE 5 + +static void disk_eject_xswatch_callback(libxl__egc *egc, libxl__ev_xswatch *w, + const char *wpath, const char *epath) { + EGC_GC; + libxl_evgen_disk_eject *evg = (void*)w; + const char *backend; + char *value; + char backend_type[BACKEND_STRING_SIZE+1]; + int rc; + + value = libxl__xs_read(gc, XBT_NULL, wpath); + + if (!value || strcmp(value, "eject")) + return; + + if (libxl__xs_printf(gc, XBT_NULL, wpath, "")) { + LIBXL__EVENT_DISASTER(egc, "xs_write failed acknowledging eject", + errno, LIBXL_EVENT_TYPE_DISK_EJECT); + return; + } + + libxl_event *ev = NEW_EVENT(egc, DISK_EJECT, evg->domid, evg->user); + libxl_device_disk *disk = &ev->u.disk_eject.disk; + + rc = libxl__xs_read_checked(gc, XBT_NULL, evg->be_ptr_path, &backend); + if (rc) { + LIBXL__EVENT_DISASTER(egc, "xs_read failed reading be_ptr_path", + errno, LIBXL_EVENT_TYPE_DISK_EJECT); + return; + } + if (!backend) { + /* device has been removed, not simply ejected */ + return; + } + + sscanf(backend, + "/local/domain/%d/backend/%" TOSTRING(BACKEND_STRING_SIZE) + "[a-z]/%*d/%*d", + &disk->backend_domid, backend_type); + if (!strcmp(backend_type, "tap") || !strcmp(backend_type, "vbd")) { + disk->backend = LIBXL_DISK_BACKEND_TAP; + } else if (!strcmp(backend_type, "qdisk")) { + disk->backend = LIBXL_DISK_BACKEND_QDISK; + } else { + disk->backend = LIBXL_DISK_BACKEND_UNKNOWN; + } + + disk->pdev_path = strdup(""); /* xxx fixme malloc failure */ + disk->format = LIBXL_DISK_FORMAT_EMPTY; + /* this value is returned to the user: do not free right away */ + disk->vdev = libxl__strdup(NOGC, evg->vdev); + disk->removable = 1; + disk->readwrite = 0; + disk->is_cdrom = 1; + + libxl__event_occurred(egc, ev); +} + +int libxl_evenable_disk_eject(libxl_ctx *ctx, uint32_t guest_domid, + const char *vdev, libxl_ev_user user, + libxl_evgen_disk_eject **evgen_out) { + GC_INIT(ctx); + CTX_LOCK; + int rc; + char *path; + libxl_evgen_disk_eject *evg = NULL; + + evg = malloc(sizeof(*evg)); if (!evg) { rc = ERROR_NOMEM; goto out; } + memset(evg, 0, sizeof(*evg)); + evg->user = user; + evg->domid = guest_domid; + LIBXL_LIST_INSERT_HEAD(&CTX->disk_eject_evgens, evg, entry); + + uint32_t domid = libxl_get_stubdom_id(ctx, guest_domid); + + if (!domid) + domid = guest_domid; + + int devid = libxl__device_disk_dev_number(vdev, NULL, NULL); + + path = GCSPRINTF("%s/device/vbd/%d/eject", + libxl__xs_get_dompath(gc, domid), + devid); + if (!path) { rc = ERROR_NOMEM; goto out; } + + const char *libxl_path = GCSPRINTF("%s/device/vbd/%d", + libxl__xs_libxl_path(gc, domid), + devid); + evg->be_ptr_path = libxl__sprintf(NOGC, "%s/backend", libxl_path); + + const char *configured_vdev; + rc = libxl__xs_read_checked(gc, XBT_NULL, + GCSPRINTF("%s/dev", libxl_path), &configured_vdev); + if (rc) goto out; + + evg->vdev = libxl__strdup(NOGC, configured_vdev); + + rc = libxl__ev_xswatch_register(gc, &evg->watch, + disk_eject_xswatch_callback, path); + if (rc) goto out; + + *evgen_out = evg; + CTX_UNLOCK; + GC_FREE; + return 0; + + out: + if (evg) + libxl__evdisable_disk_eject(gc, evg); + CTX_UNLOCK; + GC_FREE; + return rc; +} + +void libxl__evdisable_disk_eject(libxl__gc *gc, libxl_evgen_disk_eject *evg) { + CTX_LOCK; + + LIBXL_LIST_REMOVE(evg, entry); + + if (libxl__ev_xswatch_isregistered(&evg->watch)) + libxl__ev_xswatch_deregister(gc, &evg->watch); + + free(evg->vdev); + free(evg->be_ptr_path); + free(evg); + + CTX_UNLOCK; +} + +void libxl_evdisable_disk_eject(libxl_ctx *ctx, libxl_evgen_disk_eject *evg) { + GC_INIT(ctx); + libxl__evdisable_disk_eject(gc, evg); + GC_FREE; +} + +int libxl__device_disk_setdefault(libxl__gc *gc, libxl_device_disk *disk, + uint32_t domid) +{ + int rc; + + libxl_defbool_setdefault(&disk->discard_enable, !!disk->readwrite); + libxl_defbool_setdefault(&disk->colo_enable, false); + libxl_defbool_setdefault(&disk->colo_restore_enable, false); + + rc = libxl__resolve_domid(gc, disk->backend_domname, &disk->backend_domid); + if (rc < 0) return rc; + + /* Force Qdisk backend for CDROM devices of guests with a device model. */ + if (disk->is_cdrom != 0 && + libxl__domain_type(gc, domid) == LIBXL_DOMAIN_TYPE_HVM && + libxl__device_model_version_running(gc, domid) != + LIBXL_DEVICE_MODEL_VERSION_NONE) { + if (!(disk->backend == LIBXL_DISK_BACKEND_QDISK || + disk->backend == LIBXL_DISK_BACKEND_UNKNOWN)) { + LOGD(ERROR, domid, "Backend for CD devices on HVM guests must be Qdisk"); + return ERROR_FAIL; + } + disk->backend = LIBXL_DISK_BACKEND_QDISK; + } + + rc = libxl__device_disk_set_backend(gc, disk); + return rc; +} + +int libxl__device_from_disk(libxl__gc *gc, uint32_t domid, + const libxl_device_disk *disk, + libxl__device *device) +{ + int devid; + + devid = libxl__device_disk_dev_number(disk->vdev, NULL, NULL); + if (devid==-1) { + LOGD(ERROR, domid, "Invalid or unsupported"" virtual disk identifier %s", + disk->vdev); + return ERROR_INVAL; + } + + device->backend_domid = disk->backend_domid; + device->backend_devid = devid; + + switch (disk->backend) { + case LIBXL_DISK_BACKEND_PHY: + device->backend_kind = LIBXL__DEVICE_KIND_VBD; + break; + case LIBXL_DISK_BACKEND_TAP: + device->backend_kind = LIBXL__DEVICE_KIND_VBD; + break; + case LIBXL_DISK_BACKEND_QDISK: + device->backend_kind = LIBXL__DEVICE_KIND_QDISK; + break; + default: + LOGD(ERROR, domid, "Unrecognized disk backend type: %d", + disk->backend); + return ERROR_INVAL; + } + + device->domid = domid; + device->devid = devid; + device->kind = LIBXL__DEVICE_KIND_VBD; + + return 0; +} + +/* Specific function called directly only by local disk attach, + * all other users should instead use the regular + * libxl__device_disk_add wrapper + * + * The (optionally) passed function get_vdev will be used to + * set the vdev the disk should be attached to. When it is set the caller + * must also pass get_vdev_user, which will be passed to get_vdev. + * + * The passed get_vdev function is also in charge of printing + * the corresponding error message when appropiate. + */ +static void device_disk_add(libxl__egc *egc, uint32_t domid, + libxl_device_disk *disk, + libxl__ao_device *aodev, + char *get_vdev(libxl__gc *, void *, + xs_transaction_t), + void *get_vdev_user) +{ + STATE_AO_GC(aodev->ao); + flexarray_t *front = NULL; + flexarray_t *back = NULL; + char *dev = NULL, *script; + libxl__device *device; + int rc; + libxl_ctx *ctx = gc->owner; + xs_transaction_t t = XBT_NULL; + libxl_domain_config d_config; + libxl_device_disk disk_saved; + libxl__domain_userdata_lock *lock = NULL; + + libxl_domain_config_init(&d_config); + libxl_device_disk_init(&disk_saved); + libxl_device_disk_copy(ctx, &disk_saved, disk); + + libxl_domain_type type = libxl__domain_type(gc, domid); + if (type == LIBXL_DOMAIN_TYPE_INVALID) { + rc = ERROR_FAIL; + goto out; + } + + /* + * get_vdev != NULL -> local attach + * get_vdev == NULL -> block attach + * + * We don't care about local attach state because it's only + * intermediate state. + */ + if (!get_vdev && aodev->update_json) { + 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) goto out; + + DEVICE_ADD(disk, disks, domid, &disk_saved, COMPARE_DISK, &d_config); + + rc = libxl__dm_check_start(gc, &d_config, domid); + if (rc) goto out; + } + + for (;;) { + rc = libxl__xs_transaction_start(gc, &t); + if (rc) goto out; + + if (get_vdev) { + assert(get_vdev_user); + disk->vdev = get_vdev(gc, get_vdev_user, t); + if (disk->vdev == NULL) { + rc = ERROR_FAIL; + goto out; + } + } + + rc = libxl__device_disk_setdefault(gc, disk, domid); + if (rc) goto out; + + front = flexarray_make(gc, 16, 1); + back = flexarray_make(gc, 16, 1); + + GCNEW(device); + rc = libxl__device_from_disk(gc, domid, disk, device); + if (rc != 0) { + LOGD(ERROR, domid, "Invalid or unsupported"" virtual disk identifier %s", + disk->vdev); + goto out; + } + + rc = libxl__device_exists(gc, t, device); + if (rc < 0) goto out; + if (rc == 1) { /* already exists in xenstore */ + LOGD(ERROR, domid, "device already exists in xenstore"); + aodev->action = LIBXL__DEVICE_ACTION_ADD; /* for error message */ + rc = ERROR_DEVICE_EXISTS; + goto out; + } + + switch (disk->backend) { + case LIBXL_DISK_BACKEND_PHY: + dev = disk->pdev_path; + + do_backend_phy: + flexarray_append(back, "params"); + flexarray_append(back, dev); + + script = libxl__abs_path(gc, disk->script?: "block", + libxl__xen_script_dir_path()); + flexarray_append_pair(back, "script", script); + + assert(device->backend_kind == LIBXL__DEVICE_KIND_VBD); + break; + + case LIBXL_DISK_BACKEND_TAP: + if (dev == NULL) { + dev = libxl__blktap_devpath(gc, disk->pdev_path, + disk->format); + if (!dev) { + LOGD(ERROR, domid, "Failed to get blktap devpath for %p", + disk->pdev_path); + rc = ERROR_FAIL; + goto out; + } + } + flexarray_append(back, "tapdisk-params"); + flexarray_append(back, GCSPRINTF("%s:%s", + libxl__device_disk_string_of_format(disk->format), + disk->pdev_path)); + + /* tap backends with scripts are rejected by + * libxl__device_disk_set_backend */ + assert(!disk->script); + + /* now create a phy device to export the device to the guest */ + goto do_backend_phy; + case LIBXL_DISK_BACKEND_QDISK: + flexarray_append(back, "params"); + flexarray_append(back, GCSPRINTF("%s:%s", + libxl__device_disk_string_of_format(disk->format), disk->pdev_path)); + if (libxl_defbool_val(disk->colo_enable)) { + flexarray_append(back, "colo-host"); + flexarray_append(back, libxl__sprintf(gc, "%s", disk->colo_host)); + flexarray_append(back, "colo-port"); + flexarray_append(back, libxl__sprintf(gc, "%d", disk->colo_port)); + flexarray_append(back, "colo-export"); + flexarray_append(back, libxl__sprintf(gc, "%s", disk->colo_export)); + flexarray_append(back, "active-disk"); + flexarray_append(back, libxl__sprintf(gc, "%s", disk->active_disk)); + flexarray_append(back, "hidden-disk"); + flexarray_append(back, libxl__sprintf(gc, "%s", disk->hidden_disk)); + } + assert(device->backend_kind == LIBXL__DEVICE_KIND_QDISK); + break; + default: + LOGD(ERROR, domid, "Unrecognized disk backend type: %d", + disk->backend); + rc = ERROR_INVAL; + goto out; + } + + flexarray_append(back, "frontend-id"); + flexarray_append(back, GCSPRINTF("%d", domid)); + flexarray_append(back, "online"); + flexarray_append(back, "1"); + flexarray_append(back, "removable"); + flexarray_append(back, GCSPRINTF("%d", (disk->removable) ? 1 : 0)); + flexarray_append(back, "bootable"); + flexarray_append(back, GCSPRINTF("%d", 1)); + flexarray_append(back, "state"); + flexarray_append(back, GCSPRINTF("%d", XenbusStateInitialising)); + flexarray_append(back, "dev"); + flexarray_append(back, disk->vdev); + flexarray_append(back, "type"); + flexarray_append(back, libxl__device_disk_string_of_backend(disk->backend)); + flexarray_append(back, "mode"); + flexarray_append(back, disk->readwrite ? "w" : "r"); + flexarray_append(back, "device-type"); + flexarray_append(back, disk->is_cdrom ? "cdrom" : "disk"); + if (disk->direct_io_safe) { + flexarray_append(back, "direct-io-safe"); + flexarray_append(back, "1"); + } + flexarray_append_pair(back, "discard-enable", + libxl_defbool_val(disk->discard_enable) ? + "1" : "0"); + + flexarray_append(front, "backend-id"); + flexarray_append(front, GCSPRINTF("%d", disk->backend_domid)); + flexarray_append(front, "state"); + flexarray_append(front, GCSPRINTF("%d", XenbusStateInitialising)); + flexarray_append(front, "virtual-device"); + flexarray_append(front, GCSPRINTF("%d", device->devid)); + flexarray_append(front, "device-type"); + flexarray_append(front, disk->is_cdrom ? "cdrom" : "disk"); + + /* + * Old PV kernel disk frontends before 2.6.26 rely on tool stack to + * write disk native protocol to frontend node. Xend does this, port + * this behaviour to xl. + * + * New kernels write this node themselves. In that case it just + * overwrites an existing node which is OK. + */ + if (type == LIBXL_DOMAIN_TYPE_PV) { + const char *protocol = + xc_domain_get_native_protocol(ctx->xch, domid); + if (protocol) { + flexarray_append(front, "protocol"); + flexarray_append(front, libxl__strdup(gc, protocol)); + } + } + + if (!get_vdev && aodev->update_json) { + rc = libxl__set_domain_configuration(gc, domid, &d_config); + if (rc) goto out; + } + + libxl__device_generic_add(gc, t, device, + libxl__xs_kvs_of_flexarray(gc, back), + libxl__xs_kvs_of_flexarray(gc, front), + NULL); + + rc = libxl__xs_transaction_commit(gc, &t); + if (!rc) break; + if (rc < 0) goto out; + } + + aodev->dev = device; + aodev->action = LIBXL__DEVICE_ACTION_ADD; + libxl__wait_device_connection(egc, aodev); + + rc = 0; + +out: + libxl__xs_transaction_abort(gc, &t); + if (lock) libxl__unlock_domain_userdata(lock); + libxl_device_disk_dispose(&disk_saved); + libxl_domain_config_dispose(&d_config); + aodev->rc = rc; + if (rc) aodev->callback(egc, aodev); + return; +} + +static void libxl__device_disk_add(libxl__egc *egc, uint32_t domid, + libxl_device_disk *disk, + libxl__ao_device *aodev) +{ + device_disk_add(egc, domid, disk, aodev, NULL, NULL); +} + +static int libxl__device_disk_from_xenstore(libxl__gc *gc, + const char *libxl_path, + libxl_device_disk *disk) +{ + libxl_ctx *ctx = libxl__gc_owner(gc); + unsigned int len; + char *tmp; + int rc; + + libxl_device_disk_init(disk); + + const char *backend_path; + rc = libxl__xs_read_checked(gc, XBT_NULL, + GCSPRINTF("%s/backend", libxl_path), + &backend_path); + if (rc) goto out; + + if (!backend_path) { + LOG(ERROR, "disk %s does not exist (no backend path", libxl_path); + rc = ERROR_FAIL; + goto out; + } + + rc = libxl__backendpath_parse_domid(gc, backend_path, &disk->backend_domid); + if (rc) { + LOG(ERROR, "Unable to fetch device backend domid from %s", backend_path); + goto out; + } + + /* + * "params" may not be present; but everything else must be. + * colo releated entries(colo-host, colo-port, colo-export, + * active-disk and hidden-disk) are present only if colo is + * enabled. + */ + tmp = xs_read(ctx->xsh, XBT_NULL, + GCSPRINTF("%s/params", libxl_path), &len); + if (tmp && strchr(tmp, ':')) { + disk->pdev_path = strdup(strchr(tmp, ':') + 1); + free(tmp); + } else { + disk->pdev_path = tmp; + } + + tmp = xs_read(ctx->xsh, XBT_NULL, + GCSPRINTF("%s/colo-host", libxl_path), &len); + if (tmp) { + libxl_defbool_set(&disk->colo_enable, true); + disk->colo_host = tmp; + + tmp = xs_read(ctx->xsh, XBT_NULL, + GCSPRINTF("%s/colo-port", libxl_path), &len); + if (!tmp) { + LOG(ERROR, "Missing xenstore node %s/colo-port", libxl_path); + goto cleanup; + } + disk->colo_port = atoi(tmp); + +#define XS_READ_COLO(param, item) do { \ + tmp = xs_read(ctx->xsh, XBT_NULL, \ + GCSPRINTF("%s/"#param"", libxl_path), &len); \ + if (!tmp) { \ + LOG(ERROR, "Missing xenstore node %s/"#param"", libxl_path); \ + goto cleanup; \ + } \ + disk->item = tmp; \ +} while (0) + XS_READ_COLO(colo-export, colo_export); + XS_READ_COLO(active-disk, active_disk); + XS_READ_COLO(hidden-disk, hidden_disk); +#undef XS_READ_COLO + } else { + libxl_defbool_set(&disk->colo_enable, false); + } + + tmp = libxl__xs_read(gc, XBT_NULL, + GCSPRINTF("%s/type", libxl_path)); + if (!tmp) { + LOG(ERROR, "Missing xenstore node %s/type", libxl_path); + goto cleanup; + } + libxl_string_to_backend(ctx, tmp, &(disk->backend)); + + disk->vdev = xs_read(ctx->xsh, XBT_NULL, + GCSPRINTF("%s/dev", libxl_path), &len); + if (!disk->vdev) { + LOG(ERROR, "Missing xenstore node %s/dev", libxl_path); + goto cleanup; + } + + tmp = libxl__xs_read(gc, XBT_NULL, libxl__sprintf + (gc, "%s/removable", libxl_path)); + if (!tmp) { + LOG(ERROR, "Missing xenstore node %s/removable", libxl_path); + goto cleanup; + } + disk->removable = atoi(tmp); + + tmp = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/mode", libxl_path)); + if (!tmp) { + LOG(ERROR, "Missing xenstore node %s/mode", libxl_path); + goto cleanup; + } + if (!strcmp(tmp, "w")) + disk->readwrite = 1; + else + disk->readwrite = 0; + + tmp = libxl__xs_read(gc, XBT_NULL, + GCSPRINTF("%s/device-type", libxl_path)); + if (!tmp) { + LOG(ERROR, "Missing xenstore node %s/device-type", libxl_path); + goto cleanup; + } + disk->is_cdrom = !strcmp(tmp, "cdrom"); + + disk->format = LIBXL_DISK_FORMAT_UNKNOWN; + + return 0; +cleanup: + rc = ERROR_FAIL; + out: + libxl_device_disk_dispose(disk); + return rc; +} + +int libxl_vdev_to_device_disk(libxl_ctx *ctx, uint32_t domid, + const char *vdev, libxl_device_disk *disk) +{ + GC_INIT(ctx); + char *dom_xl_path, *libxl_path; + int devid = libxl__device_disk_dev_number(vdev, NULL, NULL); + int rc = ERROR_FAIL; + + if (devid < 0) + return ERROR_INVAL; + + libxl_device_disk_init(disk); + + dom_xl_path = libxl__xs_libxl_path(gc, domid); + if (!dom_xl_path) { + goto out; + } + libxl_path = GCSPRINTF("%s/device/vbd/%d", dom_xl_path, devid); + + rc = libxl__device_disk_from_xenstore(gc, libxl_path, disk); +out: + GC_FREE; + return rc; +} + +static int libxl__append_disk_list(libxl__gc *gc, + uint32_t domid, + libxl_device_disk **disks, + int *ndisks) +{ + char *libxl_dir_path = NULL; + char **dir = NULL; + unsigned int n = 0; + libxl_device_disk *pdisk = NULL, *pdisk_end = NULL; + int rc=0; + int initial_disks = *ndisks; + + libxl_dir_path = GCSPRINTF("%s/device/vbd", + libxl__xs_libxl_path(gc, domid)); + dir = libxl__xs_directory(gc, XBT_NULL, libxl_dir_path, &n); + if (dir && n) { + libxl_device_disk *tmp; + tmp = realloc(*disks, sizeof (libxl_device_disk) * (*ndisks + n)); + if (tmp == NULL) + return ERROR_NOMEM; + *disks = tmp; + pdisk = *disks + initial_disks; + pdisk_end = *disks + initial_disks + n; + for (; pdisk < pdisk_end; pdisk++, dir++) { + const char *p; + p = GCSPRINTF("%s/%s", libxl_dir_path, *dir); + if ((rc=libxl__device_disk_from_xenstore(gc, p, pdisk))) + goto out; + *ndisks += 1; + } + } +out: + return rc; +} + +libxl_device_disk *libxl_device_disk_list(libxl_ctx *ctx, uint32_t domid, int *num) +{ + GC_INIT(ctx); + libxl_device_disk *disks = NULL; + int rc; + + *num = 0; + + rc = libxl__append_disk_list(gc, domid, &disks, num); + if (rc) goto out_err; + + GC_FREE; + return disks; + +out_err: + LOG(ERROR, "Unable to list disks"); + while (disks && *num) { + (*num)--; + libxl_device_disk_dispose(&disks[*num]); + } + free(disks); + return NULL; +} + +int libxl_device_disk_getinfo(libxl_ctx *ctx, uint32_t domid, + libxl_device_disk *disk, libxl_diskinfo *diskinfo) +{ + GC_INIT(ctx); + char *dompath, *fe_path, *libxl_path; + char *val; + int rc; + + diskinfo->backend = NULL; + + dompath = libxl__xs_get_dompath(gc, domid); + diskinfo->devid = libxl__device_disk_dev_number(disk->vdev, NULL, NULL); + + /* tap devices entries in xenstore are written as vbd devices. */ + fe_path = GCSPRINTF("%s/device/vbd/%d", dompath, diskinfo->devid); + libxl_path = GCSPRINTF("%s/device/vbd/%d", + libxl__xs_libxl_path(gc, domid), diskinfo->devid); + diskinfo->backend = xs_read(ctx->xsh, XBT_NULL, + GCSPRINTF("%s/backend", libxl_path), NULL); + if (!diskinfo->backend) { + GC_FREE; + return ERROR_FAIL; + } + rc = libxl__backendpath_parse_domid(gc, diskinfo->backend, + &diskinfo->backend_id); + if (rc) goto out; + + val = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/state", fe_path)); + diskinfo->state = val ? strtoul(val, NULL, 10) : -1; + val = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/event-channel", fe_path)); + diskinfo->evtch = val ? strtoul(val, NULL, 10) : -1; + val = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/ring-ref", fe_path)); + diskinfo->rref = val ? strtoul(val, NULL, 10) : -1; + diskinfo->frontend = xs_read(ctx->xsh, XBT_NULL, + GCSPRINTF("%s/frontend", libxl_path), NULL); + diskinfo->frontend_id = domid; + + GC_FREE; + return 0; + + out: + free(diskinfo->backend); + return rc; +} + +int libxl_cdrom_insert(libxl_ctx *ctx, uint32_t domid, libxl_device_disk *disk, + const libxl_asyncop_how *ao_how) +{ + AO_CREATE(ctx, domid, ao_how); + int num = 0, i; + libxl_device_disk *disks = NULL, disk_saved, disk_empty; + libxl_domain_config d_config; + int rc, dm_ver; + libxl__device device; + const char *be_path, *libxl_path; + char * tmp; + libxl__domain_userdata_lock *lock = NULL; + xs_transaction_t t = XBT_NULL; + flexarray_t *insert = NULL, *empty = NULL; + + libxl_domain_config_init(&d_config); + libxl_device_disk_init(&disk_empty); + libxl_device_disk_init(&disk_saved); + libxl_device_disk_copy(ctx, &disk_saved, disk); + + disk_empty.format = LIBXL_DISK_FORMAT_EMPTY; + disk_empty.vdev = libxl__strdup(NOGC, disk->vdev); + disk_empty.pdev_path = libxl__strdup(NOGC, ""); + disk_empty.is_cdrom = 1; + libxl__device_disk_setdefault(gc, &disk_empty, domid); + + 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) { + LOGD(ERROR, domid, "cdrom-insert requires an HVM domain"); + rc = ERROR_INVAL; + goto out; + } + + if (libxl_get_stubdom_id(ctx, domid) != 0) { + LOGD(ERROR, domid, "cdrom-insert doesn't work for stub domains"); + rc = ERROR_INVAL; + goto out; + } + + dm_ver = libxl__device_model_version_running(gc, domid); + if (dm_ver == -1) { + LOGD(ERROR, domid, "Cannot determine device model version"); + rc = ERROR_FAIL; + goto out; + } + + if (dm_ver == LIBXL_DEVICE_MODEL_VERSION_NONE) { + LOGD(ERROR, domid, "Guests without a device model cannot use cd-insert"); + rc = ERROR_FAIL; + goto out; + } + + disks = libxl_device_disk_list(ctx, domid, &num); + for (i = 0; i < num; i++) { + if (disks[i].is_cdrom && !strcmp(disk->vdev, disks[i].vdev)) + { + /* Found. Set backend type appropriately. */ + disk->backend=disks[i].backend; + break; + } + } + if (i == num) { + LOGD(ERROR, domid, "Virtual device not found"); + rc = ERROR_FAIL; + goto out; + } + + rc = libxl__device_disk_setdefault(gc, disk, domid); + if (rc) goto out; + + if (!disk->pdev_path) { + disk->pdev_path = libxl__strdup(NOGC, ""); + disk->format = LIBXL_DISK_FORMAT_EMPTY; + } + + rc = libxl__device_from_disk(gc, domid, disk, &device); + if (rc) goto out; + + be_path = libxl__device_backend_path(gc, &device); + libxl_path = libxl__device_libxl_path(gc, &device); + + insert = flexarray_make(gc, 4, 1); + + flexarray_append_pair(insert, "type", + libxl__device_disk_string_of_backend(disk->backend)); + if (disk->format != LIBXL_DISK_FORMAT_EMPTY) + flexarray_append_pair(insert, "params", + GCSPRINTF("%s:%s", + libxl__device_disk_string_of_format(disk->format), + disk->pdev_path)); + else + flexarray_append_pair(insert, "params", ""); + + empty = flexarray_make(gc, 4, 1); + flexarray_append_pair(empty, "type", + libxl__device_disk_string_of_backend(disk->backend)); + flexarray_append_pair(empty, "params", ""); + + /* Note: CTX lock is already held at this point so lock hierarchy + * is maintained. + */ + lock = libxl__lock_domain_userdata(gc, domid); + if (!lock) { + rc = ERROR_LOCK_FAIL; + goto out; + } + + /* We need to eject the original image first. This is implemented + * by inserting empty media. JSON is not updated. + */ + if (dm_ver == LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN) { + rc = libxl__qmp_insert_cdrom(gc, domid, &disk_empty); + if (rc) goto out; + } + + for (;;) { + rc = libxl__xs_transaction_start(gc, &t); + if (rc) goto out; + /* Sanity check: make sure the device exists before writing here */ + tmp = libxl__xs_read(gc, t, GCSPRINTF("%s/frontend", libxl_path)); + if (!tmp) + { + LOGD(ERROR, domid, "Internal error: %s does not exist", + GCSPRINTF("%s/frontend", libxl_path)); + rc = ERROR_FAIL; + goto out; + } + + char **kvs = libxl__xs_kvs_of_flexarray(gc, empty); + + rc = libxl__xs_writev(gc, t, be_path, kvs); + if (rc) goto out; + + rc = libxl__xs_writev(gc, t, libxl_path, kvs); + if (rc) goto out; + + rc = libxl__xs_transaction_commit(gc, &t); + if (!rc) break; + if (rc < 0) goto out; + } + + rc = libxl__get_domain_configuration(gc, domid, &d_config); + if (rc) goto out; + + DEVICE_ADD(disk, disks, domid, &disk_saved, COMPARE_DISK, &d_config); + + rc = libxl__dm_check_start(gc, &d_config, domid); + if (rc) goto out; + + if (dm_ver == LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN) { + rc = libxl__qmp_insert_cdrom(gc, domid, disk); + if (rc) goto out; + } + + for (;;) { + rc = libxl__xs_transaction_start(gc, &t); + if (rc) goto out; + /* Sanity check: make sure the device exists before writing here */ + tmp = libxl__xs_read(gc, t, GCSPRINTF("%s/frontend", libxl_path)); + if (!tmp) + { + LOGD(ERROR, domid, "Internal error: %s does not exist", + GCSPRINTF("%s/frontend", libxl_path)); + rc = ERROR_FAIL; + goto out; + } + + rc = libxl__set_domain_configuration(gc, domid, &d_config); + if (rc) goto out; + + char **kvs = libxl__xs_kvs_of_flexarray(gc, insert); + + rc = libxl__xs_writev(gc, t, be_path, kvs); + if (rc) goto out; + + rc = libxl__xs_writev(gc, t, libxl_path, kvs); + if (rc) goto out; + + rc = libxl__xs_transaction_commit(gc, &t); + if (!rc) break; + if (rc < 0) goto out; + } + + /* success, no actual async */ + libxl__ao_complete(egc, ao, 0); + + rc = 0; + +out: + libxl__xs_transaction_abort(gc, &t); + for (i = 0; i < num; i++) + libxl_device_disk_dispose(&disks[i]); + free(disks); + libxl_device_disk_dispose(&disk_empty); + libxl_device_disk_dispose(&disk_saved); + libxl_domain_config_dispose(&d_config); + + if (lock) libxl__unlock_domain_userdata(lock); + + if (rc) return AO_CREATE_FAIL(rc); + return AO_INPROGRESS; +} + +/* libxl__alloc_vdev only works on the local domain, that is the domain + * where the toolstack is running */ +static char * libxl__alloc_vdev(libxl__gc *gc, void *get_vdev_user, + xs_transaction_t t) +{ + const char *blkdev_start = (const char *) get_vdev_user; + int devid = 0, disk = 0, part = 0; + char *libxl_dom_path = libxl__xs_libxl_path(gc, LIBXL_TOOLSTACK_DOMID); + + libxl__device_disk_dev_number(blkdev_start, &disk, &part); + if (part != 0) { + LOG(ERROR, "blkdev_start is invalid"); + return NULL; + } + + do { + devid = libxl__device_disk_dev_number(GCSPRINTF("d%dp0", disk), + NULL, NULL); + if (devid < 0) + return NULL; + if (libxl__xs_read(gc, t, + GCSPRINTF("%s/device/vbd/%d/backend", + libxl_dom_path, devid)) == NULL) { + if (errno == ENOENT) + return libxl__devid_to_vdev(gc, devid); + else + return NULL; + } + disk++; + } while (1); + return NULL; +} + +/* Callbacks */ + +char *libxl__device_disk_find_local_path(libxl__gc *gc, + libxl_domid guest_domid, + const libxl_device_disk *disk, + bool qdisk_direct) +{ + char *path = NULL; + + /* No local paths for driver domains */ + if (disk->backend_domname != NULL) { + LOG(DEBUG, "Non-local backend, can't access locally.\n"); + goto out; + } + + /* + * If this is in raw format, and we're not using a script or a + * driver domain, we can access the target path directly. + */ + if (disk->format == LIBXL_DISK_FORMAT_RAW + && disk->script == NULL) { + path = libxl__strdup(gc, disk->pdev_path); + LOG(DEBUG, "Directly accessing local RAW disk %s", path); + goto out; + } + + /* + * If we're being called for a qemu path, we can pass the target + * string directly as well + */ + if (qdisk_direct && disk->backend == LIBXL_DISK_BACKEND_QDISK) { + path = libxl__strdup(gc, disk->pdev_path); + LOG(DEBUG, "Directly accessing local QDISK target %s", path); + goto out; + } + + /* + * If the format isn't raw and / or we're using a script, then see + * if the script has written a path to the "cooked" node + */ + if (disk->script && guest_domid != INVALID_DOMID) { + libxl__device device; + char *be_path, *pdpath; + int rc; + + LOGD(DEBUG, guest_domid, + "Run from a script; checking for physical-device-path (vdev %s)", + disk->vdev); + + rc = libxl__device_from_disk(gc, guest_domid, disk, &device); + if (rc < 0) + goto out; + + be_path = libxl__device_backend_path(gc, &device); + + pdpath = libxl__sprintf(gc, "%s/physical-device-path", be_path); + + LOGD(DEBUG, guest_domid, "Attempting to read node %s", pdpath); + path = libxl__xs_read(gc, XBT_NULL, pdpath); + + if (path) + LOGD(DEBUG, guest_domid, "Accessing cooked block device %s", path); + else + LOGD(DEBUG, guest_domid, "No physical-device-path, can't access locally."); + + goto out; + } + + out: + return path; +} + +static void local_device_attach_cb(libxl__egc *egc, libxl__ao_device *aodev); + +void libxl__device_disk_local_initiate_attach(libxl__egc *egc, + libxl__disk_local_state *dls) +{ + STATE_AO_GC(dls->ao); + int rc; + const libxl_device_disk *in_disk = dls->in_disk; + libxl_device_disk *disk = &dls->disk; + const char *blkdev_start = dls->blkdev_start; + + assert(in_disk->pdev_path); + + disk->vdev = NULL; + + if (dls->diskpath) + LOG(DEBUG, "Strange, dls->diskpath already set: %s", dls->diskpath); + + LOG(DEBUG, "Trying to find local path"); + + dls->diskpath = libxl__device_disk_find_local_path(gc, INVALID_DOMID, + in_disk, false); + if (dls->diskpath) { + LOG(DEBUG, "Local path found, executing callback."); + dls->callback(egc, dls, 0); + } else { + LOG(DEBUG, "Local path not found, initiating attach."); + + memcpy(disk, in_disk, sizeof(libxl_device_disk)); + disk->pdev_path = libxl__strdup(gc, in_disk->pdev_path); + if (in_disk->script != NULL) + disk->script = libxl__strdup(gc, in_disk->script); + disk->vdev = NULL; + + rc = libxl__device_disk_setdefault(gc, disk, LIBXL_TOOLSTACK_DOMID); + if (rc) goto out; + + libxl__prepare_ao_device(ao, &dls->aodev); + dls->aodev.callback = local_device_attach_cb; + device_disk_add(egc, LIBXL_TOOLSTACK_DOMID, disk, &dls->aodev, + libxl__alloc_vdev, (void *) blkdev_start); + } + + return; + + out: + assert(rc); + dls->rc = rc; + libxl__device_disk_local_initiate_detach(egc, dls); + dls->callback(egc, dls, rc); +} + +static void local_device_attach_cb(libxl__egc *egc, libxl__ao_device *aodev) +{ + STATE_AO_GC(aodev->ao); + libxl__disk_local_state *dls = CONTAINER_OF(aodev, *dls, aodev); + char *be_path = NULL; + int rc; + libxl__device device; + libxl_device_disk *disk = &dls->disk; + + rc = aodev->rc; + if (rc) { + LOGE(ERROR, "unable locally attach device: %s", disk->pdev_path); + goto out; + } + + rc = libxl__device_from_disk(gc, LIBXL_TOOLSTACK_DOMID, disk, &device); + if (rc < 0) + goto out; + be_path = libxl__device_backend_path(gc, &device); + rc = libxl__wait_for_backend(gc, be_path, GCSPRINTF("%d", XenbusStateConnected)); + if (rc < 0) + goto out; + + dls->diskpath = GCSPRINTF("/dev/%s", + libxl__devid_to_localdev(gc, device.devid)); + LOG(DEBUG, "locally attached disk %s", dls->diskpath); + + dls->callback(egc, dls, 0); + return; + + out: + assert(rc); + dls->rc = rc; + libxl__device_disk_local_initiate_detach(egc, dls); + return; +} + +/* Callbacks for local detach */ + +static void local_device_detach_cb(libxl__egc *egc, libxl__ao_device *aodev); + +void libxl__device_disk_local_initiate_detach(libxl__egc *egc, + libxl__disk_local_state *dls) +{ + STATE_AO_GC(dls->ao); + int rc = 0; + libxl_device_disk *disk = &dls->disk; + libxl__device *device; + libxl__ao_device *aodev = &dls->aodev; + libxl__prepare_ao_device(ao, aodev); + + if (!dls->diskpath) goto out; + + if (disk->vdev != NULL) { + GCNEW(device); + rc = libxl__device_from_disk(gc, LIBXL_TOOLSTACK_DOMID, + disk, device); + if (rc != 0) goto out; + + aodev->action = LIBXL__DEVICE_ACTION_REMOVE; + aodev->dev = device; + aodev->callback = local_device_detach_cb; + aodev->force = 0; + libxl__initiate_device_generic_remove(egc, aodev); + return; + } + +out: + aodev->rc = rc; + local_device_detach_cb(egc, aodev); + return; +} + +static void local_device_detach_cb(libxl__egc *egc, libxl__ao_device *aodev) +{ + STATE_AO_GC(aodev->ao); + libxl__disk_local_state *dls = CONTAINER_OF(aodev, *dls, aodev); + int rc; + + if (aodev->rc) { + LOGED(ERROR, aodev->dev->domid, "Unable to %s %s with id %u", + libxl__device_action_to_string(aodev->action), + libxl__device_kind_to_string(aodev->dev->kind), + aodev->dev->devid); + goto out; + } + +out: + /* + * If there was an error in dls->rc, it means we have been called from + * a failed execution of libxl__device_disk_local_initiate_attach, + * so return the original error. + */ + rc = dls->rc ? dls->rc : aodev->rc; + dls->callback(egc, dls, rc); + return; +} + +/* The following functions are defined: + * libxl_device_disk_add + * libxl__add_disks + * libxl_device_disk_remove + * libxl_device_disk_destroy + */ +LIBXL_DEFINE_DEVICE_ADD(disk) +LIBXL_DEFINE_DEVICES_ADD(disk) +LIBXL_DEFINE_DEVICE_REMOVE(disk) + +static int libxl_device_disk_compare(libxl_device_disk *d1, + libxl_device_disk *d2) +{ + return COMPARE_DISK(d1, d2); +} + +/* Take care of removable device. We maintain invariant in the + * insert / remove operation so that: + * 1. if xenstore is "empty" while JSON is not, the result + * is "empty" + * 2. if xenstore has a different media than JSON, use the + * one in JSON + * 3. if xenstore and JSON have the same media, well, you + * know the answer :-) + * + * Currently there is only one removable device -- CDROM. + * Look for libxl_cdrom_insert for reference. + */ +static void libxl_device_disk_merge(libxl_ctx *ctx, void *d1, void *d2) +{ + GC_INIT(ctx); + libxl_device_disk *src = d1; + libxl_device_disk *dst = d2; + + if (src->removable) { + if (!src->pdev_path || *src->pdev_path == '\0') { + /* 1, no media in drive */ + free(dst->pdev_path); + dst->pdev_path = libxl__strdup(NOGC, ""); + dst->format = LIBXL_DISK_FORMAT_EMPTY; + } else { + /* 2 and 3, use JSON, no need to touch anything */ + ; + } + } +} + +static int libxl_device_disk_dm_needed(void *e, unsigned domid) +{ + libxl_device_disk *elem = e; + + return elem->backend == LIBXL_DISK_BACKEND_QDISK && + elem->backend_domid == domid; +} + +DEFINE_DEVICE_TYPE_STRUCT(disk, + .merge = libxl_device_disk_merge, + .dm_needed = libxl_device_disk_dm_needed, + .skip_attach = 1 +); + +/* + * Local variables: + * mode: C + * c-basic-offset: 4 + * indent-tabs-mode: nil + * End: + */ -- 2.10.2 _______________________________________________ Xen-devel mailing list Xen-devel@lists.xen.org https://lists.xen.org/xen-devel