[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [Xen-devel] [PATCH v5 3/5] libxl: add support for vscsi
Port pvscsi support from xend to libxl: vscsi=['pdev,vdev{,options}'] xl scsi-attach xl scsi-detach xl scsi-list Signed-off-by: Olaf Hering <olaf@xxxxxxxxx> Cc: Ian Jackson <ian.jackson@xxxxxxxxxxxxx> Cc: Stefano Stabellini <stefano.stabellini@xxxxxxxxxxxxx> Cc: Ian Campbell <ian.campbell@xxxxxxxxxx> Cc: Wei Liu <wei.liu2@xxxxxxxxxx> --- docs/man/xl.cfg.pod.5 | 55 +++ docs/man/xl.pod.1 | 18 + tools/libxl/Makefile | 2 + tools/libxl/libxl.c | 440 +++++++++++++++++++++ tools/libxl/libxl.h | 27 ++ tools/libxl/libxl_create.c | 1 + tools/libxl/libxl_device.c | 2 + tools/libxl/libxl_internal.h | 16 + tools/libxl/libxl_types.idl | 56 +++ tools/libxl/libxl_types_internal.idl | 1 + tools/libxl/libxl_vscsi.c | 274 +++++++++++++ tools/libxl/libxlu_vscsi.c | 745 +++++++++++++++++++++++++++++++++++ tools/libxl/libxlutil.h | 18 + tools/libxl/xl.h | 3 + tools/libxl/xl_cmdimpl.c | 205 +++++++++- tools/libxl/xl_cmdtable.c | 15 + 16 files changed, 1877 insertions(+), 1 deletion(-) diff --git a/docs/man/xl.cfg.pod.5 b/docs/man/xl.cfg.pod.5 index f936dfc..6a7dc8c 100644 --- a/docs/man/xl.cfg.pod.5 +++ b/docs/man/xl.cfg.pod.5 @@ -510,6 +510,61 @@ value is optional if this is a guest domain. =back +=item B<vscsi=[ "VSCSI_SPEC_STRING", "VSCSI_SPEC_STRING", ...]> + +Specifies the PVSCSI devices to be provided to the guest. PVSCSI passes +SCSI devices from the backend domain to the guest. + +Each VSCSI_SPEC_STRING consists of "pdev,vdev[,options]". +'pdev' describes the physical device, preferable in a persistent format such as /dev/disk/by-*/*. +'vdev' is the domU device in vHOST:CHANNEL:TARGET:LUN notation, all integers. +'option' lists additional flags which a backend may recognize. + +The supported values for "pdev" and "option" depends on the used backend driver: + +=over 4 + +=item B<Linux> + +=over 4 + +=item C<pdev> + +The backend driver in the pvops kernel is part of the Linux-IO Target framework +(LIO). As such the SCSI devices have to be configured first with the tools +provided by this framework, such as a xen-scsiback aware targetcli. The "pdev" +in domU.cfg has to refer to a config item in that framework instead of the raw +device. Ususally this is a WWN in the form of "na.WWN:LUN". + +=item C<option> + +No options recognized. + +=back + +=item B<Linux based on classic Xen kernel> + +=over 4 + +=item C<pdev> + +The dom0 device in either /dev/scsidev or pHOST:CHANNEL:TARGET:LUN notation. + +It's recommended to use persistent names "/dev/disk/by-*/*" to refer to a "pdev". +The toolstack will translate this internally to "h:c:t:l" notation, which is how +the backend driver will access the device. Using the "h:c:t:l" notation for +"pdev" in domU.cfg is discouraged because this value will change across reboots, +depending on the detection order in the OS. + +=item C<option> + +Currently only the option value "feature-host" is recognized. SCSI command +emulation in backend driver is bypassed when "feature-host" is specified. + +=back + +=back + =item B<vfb=[ "VFB_SPEC_STRING", "VFB_SPEC_STRING", ...]> Specifies the paravirtual framebuffer devices which should be supplied diff --git a/docs/man/xl.pod.1 b/docs/man/xl.pod.1 index 16783c8..19bdbfa 100644 --- a/docs/man/xl.pod.1 +++ b/docs/man/xl.pod.1 @@ -1328,6 +1328,24 @@ List virtual trusted platform modules for a domain. =back +=head2 PVSCSI DEVICES + +=over 4 + +=item B<scsi-attach> I<domain-id> I<pdev> I<vdev>,I<[feature-host]> + +Creates a new vscsi device in the domain specified by I<domain-id>. + +=item B<scsi-detach> I<domain-id> I<vdev> + +Removes the vscsi device from domain specified by I<domain-id>. + +=item B<scsi-list> I<domain-id> I<[domain-id] ...> + +List vscsi devices for the domain specified by I<domain-id>. + +=back + =head1 PCI PASS-THROUGH =over 4 diff --git a/tools/libxl/Makefile b/tools/libxl/Makefile index 1b16598..79b3867 100644 --- a/tools/libxl/Makefile +++ b/tools/libxl/Makefile @@ -91,6 +91,7 @@ endif LIBXL_LIBS += -lyajl LIBXL_OBJS = flexarray.o libxl.o libxl_create.o libxl_dm.o libxl_pci.o \ + libxl_vscsi.o \ libxl_dom.o libxl_exec.o libxl_xshelp.o libxl_device.o \ libxl_internal.o libxl_utils.o libxl_uuid.o \ libxl_json.o libxl_aoutils.o libxl_numa.o libxl_vnuma.o \ @@ -122,6 +123,7 @@ AUTOINCS= libxlu_cfg_y.h libxlu_cfg_l.h _libxl_list.h _paths.h \ AUTOSRCS= libxlu_cfg_y.c libxlu_cfg_l.c AUTOSRCS += _libxl_save_msgs_callout.c _libxl_save_msgs_helper.c LIBXLU_OBJS = libxlu_cfg_y.o libxlu_cfg_l.o libxlu_cfg.o \ + libxlu_vscsi.o \ libxlu_disk_l.o libxlu_disk.o libxlu_vif.o libxlu_pci.o $(LIBXLU_OBJS): CFLAGS += $(CFLAGS_libxenctrl) # For xentoollog.h diff --git a/tools/libxl/libxl.c b/tools/libxl/libxl.c index 516713e..4c17d63 100644 --- a/tools/libxl/libxl.c +++ b/tools/libxl/libxl.c @@ -2014,6 +2014,436 @@ static int libxl__resolve_domid(libxl__gc *gc, const char *name, } /******************************************************************************/ + +static void libxl__device_vscsi_dev_backend_rm(libxl__gc *gc, + libxl_vscsi_dev *v, + xs_transaction_t t, + const char *be_path, + int dev_wait) +{ + char *dir, *path, *val; + + dir = GCSPRINTF("%s/vscsi-devs/dev-%u", be_path, v->vscsi_dev_id); + path = GCSPRINTF("%s/state", dir); + val = libxl__xs_read(gc, t, path); + LOG(DEBUG, "%s is %s", path, val); + if (val && strcmp(val, GCSPRINTF("%d", dev_wait)) == 0) { + xs_rm(CTX->xsh, t, GCSPRINTF("%s/state", dir)); + xs_rm(CTX->xsh, t, GCSPRINTF("%s/p-devname", dir)); + xs_rm(CTX->xsh, t, GCSPRINTF("%s/p-dev", dir)); + xs_rm(CTX->xsh, t, GCSPRINTF("%s/v-dev", dir)); + xs_rm(CTX->xsh, t, dir); + } else { + LOG(ERROR, "%s has %s, expected %d", path, val, dev_wait); + } +} + +static int libxl__device_vscsi_dev_backend_set(libxl__gc *gc, + libxl_vscsi_dev *v, + flexarray_t *back) +{ + int rc; + char *dir; + libxl_vscsi_hctl *hctl; + + dir = GCSPRINTF("vscsi-devs/dev-%u", v->vscsi_dev_id); + switch (v->pdev.type) { + case LIBXL_VSCSI_PDEV_TYPE_WWN: + flexarray_append_pair(back, + GCSPRINTF("%s/p-dev", dir), + v->pdev.u.wwn.m); + break; + case LIBXL_VSCSI_PDEV_TYPE_HCTL: + hctl = &v->pdev.u.hctl.m; + flexarray_append_pair(back, + GCSPRINTF("%s/p-dev", dir), + GCSPRINTF("%u:%u:%u:%u", hctl->hst, hctl->chn, hctl->tgt, hctl->lun)); + break; + case LIBXL_VSCSI_PDEV_TYPE_INVALID: + default: + rc = ERROR_FAIL; + goto out; + } + flexarray_append_pair(back, + GCSPRINTF("%s/p-devname", dir), + v->pdev.p_devname); + flexarray_append_pair(back, + GCSPRINTF("%s/v-dev", dir), + GCSPRINTF("%u:%u:%u:%u", v->vdev.hst, v->vdev.chn, v->vdev.tgt, v->vdev.lun)); + flexarray_append_pair(back, + GCSPRINTF("%s/state", dir), + GCSPRINTF("%d", XenbusStateInitialising)); + rc = 0; +out: + return rc; +} + +static int libxl__device_vscsi_new_backend(libxl__egc *egc, + libxl__ao_device *aodev, + libxl_device_vscsi *vscsi, + libxl_domain_config *d_config) +{ + STATE_AO_GC(aodev->ao); + int rc, i; + flexarray_t *back; + flexarray_t *front; + libxl_vscsi_dev *v; + xs_transaction_t t = XBT_NULL; + + /* Prealloc key+value: 4 toplevel + 4 per device */ + i = 2 * (4 + (4 * vscsi->num_vscsi_devs)); + back = flexarray_make(gc, i, 1); + front = flexarray_make(gc, 2 * 2, 1); + + flexarray_append_pair(back, "frontend-id", GCSPRINTF("%d", aodev->dev->domid)); + flexarray_append_pair(back, "online", "1"); + flexarray_append_pair(back, "state", GCSPRINTF("%d", XenbusStateInitialising)); + flexarray_append_pair(back, "feature-host", + libxl_defbool_val(vscsi->scsi_raw_cmds) ? + "1" : "0"); + + flexarray_append_pair(front, "backend-id", GCSPRINTF("%d", vscsi->backend_domid)); + flexarray_append_pair(front, "state", GCSPRINTF("%d", XenbusStateInitialising)); + + for (i = 0; i < vscsi->num_vscsi_devs; i++) { + v = vscsi->vscsi_devs + i; + if (v->remove) + continue; + rc = libxl__device_vscsi_dev_backend_set(gc, v, back); + if (rc) return rc; + } + + for (;;) { + rc = libxl__xs_transaction_start(gc, &t); + if (rc) goto out; + + rc = libxl__device_exists(gc, t, aodev->dev); + if (rc < 0) goto out; + if (rc == 1) { /* already exists in xenstore */ + LOG(ERROR, "device already exists in xenstore"); + rc = ERROR_DEVICE_EXISTS; + goto out; + } + + if (aodev->update_json) { + rc = libxl__set_domain_configuration(gc, aodev->dev->domid, d_config); + if (rc) goto out; + } + + libxl__device_generic_add(gc, t, aodev->dev, + libxl__xs_kvs_of_flexarray(gc, back, + back->count), + libxl__xs_kvs_of_flexarray(gc, front, + front->count), + NULL); + + rc = libxl__xs_transaction_commit(gc, &t); + if (!rc) break; + if (rc < 0) goto out; + } + + libxl__wait_device_connection(egc, aodev); + rc = 0; + +out: + libxl__xs_transaction_abort(gc, &t); + return rc; +} + +static int libxl__device_vscsi_reconfigure(libxl__egc *egc, + libxl__ao_device *aodev, + libxl_device_vscsi *vscsi, + libxl_domain_config *d_config, + const char *be_path, + int *dev_wait) +{ + STATE_AO_GC(aodev->ao); + int rc, i, be_state, be_wait; + char *dev_path, *state_path, *state_val; + flexarray_t *back; + libxl_vscsi_dev *v; + xs_transaction_t t = XBT_NULL; + bool do_reconfigure = false; + + /* Prealloc key+value: 1 toplevel + 4 per device */ + i = 2 * (1 + (4 * vscsi->num_vscsi_devs)); + back = flexarray_make(gc, i, 1); + + state_path = GCSPRINTF("%s/state", be_path); + + for (;;) { + rc = libxl__xs_transaction_start(gc, &t); + if (rc) goto out; + + state_val = libxl__xs_read(gc, t, state_path); + LOG(DEBUG, "%s is %s", state_path, state_val); + if (!state_val) { + rc = ERROR_FAIL; + goto out; + } + + be_state = atoi(state_val); + switch (be_state) { + case XenbusStateUnknown: + case XenbusStateInitialising: + case XenbusStateClosing: + case XenbusStateClosed: + default: + /* The backend is in a bad state */ + rc = ERROR_FAIL; + goto out; + case XenbusStateInitialised: + case XenbusStateReconfiguring: + case XenbusStateReconfigured: + /* Backend is still busy, caller has to retry */ + rc = ERROR_NOT_READY; + goto out; + case XenbusStateInitWait: + /* The frontend did not connect yet */ + be_wait = XenbusStateInitWait; + if (dev_wait) + *dev_wait = XenbusStateInitialising; + do_reconfigure = false; + break; + case XenbusStateConnected: + /* The backend can handle reconfigure */ + be_wait = XenbusStateConnected; + if (dev_wait) + *dev_wait = XenbusStateClosed; + flexarray_append_pair(back, "state", GCSPRINTF("%d", XenbusStateReconfiguring)); + do_reconfigure = true; + break; + } + + /* Append new device or trigger removal */ + for (i = 0; i < vscsi->num_vscsi_devs; i++) { + unsigned int nb = 0; + v = vscsi->vscsi_devs + i; + dev_path = GCSPRINTF("%s/vscsi-devs/dev-%u", be_path, v->vscsi_dev_id); + /* Preserve existing device */ + if (libxl__xs_directory(gc, XBT_NULL, dev_path, &nb) && nb) { + /* Trigger device removal by forwarding state to XenbusStateClosing */ + if (do_reconfigure && v->remove) + flexarray_append_pair(back, + GCSPRINTF("vscsi-devs/dev-%u/state", v->vscsi_dev_id), + GCSPRINTF("%d", XenbusStateClosing)); + continue; + } + rc = libxl__device_vscsi_dev_backend_set(gc, v, back); + if (rc) goto out; + } + + if (aodev->update_json) { + rc = libxl__set_domain_configuration(gc, aodev->dev->domid, d_config); + if (rc) goto out; + } + + libxl__xs_writev(gc, t, be_path, + libxl__xs_kvs_of_flexarray(gc, back, back->count)); + + rc = libxl__xs_transaction_commit(gc, &t); + if (!rc) break; + if (rc < 0) goto out; + } + + if (do_reconfigure) { + /* Poll for backend change */ + rc = libxl__wait_for_backend(gc, be_path, GCSPRINTF("%d", be_wait)); + if (rc) goto out; + } + rc = 0; + +out: + libxl__xs_transaction_abort(gc, &t); + return rc; +} + +static int libxl__device_from_vscsi(libxl__gc *gc, uint32_t domid, + libxl_device_vscsi *vscsi, + libxl__device *device) +{ + device->backend_domid = vscsi->backend_domid; + device->devid = vscsi->devid; + device->domid = domid; + device->backend_kind = LIBXL__DEVICE_KIND_VSCSI; + device->kind = LIBXL__DEVICE_KIND_VSCSI; + + return 0; +} + +void libxl__device_vscsi_add(libxl__egc *egc, uint32_t domid, + libxl_device_vscsi *vscsi, + libxl__ao_device *aodev) +{ + STATE_AO_GC(aodev->ao); + libxl__device *device; + char *be_path; + unsigned int be_dirs = 0; + int rc; + libxl_domain_config d_config; + libxl_device_vscsi vscsi_saved; + libxl__domain_userdata_lock *lock = NULL; + + libxl_domain_config_init(&d_config); + + libxl_device_vscsi_init(&vscsi_saved); + libxl_device_vscsi_copy(CTX, &vscsi_saved, vscsi); + + if (vscsi->devid == -1) { + if ((vscsi->devid = libxl__device_nextid(gc, domid, "vscsi")) < 0) { + rc = ERROR_FAIL; + goto out; + } + } + + /* Adjust copy */ + libxl__update_config_vscsi(gc, &vscsi_saved, vscsi); + + GCNEW(device); + rc = libxl__device_from_vscsi(gc, domid, vscsi, device); + if (rc) goto out; + + if (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; + + /* Replace or append the copy to the domain config */ + DEVICE_ADD(vscsi, vscsis, domid, &vscsi_saved, COMPARE_VSCSI, &d_config); + } + + aodev->dev = device; + + be_path = libxl__device_backend_path(gc, aodev->dev); + if (libxl__xs_directory(gc, XBT_NULL, be_path, &be_dirs)) { + rc = libxl__device_vscsi_reconfigure(egc, aodev, vscsi, &d_config, be_path, NULL); + if (rc) + goto out; + /* Notify that this is done */ + aodev->callback(egc, aodev); + } else { + rc = libxl__device_vscsi_new_backend(egc, aodev, vscsi, &d_config); + if (rc) + goto out; + } + + rc = 0; +out: + if (lock) libxl__unlock_domain_userdata(lock); + libxl_device_vscsi_dispose(&vscsi_saved); + libxl_domain_config_dispose(&d_config); + aodev->rc = rc; + if (rc) aodev->callback(egc, aodev); + return; +} + +static void libxl__device_vscsi_dev_rm(libxl__egc *egc, + libxl_device_vscsi *vscsi, + libxl__ao_device *aodev) +{ + STATE_AO_GC(aodev->ao); + char *be_path; + int rc, i, dev_wait; + libxl_domain_config d_config; + libxl__domain_userdata_lock *lock = NULL; + libxl_vscsi_dev *v; + xs_transaction_t t = XBT_NULL; + + libxl_domain_config_init(&d_config); + + if (vscsi->devid == -1) { + rc = ERROR_INVAL; + goto out; + } + + /* No other code will traverse device list, update json with removal info */ + if (aodev->update_json) { + lock = libxl__lock_domain_userdata(gc, aodev->dev->domid); + if (!lock) { + rc = ERROR_LOCK_FAIL; + goto out; + } + + rc = libxl__get_domain_configuration(gc, aodev->dev->domid, &d_config); + if (rc) goto out; + + /* Replace the item in the domain config */ + DEVICE_ADD(vscsi, vscsis, aodev->dev->domid, vscsi, COMPARE_VSCSI, &d_config); + } + + be_path = libxl__device_backend_path(gc, aodev->dev); + rc = libxl__device_vscsi_reconfigure(egc, aodev, vscsi, &d_config, be_path, &dev_wait); + if (rc) goto out; + + for (;;) { + rc = libxl__xs_transaction_start(gc, &t); + if (rc) goto out; + + for (i = 0; i < vscsi->num_vscsi_devs; i++) { + v = vscsi->vscsi_devs + i; + if (v->remove) + libxl__device_vscsi_dev_backend_rm(gc, v, t, be_path, dev_wait); + } + + rc = libxl__xs_transaction_commit(gc, &t); + if (!rc) break; + if (rc < 0) goto out; + } + + rc = 0; + +out: + if (lock) libxl__unlock_domain_userdata(lock); + libxl_domain_config_dispose(&d_config); + aodev->rc = rc; + /* Notify that this is done */ + aodev->callback(egc, aodev); +} + +/* Extended variant of DEFINE_DEVICE_REMOVE to handle reconfigure */ +int libxl_device_vscsi_remove(libxl_ctx *ctx, uint32_t domid, + libxl_device_vscsi *vscsi, + const libxl_asyncop_how *ao_how) +{ + AO_CREATE(ctx, domid, ao_how); + libxl__device *device; + libxl__ao_device *aodev; + int rc, i; + unsigned int remaining = vscsi->num_vscsi_devs; + + GCNEW(device); + rc = libxl__device_from_vscsi(gc, domid, vscsi, device); + if (rc != 0) goto out; + + GCNEW(aodev); + libxl__prepare_ao_device(ao, aodev); + aodev->action = LIBXL__DEVICE_ACTION_REMOVE; + aodev->dev = device; + aodev->callback = device_addrm_aocomplete; + aodev->force = 0; + aodev->update_json = true; + + for (i = 0; i < vscsi->num_vscsi_devs; i++) { + if (vscsi->vscsi_devs[i].remove) + remaining--; + } + + LOG(DEBUG, "%u: v_hst %u, %u of %u remaining", domid, vscsi->v_hst, remaining, vscsi->num_vscsi_devs); + if (remaining) + libxl__device_vscsi_dev_rm(egc, vscsi, aodev); + else + libxl__initiate_device_remove(egc, aodev); + +out: + if (rc) return AO_ABORT(rc); + return AO_INPROGRESS; +} + int libxl__device_vtpm_setdefault(libxl__gc *gc, libxl_device_vtpm *vtpm) { int rc; @@ -4158,6 +4588,7 @@ out: * libxl_device_disk_destroy * libxl_device_nic_remove * libxl_device_nic_destroy + * libxl_device_vscsi_destroy * libxl_device_vtpm_remove * libxl_device_vtpm_destroy * libxl_device_vkb_remove @@ -4202,6 +4633,9 @@ DEFINE_DEVICE_REMOVE(disk, destroy, 1) DEFINE_DEVICE_REMOVE(nic, remove, 0) DEFINE_DEVICE_REMOVE(nic, destroy, 1) +/* vscsi */ +DEFINE_DEVICE_REMOVE(vscsi, destroy, 1) + /* vkb */ DEFINE_DEVICE_REMOVE(vkb, remove, 0) DEFINE_DEVICE_REMOVE(vkb, destroy, 1) @@ -4227,6 +4661,7 @@ DEFINE_DEVICE_REMOVE(vtpm, destroy, 1) /* The following functions are defined: * libxl_device_disk_add * libxl_device_nic_add + * libxl_device_vscsi_add * libxl_device_vtpm_add */ @@ -4256,6 +4691,9 @@ DEFINE_DEVICE_ADD(disk) /* nic */ DEFINE_DEVICE_ADD(nic) +/* vscsi */ +DEFINE_DEVICE_ADD(vscsi) + /* vtpm */ DEFINE_DEVICE_ADD(vtpm) @@ -6740,6 +7178,8 @@ int libxl_retrieve_domain_configuration(libxl_ctx *ctx, uint32_t domid, MERGE(nic, nics, COMPARE_DEVID, {}); + MERGE(vscsi, vscsis, COMPARE_VSCSI, {}); + MERGE(vtpm, vtpms, COMPARE_DEVID, {}); MERGE(pci, pcidevs, COMPARE_PCI, {}); diff --git a/tools/libxl/libxl.h b/tools/libxl/libxl.h index 44bd8e2..c3d5b74 100644 --- a/tools/libxl/libxl.h +++ b/tools/libxl/libxl.h @@ -750,6 +750,13 @@ void libxl_mac_copy(libxl_ctx *ctx, libxl_mac *dst, libxl_mac *src); #define LIBXL_HAVE_PSR_MBM 1 #endif +/* + * LIBXL_HAVE_VSCSI + * + * If this is defined, the PV SCSI feature is supported. + */ +#define LIBXL_HAVE_VSCSI 1 + typedef char **libxl_string_list; void libxl_string_list_dispose(libxl_string_list *sl); int libxl_string_list_length(const libxl_string_list *sl); @@ -1268,6 +1275,26 @@ int libxl_device_channel_getinfo(libxl_ctx *ctx, uint32_t domid, libxl_device_channel *channel, libxl_channelinfo *channelinfo); +/* Virtual SCSI */ +int libxl_device_vscsi_add(libxl_ctx *ctx, uint32_t domid, + libxl_device_vscsi *vscsi, + const libxl_asyncop_how *ao_how) + LIBXL_EXTERNAL_CALLERS_ONLY; +int libxl_device_vscsi_remove(libxl_ctx *ctx, uint32_t domid, + libxl_device_vscsi *vscsi, + const libxl_asyncop_how *ao_how) + LIBXL_EXTERNAL_CALLERS_ONLY; +int libxl_device_vscsi_destroy(libxl_ctx *ctx, uint32_t domid, + libxl_device_vscsi *vscsi, + const libxl_asyncop_how *ao_how) + LIBXL_EXTERNAL_CALLERS_ONLY; + +libxl_device_vscsi *libxl_device_vscsi_list(libxl_ctx *ctx, uint32_t domid, int *num); +int libxl_device_vscsi_getinfo(libxl_ctx *ctx, uint32_t domid, + libxl_device_vscsi *vscsi_host, + libxl_vscsi_dev *vscsi_dev, + libxl_vscsiinfo *vscsiinfo); + /* Virtual TPMs */ int libxl_device_vtpm_add(libxl_ctx *ctx, uint32_t domid, libxl_device_vtpm *vtpm, const libxl_asyncop_how *ao_how) diff --git a/tools/libxl/libxl_create.c b/tools/libxl/libxl_create.c index e5a343f..f67b4ce 100644 --- a/tools/libxl/libxl_create.c +++ b/tools/libxl/libxl_create.c @@ -1157,6 +1157,7 @@ static void domcreate_rebuild_done(libxl__egc *egc, libxl__multidev_begin(ao, &dcs->multidev); dcs->multidev.callback = domcreate_launch_dm; libxl__add_disks(egc, ao, domid, d_config, &dcs->multidev); + libxl__add_vscsis(egc, ao, domid, d_config, &dcs->multidev); libxl__multidev_prepared(egc, &dcs->multidev, 0); return; diff --git a/tools/libxl/libxl_device.c b/tools/libxl/libxl_device.c index 93bb41e..a1c63f8 100644 --- a/tools/libxl/libxl_device.c +++ b/tools/libxl/libxl_device.c @@ -543,6 +543,7 @@ void libxl__multidev_prepared(libxl__egc *egc, * The following functions are defined: * libxl__add_disks * libxl__add_nics + * libxl__add_vscsis * libxl__add_vtpms */ @@ -562,6 +563,7 @@ void libxl__multidev_prepared(libxl__egc *egc, DEFINE_DEVICES_ADD(disk) DEFINE_DEVICES_ADD(nic) +DEFINE_DEVICES_ADD(vscsi) DEFINE_DEVICES_ADD(vtpm) #undef DEFINE_DEVICES_ADD diff --git a/tools/libxl/libxl_internal.h b/tools/libxl/libxl_internal.h index 8eb38aa..01b00e1 100644 --- a/tools/libxl/libxl_internal.h +++ b/tools/libxl/libxl_internal.h @@ -2420,6 +2420,10 @@ _hidden void libxl__device_nic_add(libxl__egc *egc, uint32_t domid, libxl_device_nic *nic, libxl__ao_device *aodev); +_hidden void libxl__device_vscsi_add(libxl__egc *egc, uint32_t domid, + libxl_device_vscsi *vscsi, + libxl__ao_device *aodev); + _hidden void libxl__device_vtpm_add(libxl__egc *egc, uint32_t domid, libxl_device_vtpm *vtpm, libxl__ao_device *aodev); @@ -3045,6 +3049,10 @@ _hidden void libxl__add_nics(libxl__egc *egc, libxl__ao *ao, uint32_t domid, libxl_domain_config *d_config, libxl__multidev *multidev); +_hidden void libxl__add_vscsis(libxl__egc *egc, libxl__ao *ao, uint32_t domid, + libxl_domain_config *d_config, + libxl__multidev *multidev); + _hidden void libxl__add_vtpms(libxl__egc *egc, libxl__ao *ao, uint32_t domid, libxl_domain_config *d_config, libxl__multidev *multidev); @@ -3616,6 +3624,13 @@ static inline void libxl__update_config_nic(libxl__gc *gc, libxl_mac_copy(CTX, &dst->mac, &src->mac); } +static inline void libxl__update_config_vscsi(libxl__gc *gc, + libxl_device_vscsi *dst, + libxl_device_vscsi *src) +{ + dst->devid = src->devid; +} + static inline void libxl__update_config_vtpm(libxl__gc *gc, libxl_device_vtpm *dst, libxl_device_vtpm *src) @@ -3628,6 +3643,7 @@ static inline void libxl__update_config_vtpm(libxl__gc *gc, * devices have same identifier. */ #define COMPARE_DEVID(a, b) ((a)->devid == (b)->devid) #define COMPARE_DISK(a, b) (!strcmp((a)->vdev, (b)->vdev)) +#define COMPARE_VSCSI(a, b) ((a)->v_hst == (b)->v_hst) #define COMPARE_PCI(a, b) ((a)->func == (b)->func && \ (a)->bus == (b)->bus && \ (a)->dev == (b)->dev) diff --git a/tools/libxl/libxl_types.idl b/tools/libxl/libxl_types.idl index 117b61d..7f0dcc6 100644 --- a/tools/libxl/libxl_types.idl +++ b/tools/libxl/libxl_types.idl @@ -553,6 +553,45 @@ libxl_device_channel = Struct("device_channel", [ ])), ]) +libxl_vscsi_pdev_type = Enumeration("vscsi_pdev_type", [ + (0, "INVALID"), + (1, "HCTL"), + (2, "WWN"), + ]) + +libxl_vscsi_hctl = Struct("vscsi_hctl", [ + ("hst", uint32), + ("chn", uint32), + ("tgt", uint32), + ("lun", uint32), + ]) + +libxl_vscsi_pdev = Struct("vscsi_pdev", [ + ("p_devname", string), + ("u", KeyedUnion(None, libxl_vscsi_pdev_type, "type", + [ + ("invalid", None), + ("hctl", Struct(None, [("m", libxl_vscsi_hctl)])), + ("wwn", Struct(None, [("m", string)])), + ])), + ]) + +libxl_vscsi_dev = Struct("vscsi_dev", [ + ("vscsi_dev_id", libxl_devid), + ("remove", bool), + ("pdev", libxl_vscsi_pdev), + ("vdev", libxl_vscsi_hctl), + ]) + +libxl_device_vscsi = Struct("device_vscsi", [ + ("backend_domid", libxl_domid), + ("devid", libxl_devid), + ("v_hst", uint32), + ("vscsi_devs", Array(libxl_vscsi_dev, "num_vscsi_devs")), + ("next_vscsi_dev_id", libxl_devid), + ("scsi_raw_cmds", libxl_defbool), + ]) + libxl_domain_config = Struct("domain_config", [ ("c_info", libxl_domain_create_info), ("b_info", libxl_domain_build_info), @@ -562,6 +601,7 @@ libxl_domain_config = Struct("domain_config", [ ("pcidevs", Array(libxl_device_pci, "num_pcidevs")), ("vfbs", Array(libxl_device_vfb, "num_vfbs")), ("vkbs", Array(libxl_device_vkb, "num_vkbs")), + ("vscsis", Array(libxl_device_vscsi, "num_vscsis")), ("vtpms", Array(libxl_device_vtpm, "num_vtpms")), # a channel manifests as a console with a name, # see docs/misc/channels.txt @@ -596,6 +636,22 @@ libxl_nicinfo = Struct("nicinfo", [ ("rref_rx", integer), ], dir=DIR_OUT) +libxl_vscsiinfo = Struct("vscsiinfo", [ + ("backend", string), + ("backend_id", uint32), + ("frontend", string), + ("frontend_id", uint32), + ("devid", libxl_devid), + ("pdev", libxl_vscsi_pdev), + ("vdev", libxl_vscsi_hctl), + ("vscsi_dev_id", libxl_devid), + ("scsi_raw_cmds", bool), + ("vscsi_host_state", integer), + ("vscsi_dev_state", integer), + ("evtch", integer), + ("rref", integer), + ], dir=DIR_OUT) + libxl_vtpminfo = Struct("vtpminfo", [ ("backend", string), ("backend_id", uint32), diff --git a/tools/libxl/libxl_types_internal.idl b/tools/libxl/libxl_types_internal.idl index 5e55685..84c44f5 100644 --- a/tools/libxl/libxl_types_internal.idl +++ b/tools/libxl/libxl_types_internal.idl @@ -22,6 +22,7 @@ libxl__device_kind = Enumeration("device_kind", [ (6, "VKBD"), (7, "CONSOLE"), (8, "VTPM"), + (9, "VSCSI"), ]) libxl__console_backend = Enumeration("console_backend", [ diff --git a/tools/libxl/libxl_vscsi.c b/tools/libxl/libxl_vscsi.c new file mode 100644 index 0000000..a29117c --- /dev/null +++ b/tools/libxl/libxl_vscsi.c @@ -0,0 +1,274 @@ +/* + * Copyright (C) 2015 SUSE Linux GmbH + * Author Olaf Hering <olaf@xxxxxxxxx> + * + * 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" /* must come before any other headers */ +#include "libxl_internal.h" + +static int vscsi_parse_hctl(char *str, libxl_vscsi_hctl *hctl) +{ + unsigned int hst, chn, tgt, lun; + + if (sscanf(str, "%u:%u:%u:%u", &hst, &chn, &tgt, &lun) != 4) + return ERROR_INVAL; + + hctl->hst = hst; + hctl->chn = chn; + hctl->tgt = tgt; + hctl->lun = lun; + return 0; +} + +static bool vscsi_wwn_valid(const char *p) +{ + bool ret = true; + int i = 0; + + for (i = 0; i < 16; i++, p++) { + if (*p >= '0' && *p <= '9') + continue; + if (*p >= 'a' && *p <= 'f') + continue; + if (*p >= 'A' && *p <= 'F') + continue; + ret = false; + break; + } + return ret; +} + +/* Translate p-dev back into pdev.type */ +static bool vscsi_parse_pdev(libxl__gc *gc, libxl_vscsi_dev *v_dev, + char *c, char *p, char *v) +{ + libxl_vscsi_hctl hctl; + unsigned int lun; + char wwn[16 + 1]; + bool parsed_ok = false; + + libxl_vscsi_hctl_init(&hctl); + + v_dev->pdev.p_devname = libxl__strdup(NOGC, c); + + if (strncmp(p, "naa.", 4) == 0) { + /* WWN as understood by pvops */ + memset(wwn, 0, sizeof(wwn)); + if (sscanf(p, "naa.%16c:%u", wwn, &lun) == 2 && vscsi_wwn_valid(wwn)) { + libxl_vscsi_pdev_init_type(&v_dev->pdev, LIBXL_VSCSI_PDEV_TYPE_WWN); + v_dev->pdev.u.wwn.m = libxl__strdup(NOGC, p); + parsed_ok = true; + } + } else if (vscsi_parse_hctl(p, &hctl) == 0) { + /* Either xenlinux, or pvops with properly configured alias in sysfs */ + libxl_vscsi_pdev_init_type(&v_dev->pdev, LIBXL_VSCSI_PDEV_TYPE_HCTL); + libxl_vscsi_hctl_copy(CTX, &v_dev->pdev.u.hctl.m, &hctl); + parsed_ok = true; + } + + if (parsed_ok && vscsi_parse_hctl(v, &v_dev->vdev) != 0) + parsed_ok = false; + + libxl_vscsi_hctl_dispose(&hctl); + + return parsed_ok; +} + +static void libxl__vscsi_fill_host(libxl__gc *gc, + const char *devs_path, + char **dev_dirs, + unsigned int ndev_dirs, + libxl_device_vscsi *v_hst) +{ + libxl_vscsi_dev *v_dev; + bool parsed_ok; + char *c, *p, *v, *s, *dev; + unsigned int vscsi_dev_id; + int i, r; + + v_hst->vscsi_devs = libxl__malloc(NOGC, ndev_dirs * sizeof(*v_dev)); + v_hst->num_vscsi_devs = ndev_dirs; + /* Fill each device connected to the host */ + for (i = 0; i < ndev_dirs; i++, dev_dirs++) { + v_dev = v_hst->vscsi_devs + i; + libxl_vscsi_dev_init(v_dev); + parsed_ok = false; + r = sscanf(*dev_dirs, "dev-%u", &vscsi_dev_id); + if (r != 1) { + LOG(ERROR, "expected dev-N, got '%s'", *dev_dirs); + continue; + } + + dev = GCSPRINTF("%s/%s", devs_path, *dev_dirs); + c = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/p-devname", dev)); + p = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/p-dev", dev)); + v = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/v-dev", dev)); + s = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/state", dev)); + LOG(DEBUG, "%s/state is %s", dev, s); + if (!(c && p && v && s)) { + LOG(ERROR, "p-devname '%s' p-dev '%s' v-dev '%s'", c, p, v); + continue; + } + + parsed_ok = vscsi_parse_pdev(gc, v_dev, c, p, v); + switch (atoi(s)) { + case XenbusStateUnknown: + case XenbusStateInitialising: + case XenbusStateInitWait: + case XenbusStateInitialised: + case XenbusStateConnected: + case XenbusStateReconfiguring: + case XenbusStateReconfigured: + break; + case XenbusStateClosing: + case XenbusStateClosed: + v_dev->remove = true; + break; + } + + /* Indication for caller that this v_dev is usable */ + if (parsed_ok) { + v_dev->vscsi_dev_id = vscsi_dev_id; + if (vscsi_dev_id >= v_hst->next_vscsi_dev_id || + v_hst->next_vscsi_dev_id == -1) + v_hst->next_vscsi_dev_id = vscsi_dev_id + 1; + v_hst->v_hst = v_dev->vdev.hst; + } + + /* FIXME what if xenstore is broken? */ + if (!parsed_ok) + LOG(ERROR, "%s failed to parse", dev); + } +} + +libxl_device_vscsi *libxl_device_vscsi_list(libxl_ctx *ctx, uint32_t domid, + int *num) +{ + GC_INIT(ctx); + libxl_device_vscsi *v_hst, *vscsi_hosts = NULL; + char *fe_path, *tmp; + char **dir, **dev_dirs; + const char *devs_path, *be_path; + bool parsed_ok; + unsigned int ndirs = 0, ndev_dirs; + + fe_path = GCSPRINTF("%s/device/vscsi", libxl__xs_get_dompath(gc, domid)); + dir = libxl__xs_directory(gc, XBT_NULL, fe_path, &ndirs); + /* Nothing to do */ + if (!(dir && ndirs)) + goto out; + + /* List of hosts to be returned to the caller */ + vscsi_hosts = libxl__malloc(NOGC, ndirs * sizeof(*vscsi_hosts)); + + /* Fill each host */ + for (v_hst = vscsi_hosts; v_hst < vscsi_hosts + ndirs; ++v_hst, ++dir) { + libxl_device_vscsi_init(v_hst); + + v_hst->devid = atoi(*dir); + + tmp = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/%s/backend-id", + fe_path, *dir)); + /* FIXME what if xenstore is broken? */ + if (tmp) + v_hst->backend_domid = atoi(tmp); + + be_path = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/%s/backend", + fe_path, *dir)); + /* FIXME what if xenstore is broken? */ + if (!be_path) { + libxl_defbool_set(&v_hst->scsi_raw_cmds, false); + continue; + } + + parsed_ok = false; + tmp = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/feature-host", be_path)); + if (tmp) + parsed_ok = atoi(tmp) != 0; + libxl_defbool_set(&v_hst->scsi_raw_cmds, parsed_ok); + + devs_path = GCSPRINTF("%s/vscsi-devs", be_path); + dev_dirs = libxl__xs_directory(gc, XBT_NULL, devs_path, &ndev_dirs); + if (dev_dirs && ndev_dirs) + libxl__vscsi_fill_host(gc, devs_path, dev_dirs, ndev_dirs, v_hst); + } + +out: + *num = ndirs; + + GC_FREE; + return vscsi_hosts; +} + +int libxl_device_vscsi_getinfo(libxl_ctx *ctx, uint32_t domid, + libxl_device_vscsi *vscsi_host, + libxl_vscsi_dev *vscsi_dev, + libxl_vscsiinfo *vscsiinfo) +{ + GC_INIT(ctx); + char *dompath, *vscsipath; + char *val; + int rc = ERROR_FAIL; + + libxl_vscsiinfo_init(vscsiinfo); + dompath = libxl__xs_get_dompath(gc, domid); + vscsiinfo->devid = vscsi_host->devid; + libxl_vscsi_pdev_copy(ctx, &vscsiinfo->pdev, &vscsi_dev->pdev); + libxl_vscsi_hctl_copy(ctx, &vscsiinfo->vdev, &vscsi_dev->vdev); + + vscsipath = GCSPRINTF("%s/device/vscsi/%d", dompath, vscsiinfo->devid); + vscsiinfo->backend = xs_read(ctx->xsh, XBT_NULL, + GCSPRINTF("%s/backend", vscsipath), NULL); + if (!vscsiinfo->backend) + goto out; + if(!libxl__xs_read(gc, XBT_NULL, vscsiinfo->backend)) + goto out; + + val = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/backend-id", vscsipath)); + vscsiinfo->backend_id = val ? strtoul(val, NULL, 10) : -1; + + val = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/state", vscsipath)); + vscsiinfo->vscsi_host_state = val ? strtoul(val, NULL, 10) : -1; + + val = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/event-channel", vscsipath)); + vscsiinfo->evtch = val ? strtoul(val, NULL, 10) : -1; + + val = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/ring-ref", vscsipath)); + vscsiinfo->rref = val ? strtoul(val, NULL, 10) : -1; + + vscsiinfo->frontend = xs_read(ctx->xsh, XBT_NULL, + GCSPRINTF("%s/frontend", vscsiinfo->backend), NULL); + + val = libxl__xs_read(gc, XBT_NULL, + GCSPRINTF("%s/frontend-id", vscsiinfo->backend)); + vscsiinfo->frontend_id = val ? strtoul(val, NULL, 10) : -1; + + val = libxl__xs_read(gc, XBT_NULL, + GCSPRINTF("%s/vscsi-devs/dev-%u/state", + vscsiinfo->backend, vscsi_dev->vscsi_dev_id)); + vscsiinfo->vscsi_dev_state = val ? strtoul(val, NULL, 10) : -1; + + rc = 0; +out: + GC_FREE; + return rc; +} + + + +/* + * Local variables: + * mode: C + * c-basic-offset: 4 + * indent-tabs-mode: nil + * End: + */ diff --git a/tools/libxl/libxlu_vscsi.c b/tools/libxl/libxlu_vscsi.c new file mode 100644 index 0000000..4131cfb --- /dev/null +++ b/tools/libxl/libxlu_vscsi.c @@ -0,0 +1,745 @@ +/* + * libxlu_vscsi.c - xl configuration file parsing: setup and helper functions + * + * Copyright (C) 2015 SUSE Linux GmbH + * Author Olaf Hering <olaf@xxxxxxxxx> + * Author OndÅej HoleÄek <aaannz@xxxxxxxxx> + * + * 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" /* must come before any other headers */ +#include <unistd.h> +#include <ctype.h> +#include <dirent.h> +#include <sys/stat.h> +#include <fcntl.h> +#include "libxlu_internal.h" + +#ifdef __linux__ +#define LOG(_c, _x, _a...) \ + if((_c) && (_c)->report) fprintf((_c)->report, "%s(%u): " _x "\n", __func__, __LINE__, ##_a) + +#define XLU_SYSFS_TARGET_PVSCSI "/sys/kernel/config/target/xen-pvscsi" +#define XLU_WWN_LEN 16 +struct xlu__vscsi_target { + XLU_Config *cfg; + libxl_vscsi_hctl *pdev_hctl; + libxl_vscsi_pdev *pdev; + char path[PATH_MAX]; + char udev_path[PATH_MAX]; + char wwn[XLU_WWN_LEN + 1]; + unsigned int lun; +}; + +static int xlu__vscsi_parse_hctl(char *str, libxl_vscsi_hctl *hctl) +{ + unsigned int hst, chn, tgt, lun; + + if (sscanf(str, "%u:%u:%u:%u", &hst, &chn, &tgt, &lun) != 4) + return ERROR_INVAL; + + hctl->hst = hst; + hctl->chn = chn; + hctl->tgt = tgt; + hctl->lun = lun; + return 0; +} + +static char *xlu__vscsi_trim_string(char *s) +{ + size_t len; + + while (isspace(*s)) + s++; + len = strlen(s); + while (len-- > 1 && isspace(s[len])) + s[len] = '\0'; + return s; +} + + +static int xlu__vscsi_parse_dev(XLU_Config *cfg, char *pdev, libxl_vscsi_hctl *hctl) +{ + struct stat dentry; + char *sysfs = NULL; + const char *type; + int rc, found = 0; + DIR *dirp; + struct dirent *de; + + /* stat pdev to get device's sysfs entry */ + if (stat (pdev, &dentry) < 0) { + LOG(cfg, "%s, device node not found", pdev); + rc = ERROR_INVAL; + goto out; + } + + if (S_ISBLK (dentry.st_mode)) { + type = "block"; + } else if (S_ISCHR (dentry.st_mode)) { + type = "char"; + } else { + LOG(cfg, "%s, device node not a block or char device", pdev); + rc = ERROR_INVAL; + goto out; + } + + /* /sys/dev/type/major:minor symlink added in 2.6.27 */ + if (asprintf(&sysfs, "/sys/dev/%s/%u:%u/device/scsi_device", type, + major(dentry.st_rdev), minor(dentry.st_rdev)) < 0) { + sysfs = NULL; + rc = ERROR_NOMEM; + goto out; + } + + dirp = opendir(sysfs); + if (!dirp) { + LOG(cfg, "%s, no major:minor link in sysfs", pdev); + rc = ERROR_INVAL; + goto out; + } + + while ((de = readdir(dirp))) { + if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, "..")) + continue; + + if (xlu__vscsi_parse_hctl(de->d_name, hctl)) + continue; + + found = 1; + break; + } + closedir(dirp); + + if (!found) { + LOG(cfg, "%s, no h:c:t:l link in sysfs", pdev); + rc = ERROR_INVAL; + goto out; + } + + rc = 0; +out: + free(sysfs); + return rc; +} + +static bool xlu__vscsi_wwn_valid(const char *p) +{ + bool ret = true; + int i = 0; + + for (i = 0; i < XLU_WWN_LEN; i++, p++) { + if (*p >= '0' && *p <= '9') + continue; + if (*p >= 'a' && *p <= 'f') + continue; + if (*p >= 'A' && *p <= 'F') + continue; + ret = false; + break; + } + return ret; +} + +static bool xlu__vscsi_compare_hctl(libxl_vscsi_hctl *a, libxl_vscsi_hctl *b) +{ + if (a->hst == b->hst && + a->chn == b->chn && + a->tgt == b->tgt && + a->lun == b->lun) + return true; + return false; +} + +/* Finally at + * /sys/kernel/config/target/xen-pvscsi/naa.<wwn>/tpgt_1/lun/lun_0/<X>/udev_path + */ +static bool xlu__vscsi_compare_udev(struct xlu__vscsi_target *tgt) +{ + bool ret; + int fd; + ssize_t read_sz; + libxl_vscsi_hctl udev_hctl; + + libxl_vscsi_hctl_init(&udev_hctl); + + fd = open(tgt->path, O_RDONLY); + if (fd < 0){ + ret = false; + goto out; + } + read_sz = read(fd, &tgt->udev_path, sizeof(tgt->udev_path) - 1); + close(fd); + + if (read_sz < 0 || read_sz > sizeof(tgt->udev_path) - 1) { + ret = false; + goto out; + } + tgt->udev_path[read_sz] = '\0'; + read_sz--; + if (tgt->udev_path[read_sz] == '\n') + tgt->udev_path[read_sz] = '\0'; + + if (xlu__vscsi_parse_dev(tgt->cfg, tgt->udev_path, &udev_hctl)) { + ret = false; + goto out; + } + ret = xlu__vscsi_compare_hctl(tgt->pdev_hctl, &udev_hctl); + +out: + libxl_vscsi_hctl_dispose(&udev_hctl); + return ret; +} + +/* /sys/kernel/config/target/xen-pvscsi/naa.<wwn>/tpgt_1/lun/lun_0/<X>/udev_path */ +static bool xlu__vscsi_walk_dir_lun(struct xlu__vscsi_target *tgt) +{ + bool found; + DIR *dirp; + struct dirent *de; + size_t path_len = strlen(tgt->path); + char *subdir = &tgt->path[path_len]; + + dirp = opendir(tgt->path); + if (!dirp) + return false; + + found = false; + while ((de = readdir(dirp))) { + if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, "..")) + continue; + + snprintf(subdir, sizeof(tgt->path) - path_len, "/%s/udev_path", de->d_name); + + found = xlu__vscsi_compare_udev(tgt); + if (found) + break; + + *subdir = '\0'; + } + closedir(dirp); + return found; +} + +/* /sys/kernel/config/target/xen-pvscsi/naa.<wwn>/tpgt_1/lun/lun_0 */ +static bool xlu__vscsi_walk_dir_luns(struct xlu__vscsi_target *tgt) +{ + bool found; + DIR *dirp; + struct dirent *de; + size_t path_len = strlen(tgt->path); + char *subdir = &tgt->path[path_len]; + + dirp = opendir(tgt->path); + if (!dirp) + return false; + + found = false; + while ((de = readdir(dirp))) { + if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, "..")) + continue; + + if (sscanf(de->d_name, "lun_%u", &tgt->lun) != 1) + continue; + + + snprintf(subdir, sizeof(tgt->path) - path_len, "/%s", de->d_name); + + found = xlu__vscsi_walk_dir_lun(tgt); + if (found) + break; + + *subdir = '\0'; + } + closedir(dirp); + return found; +} + +/* /sys/kernel/config/target/xen-pvscsi/naa.<wwn>/tpgt_1 */ +static bool xlu__vscsi_walk_dir_naa(struct xlu__vscsi_target *tgt) +{ + bool found; + DIR *dirp; + struct dirent *de; + size_t path_len = strlen(tgt->path); + char *subdir = &tgt->path[path_len]; + unsigned int tpgt; + + dirp = opendir(tgt->path); + if (!dirp) + return false; + + found = false; + while ((de = readdir(dirp))) { + if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, "..")) + continue; + + if (sscanf(de->d_name, "tpgt_%u", &tpgt) != 1) + continue; + + snprintf(subdir, sizeof(tgt->path) - path_len, "/%s/lun", de->d_name); + + found = xlu__vscsi_walk_dir_luns(tgt); + if (found) + break; + + *subdir = '\0'; + } + closedir(dirp); + return found; +} + +/* /sys/kernel/config/target/xen-pvscsi/naa.<wwn> */ +static bool xlu__vscsi_find_target_wwn(struct xlu__vscsi_target *tgt) +{ + bool found; + DIR *dirp; + struct dirent *de; + size_t path_len = strlen(tgt->path); + char *subdir = &tgt->path[path_len]; + + dirp = opendir(tgt->path); + if (!dirp) + return false; + + found = false; + while ((de = readdir(dirp))) { + if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, "..")) + continue; + + if (sscanf(de->d_name, "naa.%16c", tgt->wwn) != 1) + continue; + if (!xlu__vscsi_wwn_valid(tgt->wwn)) + continue; + + snprintf(subdir, sizeof(tgt->path) - path_len, "/%s", de->d_name); + + found = xlu__vscsi_walk_dir_naa(tgt); + if (found) + break; + + *subdir = '\0'; + } + closedir(dirp); + return found; +} + +/* + * Convert pdev from config string into pdev property for backend, + * which is either h:c:t:l for xenlinux or naa.wwn:lun for pvops + */ +static int xlu__vscsi_dev_to_pdev(XLU_Config *cfg, libxl_ctx *ctx, char *str, + libxl_vscsi_hctl *pdev_hctl, + libxl_vscsi_pdev *pdev) +{ + int rc = ERROR_INVAL; + struct xlu__vscsi_target *tgt; + static const char xen_pvscsi[] = XLU_SYSFS_TARGET_PVSCSI; + + /* First get hctl representation of config item */ + if (xlu__vscsi_parse_dev(cfg, str, pdev_hctl)) + goto out; + + /* Check if a SCSI target item exists for the config item */ + if (access(xen_pvscsi, F_OK) == 0) { + tgt = calloc(1, sizeof(*tgt)); + if (!tgt) { + rc = ERROR_NOMEM; + goto out; + } + tgt->cfg = cfg; + tgt->pdev_hctl = pdev_hctl; + tgt->pdev = pdev; + snprintf(tgt->path, sizeof(tgt->path), "%s", xen_pvscsi); + if (xlu__vscsi_find_target_wwn(tgt) == true) { + LOG(cfg, "'%s' maps to '%s(%s)'", str, tgt->path, tgt->udev_path); + libxl_vscsi_pdev_init_type(pdev, LIBXL_VSCSI_PDEV_TYPE_WWN); + if (asprintf(&pdev->u.wwn.m, "naa.%s:%u", tgt->wwn, tgt->lun) < 0) { + rc = ERROR_NOMEM; + goto out; + } + } + free(tgt); + } else { + /* Assume xenlinux backend */ + libxl_vscsi_pdev_init_type(pdev, LIBXL_VSCSI_PDEV_TYPE_HCTL); + libxl_vscsi_hctl_copy(ctx, &pdev->u.hctl.m, pdev_hctl); + } + rc = 0; + +out: + return rc; +} + +/* WWN as understood by pvops */ +static int xlu__vscsi_wwn_to_pdev(XLU_Config *cfg, char *str, libxl_vscsi_pdev *pdev) +{ + int rc = ERROR_INVAL; + unsigned int lun; + char wwn[XLU_WWN_LEN + 1]; + + memset(wwn, 0, sizeof(wwn)); + if (sscanf(str, "naa.%16c:%u", wwn, &lun) == 2 && xlu__vscsi_wwn_valid(wwn)) { + libxl_vscsi_pdev_init_type(pdev, LIBXL_VSCSI_PDEV_TYPE_WWN); + pdev->u.wwn.m = strdup(str); + rc = pdev->u.wwn.m ? 0 : ERROR_NOMEM; + } + return rc; +} + +static int xlu__vscsi_parse_pdev(XLU_Config *cfg, libxl_ctx *ctx, char *str, + libxl_vscsi_pdev *pdev) +{ + int rc = ERROR_INVAL; + libxl_vscsi_hctl pdev_hctl; + + libxl_vscsi_hctl_init(&pdev_hctl); + if (strncmp(str, "/dev/", 5) == 0) { + rc = xlu__vscsi_dev_to_pdev(cfg, ctx, str, &pdev_hctl, pdev); + } else if (strncmp(str, "naa.", 4) == 0) { + rc = xlu__vscsi_wwn_to_pdev(cfg, str, pdev); + } else if (xlu__vscsi_parse_hctl(str, &pdev_hctl) == 0) { + /* Either xenlinux, or pvops with properly configured alias in sysfs */ + libxl_vscsi_pdev_init_type(pdev, LIBXL_VSCSI_PDEV_TYPE_HCTL); + libxl_vscsi_hctl_copy(ctx, &pdev->u.hctl.m, &pdev_hctl); + rc = 0; + } + + if (rc == 0) { + pdev->p_devname = strdup(str); + if (!pdev->p_devname) + rc = ERROR_NOMEM; + } + + libxl_vscsi_hctl_dispose(&pdev_hctl); + return rc; +} + +int xlu_vscsi_parse(XLU_Config *cfg, libxl_ctx *ctx, const char *str, + libxl_device_vscsi *new_host, + libxl_vscsi_dev *new_dev) +{ + int rc; + char *tmp, *pdev, *vdev, *fhost; + + tmp = strdup(str); + if (!tmp) { + rc = ERROR_NOMEM; + goto out; + } + + pdev = strtok(tmp, ","); + vdev = strtok(NULL, ","); + fhost = strtok(NULL, ","); + if (!(pdev && vdev)) { + LOG(cfg, "invalid devspec: '%s'\n", str); + rc = ERROR_INVAL; + goto out; + } + + pdev = xlu__vscsi_trim_string(pdev); + vdev = xlu__vscsi_trim_string(vdev); + + rc = xlu__vscsi_parse_pdev(cfg, ctx, pdev, &new_dev->pdev); + if (rc) { + LOG(cfg, "failed to parse %s, rc == %d", pdev, rc); + goto out; + } + + if (xlu__vscsi_parse_hctl(vdev, &new_dev->vdev)) { + LOG(cfg, "invalid '%s', expecting hst:chn:tgt:lun", vdev); + rc = ERROR_INVAL; + goto out; + } + + /* Record group index */ + new_host->v_hst = new_dev->vdev.hst; + + if (fhost) { + fhost = xlu__vscsi_trim_string(fhost); + if (strcmp(fhost, "feature-host") == 0) { + libxl_defbool_set(&new_host->scsi_raw_cmds, true); + } else { + LOG(cfg, "invalid option '%s', expecting %s", fhost, "feature-host"); + rc = ERROR_INVAL; + goto out; + } + } else + libxl_defbool_set(&new_host->scsi_raw_cmds, false); + rc = 0; + +out: + free(tmp); + return rc; +} + + +static int xlu_vscsi_append_dev(libxl_ctx *ctx, libxl_device_vscsi *hst, + libxl_vscsi_dev *dev) +{ + int rc; + libxl_vscsi_dev *devs; + + devs = realloc(hst->vscsi_devs, sizeof(*dev) * (hst->num_vscsi_devs + 1)); + if (!devs) { + rc = ERROR_NOMEM; + goto out; + } + + hst->vscsi_devs = devs; + libxl_vscsi_dev_init(hst->vscsi_devs + hst->num_vscsi_devs); + dev->vscsi_dev_id = hst->next_vscsi_dev_id; + libxl_vscsi_dev_copy(ctx, hst->vscsi_devs + hst->num_vscsi_devs, dev); + hst->num_vscsi_devs++; + hst->next_vscsi_dev_id++; + rc = 0; +out: + return rc; +} + +int xlu_vscsi_get_host(XLU_Config *cfg, libxl_ctx *ctx, uint32_t domid, + const char *str, libxl_device_vscsi *vscsi_host) +{ + libxl_vscsi_dev *new_dev = NULL; + libxl_device_vscsi *new_host, *vscsi_hosts = NULL, *tmp; + int rc, found_host = -1, i; + int num_hosts; + + new_host = malloc(sizeof(*new_host)); + new_dev = malloc(sizeof(*new_dev)); + if (!(new_host && new_dev)) { + rc = ERROR_NOMEM; + goto out; + } + libxl_device_vscsi_init(new_host); + libxl_vscsi_dev_init(new_dev); + + rc = xlu_vscsi_parse(cfg, ctx, str, new_host, new_dev); + if (rc) + goto out; + + /* Look for existing vscsi_host for given domain */ + vscsi_hosts = libxl_device_vscsi_list(ctx, domid, &num_hosts); + if (vscsi_hosts) { + for (i = 0; i < num_hosts; ++i) { + if (vscsi_hosts[i].v_hst == new_host->v_hst) { + found_host = i; + break; + } + } + } + + if (found_host == -1) { + /* Not found, create new host */ + new_host->next_vscsi_dev_id = 0; + tmp = new_host; + } else { + tmp = vscsi_hosts + found_host; + + /* Check if the vdev address is already taken */ + for (i = 0; i < tmp->num_vscsi_devs; ++i) { + if (tmp->vscsi_devs[i].vscsi_dev_id != -1 && + tmp->vscsi_devs[i].vdev.chn == new_dev->vdev.chn && + tmp->vscsi_devs[i].vdev.tgt == new_dev->vdev.tgt && + tmp->vscsi_devs[i].vdev.lun == new_dev->vdev.lun) { + LOG(cfg, "vdev '%u:%u:%u:%u' is already used.\n", + new_dev->vdev.hst, new_dev->vdev.chn, new_dev->vdev.tgt, new_dev->vdev.lun); + rc = ERROR_INVAL; + goto out; + } + } + + if (libxl_defbool_val(new_host->scsi_raw_cmds) != + libxl_defbool_val(tmp->scsi_raw_cmds)) { + LOG(cfg, "different feature-host setting: " + "existing host has it %s, new host has it %s\n", + libxl_defbool_val(new_host->scsi_raw_cmds) ? "set" : "unset", + libxl_defbool_val(tmp->scsi_raw_cmds) ? "set" : "unset"); + rc = ERROR_INVAL; + goto out; + } + } + + libxl_device_vscsi_copy(ctx, vscsi_host, tmp); + rc = xlu_vscsi_append_dev(ctx, vscsi_host, new_dev); + if (rc) + goto out; + + rc = 0; + +out: + if (vscsi_hosts) { + for (i = 0; i < num_hosts; ++i) + libxl_device_vscsi_dispose(&vscsi_hosts[i]); + free(vscsi_hosts); + } + libxl_vscsi_dev_dispose(new_dev); + libxl_device_vscsi_dispose(new_host); + free(new_dev); + free(new_host); + return rc; +} + +int xlu_vscsi_detach(XLU_Config *cfg, libxl_ctx *ctx, uint32_t domid, char *str) +{ + libxl_vscsi_dev v_dev = { }, *vd; + libxl_device_vscsi v_hst = { }, *vh, *vscsi_hosts; + int num_hosts, h, d, found = 0; + char *tmp = NULL; + + libxl_device_vscsi_init(&v_hst); + libxl_vscsi_dev_init(&v_dev); + + /* Create a dummy cfg */ + if (asprintf(&tmp, "0:0:0:0,%s", str) < 0) { + LOG(cfg, "asprintf failed while removing %s from domid %u", str, domid); + goto out; + } + + if (xlu_vscsi_parse(cfg, ctx, tmp, &v_hst, &v_dev)) + goto out; + + vscsi_hosts = libxl_device_vscsi_list(ctx, domid, &num_hosts); + if (!vscsi_hosts) + goto out; + + for (h = 0; h < num_hosts; ++h) { + vh = vscsi_hosts + h; + for (d = 0; d < vh->num_vscsi_devs; d++) { + vd = vh->vscsi_devs + d; +#define CMP(member) (vd->vdev.member == v_dev.vdev.member) + if (!found && vd->vscsi_dev_id != -1 && + CMP(hst) && CMP(chn) && CMP(tgt) && CMP(lun)) { + vd->remove = true; + libxl_device_vscsi_remove(ctx, domid, vh, NULL); + found = 1; + } +#undef CMP + libxl_vscsi_dev_dispose(vd); + } + libxl_device_vscsi_dispose(vh); + } + free(vscsi_hosts); + +out: + free(tmp); + libxl_vscsi_dev_dispose(&v_dev); + libxl_device_vscsi_dispose(&v_hst); + return found; +} + +int xlu_vscsi_config_add(XLU_Config *cfg, + libxl_ctx *ctx, + const char *str, + int *num_vscsis, + libxl_device_vscsi **vscsis) +{ + int rc, i; + libxl_vscsi_dev v_dev = { }; + libxl_device_vscsi *tmp, v_hst = { }; + bool hst_found = false; + + /* + * #1: parse the devspec and place it in temporary host+dev part + * #2: find existing vscsi_host with number v_hst + * if found, append the vscsi_dev to this vscsi_host + * #3: otherwise, create new vscsi_host and append vscsi_dev + * Note: v_hst does not represent the index named "num_vscsis", + * it is a private index used just in the config file + */ + libxl_device_vscsi_init(&v_hst); + libxl_vscsi_dev_init(&v_dev); + + rc = xlu_vscsi_parse(cfg, ctx, str, &v_hst, &v_dev); + if (rc) + goto out; + + if (*num_vscsis) { + for (i = 0; i < *num_vscsis; i++) { + tmp = *vscsis + i; + if (tmp->v_hst == v_hst.v_hst) { + rc = xlu_vscsi_append_dev(ctx, tmp, &v_dev); + if (rc) { + LOG(cfg, "xlu_vscsi_append_dev failed: %d\n", rc); + goto out; + } + hst_found = true; + break; + } + } + } + + if (!hst_found || !*num_vscsis) { + tmp = realloc(*vscsis, sizeof(v_hst) * (*num_vscsis + 1)); + if (!tmp) { + LOG(cfg, "realloc #%d failed", *num_vscsis + 1); + rc = ERROR_NOMEM; + goto out; + } + *vscsis = tmp; + tmp = *vscsis + *num_vscsis; + libxl_device_vscsi_init(tmp); + + v_hst.devid = *num_vscsis; + v_hst.next_vscsi_dev_id = 0; + libxl_device_vscsi_copy(ctx, tmp, &v_hst); + + rc = xlu_vscsi_append_dev(ctx, tmp, &v_dev); + if (rc) { + LOG(cfg, "xlu_vscsi_append_dev failed: %d\n", rc); + goto out; + } + + (*num_vscsis)++; + } + + rc = 0; +out: + libxl_vscsi_dev_dispose(&v_dev); + libxl_device_vscsi_dispose(&v_hst); + return rc; +} +#else /* ! __linux__ */ +int xlu_vscsi_get_host(XLU_Config *config, + libxl_ctx *ctx, + uint32_t domid, + const char *str, + libxl_device_vscsi *vscsi_host) +{ + return ERROR_INVAL; +} + +int xlu_vscsi_parse(XLU_Config *cfg, + libxl_ctx *ctx, + const char *str, + libxl_device_vscsi *new_host, + libxl_vscsi_dev *new_dev) +{ + return ERROR_INVAL; +} + +int xlu_vscsi_detach(XLU_Config *cfg, + libxl_ctx *ctx, + uint32_t domid, + char *str) +{ + return ERROR_INVAL; +} + +int xlu_vscsi_config_add(XLU_Config *cfg, + libxl_ctx *ctx, + const char *str, + int *num_vscsis, + libxl_device_vscsi **vscsis) +{ + return ERROR_INVAL; +} +#endif diff --git a/tools/libxl/libxlutil.h b/tools/libxl/libxlutil.h index 989605a..1bd761c 100644 --- a/tools/libxl/libxlutil.h +++ b/tools/libxl/libxlutil.h @@ -114,6 +114,24 @@ int xlu_pci_parse_bdf(XLU_Config *cfg, libxl_device_pci *pcidev, const char *str int xlu_vif_parse_rate(XLU_Config *cfg, const char *rate, libxl_device_nic *nic); +/* Fill vscsi_host with device described in str (pdev,vdev[,options]) */ +int xlu_vscsi_get_host(XLU_Config *config, + libxl_ctx *ctx, + uint32_t domid, + const char *str, + libxl_device_vscsi *vscsi_host); +/* Parse config string and fill provided vscsi host and vscsi device */ +int xlu_vscsi_parse(XLU_Config *cfg, libxl_ctx *ctx, const char *str, + libxl_device_vscsi *new_host, + libxl_vscsi_dev *new_dev); +/* Detach vscsi device described in config string (pdev,vdev[,options]) */ +int xlu_vscsi_detach(XLU_Config *cfg, libxl_ctx *ctx, uint32_t domid, char *str); +/* Add vscsi device described in config string (pdev,vdev[,options]) to d_config */ +int xlu_vscsi_config_add(XLU_Config *cfg, + libxl_ctx *ctx, + const char *str, + int *num_vscsis, + libxl_device_vscsi **vscsis); #endif /* LIBXLUTIL_H */ /* diff --git a/tools/libxl/xl.h b/tools/libxl/xl.h index 5bc138c..5c82688 100644 --- a/tools/libxl/xl.h +++ b/tools/libxl/xl.h @@ -83,6 +83,9 @@ int main_channellist(int argc, char **argv); int main_blockattach(int argc, char **argv); int main_blocklist(int argc, char **argv); int main_blockdetach(int argc, char **argv); +int main_vscsiattach(int argc, char **argv); +int main_vscsilist(int argc, char **argv); +int main_vscsidetach(int argc, char **argv); int main_vtpmattach(int argc, char **argv); int main_vtpmlist(int argc, char **argv); int main_vtpmdetach(int argc, char **argv); diff --git a/tools/libxl/xl_cmdimpl.c b/tools/libxl/xl_cmdimpl.c index 648ca08..4d5d69a 100644 --- a/tools/libxl/xl_cmdimpl.c +++ b/tools/libxl/xl_cmdimpl.c @@ -1168,7 +1168,7 @@ static void parse_config_data(const char *config_source, const char *buf; long l, vcpus = 0; XLU_Config *config; - XLU_ConfigList *cpus, *vbds, *nics, *pcis, *cvfbs, *cpuids, *vtpms; + XLU_ConfigList *cpus, *vbds, *nics, *pcis, *cvfbs, *cpuids, *vtpms, *vscsis; XLU_ConfigList *channels, *ioports, *irqs, *iomem, *viridian; int num_ioports, num_irqs, num_iomem, num_cpus, num_viridian; int pci_power_mgmt = 0; @@ -1683,6 +1683,17 @@ static void parse_config_data(const char *config_source, } } + if (!xlu_cfg_get_list(config, "vscsi", &vscsis, 0, 0)) { + int num_vscsi_items = 0; + d_config->num_vscsis = 0; + d_config->vscsis = NULL; + while ((buf = xlu_cfg_get_listitem (vscsis, num_vscsi_items)) != NULL) { + if (xlu_vscsi_config_add(config, ctx, buf, &d_config->num_vscsis, &d_config->vscsis)) + exit(1); + num_vscsi_items++; + } + } + if (!xlu_cfg_get_list(config, "vtpm", &vtpms, 0, 0)) { d_config->num_vtpms = 0; d_config->vtpms = NULL; @@ -6643,6 +6654,198 @@ int main_blockdetach(int argc, char **argv) return rc; } +int main_vscsiattach(int argc, char **argv) +{ + uint32_t domid; + int opt, rc; + XLU_Config *config = NULL; + libxl_device_vscsi *vscsi_host = NULL; + char *str = NULL, *feat_buf = NULL; + + SWITCH_FOREACH_OPT(opt, "", NULL, "scsi-attach", 1) { + /* No options */ + } + + if (argc < 4 || argc > 5) { + help("scsi-attach"); + return 1; + } + + if (libxl_domain_qualifier_to_domid(ctx, argv[optind], &domid) < 0) { + fprintf(stderr, "%s is an invalid domain identifier\n", argv[optind]); + return 1; + } + + optind++; + + if (argc == 5) { + if (asprintf(&feat_buf, ",%s", argv[4]) < 0) { + perror("asprintf"); + return 1; + } + } + + if (asprintf(&str, "%s,%s%s", argv[2], argv[3], feat_buf ?: "") < 0) { + perror("asprintf"); + rc = 1; + goto out;; + } + + vscsi_host = xmalloc(sizeof(*vscsi_host)); + libxl_device_vscsi_init(vscsi_host); + + config = xlu_cfg_init(stderr, "command line"); + if (!config) { + fprintf(stderr, "Failed to allocate for configuration\n"); + rc = 1; + goto out; + } + + /* Parse config string and store result */ + rc = xlu_vscsi_get_host(config, ctx, domid, str, vscsi_host); + if (rc < 0) + goto out; + + if (dryrun_only) { + char *json = libxl_device_vscsi_to_json(ctx, vscsi_host); + printf("vscsi: %s\n", json); + free(json); + if (ferror(stdout) || fflush(stdout)) { perror("stdout"); exit(-1); } + rc = 0; + goto out; + } + + /* Finally add the device */ + if (libxl_device_vscsi_add(ctx, domid, vscsi_host, NULL)) { + fprintf(stderr, "libxl_device_vscsi_add failed.\n"); + rc = 1; + goto out; + } + + rc = 0; +out: + if (config) + xlu_cfg_destroy(config); + libxl_device_vscsi_dispose(vscsi_host); + free(vscsi_host); + free(str); + free(feat_buf); + return rc; +} + +int main_vscsilist(int argc, char **argv) +{ + int opt; + uint32_t domid; + libxl_device_vscsi *vscsi_hosts; + libxl_vscsiinfo vscsiinfo; + int num_hosts, h, d; + + SWITCH_FOREACH_OPT(opt, "", NULL, "scsi-list", 1) { + /* No options */ + } + if (argc < 2) { + help("scsi-list"); + return 1; + } + + /* Idx BE state host p_hst v_hst state */ + printf("%-3s %-3s %-5s %-5s %-10s %-10s %-5s\n", + "Idx", "BE", "state", "host", "phy-hctl", "vir-hctl", "devstate"); + for (argv += optind, argc -= optind; argc > 0; --argc, ++argv) { + if (libxl_domain_qualifier_to_domid(ctx, *argv, &domid) < 0) { + fprintf(stderr, "%s is an invalid domain identifier\n", *argv); + continue; + } + if (!(vscsi_hosts = libxl_device_vscsi_list(ctx, domid, &num_hosts))) { + continue; + } + for (h = 0; h < num_hosts; ++h) { + for (d = 0; d < vscsi_hosts[h].num_vscsi_devs; d++) { + if (!libxl_device_vscsi_getinfo(ctx, domid, &vscsi_hosts[h], + &vscsi_hosts[h].vscsi_devs[d], + &vscsiinfo)) { + char pdev[64], vdev[64]; + switch (vscsiinfo.pdev.type) { + case LIBXL_VSCSI_PDEV_TYPE_HCTL: + snprintf(pdev, sizeof(pdev), "%u:%u:%u:%u", + vscsiinfo.pdev.u.hctl.m.hst, + vscsiinfo.pdev.u.hctl.m.chn, + vscsiinfo.pdev.u.hctl.m.tgt, + vscsiinfo.pdev.u.hctl.m.lun); + break; + case LIBXL_VSCSI_PDEV_TYPE_WWN: + snprintf(pdev, sizeof(pdev), "%s", + vscsiinfo.pdev.u.wwn.m); + break; + default: + pdev[0] = '\0'; + break; + } + snprintf(vdev, sizeof(vdev), "%u:%u:%u:%u", + vscsiinfo.vdev.hst, + vscsiinfo.vdev.chn, + vscsiinfo.vdev.tgt, + vscsiinfo.vdev.lun); + /* Idx BE state Sta */ + printf("%-3d %-3d %-5d %-5d %-10s %-10s %d\n", + vscsiinfo.devid, + vscsiinfo.backend_id, + vscsiinfo.vscsi_host_state, + vscsiinfo.backend_id, + pdev, vdev, + vscsiinfo.vscsi_dev_state); + + } + libxl_vscsiinfo_dispose(&vscsiinfo); + } + libxl_device_vscsi_dispose(&vscsi_hosts[h]); + } + free(vscsi_hosts); + + } + + return 0; +} + +int main_vscsidetach(int argc, char **argv) +{ + int opt; + char *dom = argv[1], *str = argv[2]; + uint32_t domid; + XLU_Config *config = NULL; + int found = 0; + + SWITCH_FOREACH_OPT(opt, "", NULL, "scsi-detach", 1) { + /* No options */ + } + + if (argc < 3) { + help("scsi-detach"); + return 1; + } + + if (libxl_domain_qualifier_to_domid(ctx, dom, &domid) < 0) { + fprintf(stderr, "%s is an invalid domain identifier\n", dom); + return 1; + } + + config = xlu_cfg_init(stderr, "command line"); + if (!config) { + fprintf(stderr, "Failed to allocate for configuration\n"); + goto out; + } + + found = xlu_vscsi_detach(config, ctx, domid, str); + if (!found) + fprintf(stderr, "%s(%u) vdev %s does not exist in domain %s\n", __func__, __LINE__, str, dom); + +out: + if (config) + xlu_cfg_destroy(config); + return !found; +} + int main_vtpmattach(int argc, char **argv) { int opt; diff --git a/tools/libxl/xl_cmdtable.c b/tools/libxl/xl_cmdtable.c index 7f4759b..67fcce0 100644 --- a/tools/libxl/xl_cmdtable.c +++ b/tools/libxl/xl_cmdtable.c @@ -367,6 +367,21 @@ struct cmd_spec cmd_table[] = { "Destroy a domain's virtual block device", "<Domain> <DevId>", }, + { "scsi-attach", + &main_vscsiattach, 1, 1, + "Attach a dom0 SCSI device to a domain.", + "<Domain> <PhysDevice> <VirtDevice>", + }, + { "scsi-list", + &main_vscsilist, 0, 0, + "List all dom0 SCSI devices currently attached to a domain.", + "<Domain(s)>", + }, + { "scsi-detach", + &main_vscsidetach, 0, 1, + "Detach a specified SCSI device from a domain.", + "<Domain> <VirtDevice>", + }, { "vtpm-attach", &main_vtpmattach, 1, 1, "Create a new virtual TPM device", _______________________________________________ Xen-devel mailing list Xen-devel@xxxxxxxxxxxxx http://lists.xen.org/xen-devel
|
Lists.xenproject.org is hosted with RackSpace, monitoring our |