[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [Xen-devel] [PATCH v3 4/4] libxl: add support for vscsi
Port pvscsi support from xend to libxl. See pvscsi.txt for details. Outstanding work is listed in the TODO section. 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> --- tools/libxl/Makefile | 1 + tools/libxl/libxl.c | 172 ++++++++++++++++ tools/libxl/libxl.h | 29 +++ tools/libxl/libxl_create.c | 1 + tools/libxl/libxl_device.c | 2 + tools/libxl/libxl_freebsd.c | 8 + tools/libxl/libxl_internal.h | 12 ++ tools/libxl/libxl_linux.c | 60 ++++++ tools/libxl/libxl_netbsd.c | 8 + tools/libxl/libxl_types.idl | 49 +++++ tools/libxl/libxl_types_internal.idl | 1 + tools/libxl/libxl_vscsi.c | 375 +++++++++++++++++++++++++++++++++++ tools/libxl/xl.h | 3 + tools/libxl/xl_cmdimpl.c | 249 ++++++++++++++++++++++- tools/libxl/xl_cmdtable.c | 15 ++ 15 files changed, 984 insertions(+), 1 deletion(-) diff --git a/tools/libxl/Makefile b/tools/libxl/Makefile index 7329521..9622d66 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 \ diff --git a/tools/libxl/libxl.c b/tools/libxl/libxl.c index 088786e..73504b9 100644 --- a/tools/libxl/libxl.c +++ b/tools/libxl/libxl.c @@ -1967,6 +1967,166 @@ static int libxl__resolve_domid(libxl__gc *gc, const char *name, } /******************************************************************************/ +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_ctx *ctx = libxl__gc_owner(gc); + flexarray_t *front; + flexarray_t *back; + libxl__device *device; + char *be_path; + unsigned int be_dirs = 0, rc, i; + + if (vscsi->devid == -1) { + rc = ERROR_FAIL; + goto out; + } + + /* 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); + + GCNEW(device); + rc = libxl__device_from_vscsi(gc, domid, vscsi, device); + if ( rc != 0 ) goto out; + + /* Check if backend device path is already present */ + be_path = libxl__device_backend_path(gc, device); + if (!libxl__xs_directory(gc, XBT_NULL, be_path, &be_dirs) || !be_dirs) { + /* backend does not exist, create a new one */ + flexarray_append_pair(back, "frontend-id", GCSPRINTF("%d", domid)); + flexarray_append_pair(back, "online", "1"); + flexarray_append_pair(back, "state", "1"); + flexarray_append_pair(back, "feature-host", GCSPRINTF("%d", !!vscsi->feature_host)); + + flexarray_append_pair(front, "backend-id", GCSPRINTF("%d", vscsi->backend_domid)); + flexarray_append_pair(front, "state", "1"); + } + + for (i = 0; i < vscsi->num_vscsi_devs; i++) { + libxl_vscsi_dev *v = vscsi->vscsi_devs + i; + /* Trigger removal, otherwise create new device */ + if (be_dirs) { + unsigned int nb = 0; + /* Preserve existing device */ + if (libxl__xs_directory(gc, XBT_NULL, GCSPRINTF("%s/vscsi-devs/dev-%u", be_path, v->vscsi_dev_id), &nb) && nb) { + /* Trigger device removal by forwarding state to XenbusStateClosing */ + if (v->remove) + flexarray_append_pair(back, GCSPRINTF("vscsi-devs/dev-%u/state", v->vscsi_dev_id), "5"); + continue; + } + } + flexarray_append_pair(back, GCSPRINTF("vscsi-devs/dev-%u/p-devname", v->vscsi_dev_id), v->p_devname); + switch (v->pdev_type) { + case LIBXL_VSCSI_PDEV_TYPE_WWN: + flexarray_append_pair(back, + GCSPRINTF("vscsi-devs/dev-%u/p-dev", v->vscsi_dev_id), + v->p_devname); + break; + case LIBXL_VSCSI_PDEV_TYPE_DEV: + case LIBXL_VSCSI_PDEV_TYPE_HCTL: + flexarray_append_pair(back, + GCSPRINTF("vscsi-devs/dev-%u/p-dev", v->vscsi_dev_id), + GCSPRINTF("%u:%u:%u:%u", v->pdev.hst, v->pdev.chn, v->pdev.tgt, v->pdev.lun)); + break; + case LIBXL_VSCSI_PDEV_TYPE_INVALID: + default: + rc = ERROR_FAIL; + goto out; + } + flexarray_append_pair(back, GCSPRINTF("vscsi-devs/dev-%u/v-dev", v->vscsi_dev_id), + GCSPRINTF("%u:%u:%u:%u", v->vdev.hst, v->vdev.chn, v->vdev.tgt, v->vdev.lun)); + flexarray_append_pair(back, GCSPRINTF("vscsi-devs/dev-%u/state", v->vscsi_dev_id), "1"); + } + + aodev->dev = device; + /* Either create new host or reconfigure existing host */ + if (be_dirs == 0) { + libxl__device_generic_add(gc, XBT_NULL, device, + libxl__xs_kvs_of_flexarray(gc, back, back->count), + libxl__xs_kvs_of_flexarray(gc, front, front->count), + NULL); + aodev->action = LIBXL__DEVICE_ACTION_ADD; + libxl__wait_device_connection(egc, aodev); + rc = 0; + /* Done with new host */ + goto out; + } + + /* Only new devices, write them and do vscsi host reconfiguration */ + xs_transaction_t t; +retry_transaction: + t = xs_transaction_start(ctx->xsh); + libxl__xs_writev(gc, t, be_path, + libxl__xs_kvs_of_flexarray(gc, back, back->count)); + xs_write(ctx->xsh, t, GCSPRINTF("%s/state", be_path), "7", 2); + if (!xs_transaction_end(ctx->xsh, t, 0)) { + if (errno == EAGAIN) + goto retry_transaction; + LOGE(ERROR, "xs transaction failed"); + rc = ERROR_FAIL; + goto out; + } + libxl__wait_for_backend(gc, be_path, "4"); + +retry_transaction2: + t = xs_transaction_start(ctx->xsh); + for (i = 0; i < vscsi->num_vscsi_devs; i++) { + libxl_vscsi_dev *v = vscsi->vscsi_devs + i; + if (v->remove) { + char *path, *val; + path = GCSPRINTF("%s/vscsi-devs/dev-%u/state", be_path, v->vscsi_dev_id); + val = libxl__xs_read(gc, t, path); + if (val && strcmp(val, "6") == 0) { + path = GCSPRINTF("%s/vscsi-devs/dev-%u/state", be_path, v->vscsi_dev_id); + xs_rm(ctx->xsh, t, path); + path = GCSPRINTF("%s/vscsi-devs/dev-%u/p-devname", be_path, v->vscsi_dev_id); + xs_rm(ctx->xsh, t, path); + path = GCSPRINTF("%s/vscsi-devs/dev-%u/p-dev", be_path, v->vscsi_dev_id); + xs_rm(ctx->xsh, t, path); + path = GCSPRINTF("%s/vscsi-devs/dev-%u/v-dev", be_path, v->vscsi_dev_id); + xs_rm(ctx->xsh, t, path); + path = GCSPRINTF("%s/vscsi-devs/dev-%u", be_path, v->vscsi_dev_id); + xs_rm(ctx->xsh, t, path); + } else { + LOGE(ERROR, "%s: %s has %s, expected 6", __func__, path, val); + } + } + } + + if (!xs_transaction_end(ctx->xsh, t, 0)) { + if (errno == EAGAIN) + goto retry_transaction2; + LOGE(ERROR, "xs transaction failed"); + rc = ERROR_FAIL; + goto out; + } + /* As we are not adding new device, skip waiting for it */ + libxl__ao_complete(egc, aodev->ao, 0); + + rc = 0; +out: + aodev->rc = rc; + if(rc) aodev->callback(egc, aodev); + return; +} + int libxl__device_vtpm_setdefault(libxl__gc *gc, libxl_device_vtpm *vtpm) { int rc; @@ -4111,6 +4271,8 @@ out: * libxl_device_disk_destroy * libxl_device_nic_remove * libxl_device_nic_destroy + * libxl_device_vscsi_remove + * libxl_device_vscsi_destroy * libxl_device_vtpm_remove * libxl_device_vtpm_destroy * libxl_device_vkb_remove @@ -4155,6 +4317,10 @@ DEFINE_DEVICE_REMOVE(disk, destroy, 1) DEFINE_DEVICE_REMOVE(nic, remove, 0) DEFINE_DEVICE_REMOVE(nic, destroy, 1) +/* vtpm */ +DEFINE_DEVICE_REMOVE(vscsi, remove, 0) +DEFINE_DEVICE_REMOVE(vscsi, destroy, 1) + /* vkb */ DEFINE_DEVICE_REMOVE(vkb, remove, 0) DEFINE_DEVICE_REMOVE(vkb, destroy, 1) @@ -4209,6 +4375,9 @@ DEFINE_DEVICE_ADD(disk) /* nic */ DEFINE_DEVICE_ADD(nic) +/* vscsi */ +DEFINE_DEVICE_ADD(vscsi) + /* vtpm */ DEFINE_DEVICE_ADD(vtpm) @@ -6654,6 +6823,9 @@ int libxl_retrieve_domain_configuration(libxl_ctx *ctx, uint32_t domid, MERGE(nic, nics, COMPARE_DEVID, {}); + /* FIXME */ + MERGE(vscsi, vscsis, COMPARE_DEVID, {}); + MERGE(vtpm, vtpms, COMPARE_DEVID, {}); MERGE(pci, pcidevs, COMPARE_PCI, {}); diff --git a/tools/libxl/libxl.h b/tools/libxl/libxl.h index 6bbc52d..1ad52e3 100644 --- a/tools/libxl/libxl.h +++ b/tools/libxl/libxl.h @@ -1224,6 +1224,35 @@ 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); +void libxl_device_vscsi_append_dev(libxl_ctx *ctx, libxl_device_vscsi *hst, + libxl_vscsi_dev *dev); +int libxl_device_vscsi_get_host(libxl_ctx *ctx, + uint32_t domid, + const char *cfg, + libxl_device_vscsi **vscsi_host); +int libxl_device_vscsi_parse(libxl_ctx *ctx, const char *cfg, + libxl_device_vscsi *vscsi_host, + libxl_vscsi_dev *vscsi_dev); + /* 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 98687bd..5cacc30 100644 --- a/tools/libxl/libxl_create.c +++ b/tools/libxl/libxl_create.c @@ -1136,6 +1136,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 0f50d04..035a4b6 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_freebsd.c b/tools/libxl/libxl_freebsd.c index e8b88b3..71bfae6 100644 --- a/tools/libxl/libxl_freebsd.c +++ b/tools/libxl/libxl_freebsd.c @@ -131,3 +131,11 @@ libxl_device_model_version libxl__default_device_model(libxl__gc *gc) { return LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN; } + +int libxl_device_vscsi_parse_pdev(libxl__gc *gc, char *pdev, unsigned int *hst, + unsigned int *chn, unsigned int *tgt, + unsigned int *lun) +{ + + return ERROR_NOPARAVIRT; +} diff --git a/tools/libxl/libxl_internal.h b/tools/libxl/libxl_internal.h index 934465a..76f6056 100644 --- a/tools/libxl/libxl_internal.h +++ b/tools/libxl/libxl_internal.h @@ -1794,6 +1794,10 @@ _hidden libxl__json_object *libxl__json_parse(libxl__gc *gc_opt, const char *s); _hidden int libxl__device_model_version_running(libxl__gc *gc, uint32_t domid); /* Return the system-wide default device model */ _hidden libxl_device_model_version libxl__default_device_model(libxl__gc *gc); + /* Convert h:c:t:l string to int */ +_hidden int libxl_device_vscsi_parse_hctl(libxl__gc *gc, char *str, libxl_vscsi_hctl *hctl); + /* Convert /dev/scsi to h:c:t:l */ +_hidden int libxl_device_vscsi_parse_pdev(libxl__gc *gc, char *pdev, libxl_vscsi_hctl *hctl); /* Check how executes hotplug script currently */ int libxl__hotplug_settings(libxl__gc *gc, xs_transaction_t t); @@ -2386,6 +2390,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); @@ -3005,6 +3013,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); diff --git a/tools/libxl/libxl_linux.c b/tools/libxl/libxl_linux.c index b51930c..305948e 100644 --- a/tools/libxl/libxl_linux.c +++ b/tools/libxl/libxl_linux.c @@ -279,3 +279,63 @@ libxl_device_model_version libxl__default_device_model(libxl__gc *gc) { return LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN; } + +int libxl_device_vscsi_parse_pdev(libxl__gc *gc, char *pdev, libxl_vscsi_hctl *hctl) +{ + struct stat dentry; + char *sysfs; + 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(ERROR, "vscsi: %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(ERROR, "vscsi: %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 */ + sysfs = GCSPRINTF("/sys/dev/%s/%u:%u/device/scsi_device", type, + major(dentry.st_rdev), minor(dentry.st_rdev)); + + dirp = opendir(sysfs); + if (!dirp) { + LOG(ERROR, "vscsi: %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 (libxl_device_vscsi_parse_hctl(gc, de->d_name, hctl)) + continue; + + found = 1; + break; + } + closedir(dirp); + + if (!found) { + LOG(ERROR, "vscsi: %s, no h:c:t:l link in sysfs", pdev); + rc = ERROR_INVAL; + goto out; + } + + rc = 0; +out: + return rc; +} diff --git a/tools/libxl/libxl_netbsd.c b/tools/libxl/libxl_netbsd.c index 898e160..b8972f0 100644 --- a/tools/libxl/libxl_netbsd.c +++ b/tools/libxl/libxl_netbsd.c @@ -95,3 +95,11 @@ libxl_device_model_version libxl__default_device_model(libxl__gc *gc) { return LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN_TRADITIONAL; } + +int libxl_device_vscsi_parse_pdev(libxl__gc *gc, char *pdev, unsigned int *hst, + unsigned int *chn, unsigned int *tgt, + unsigned int *lun) +{ + + return ERROR_NOPARAVIRT; +} diff --git a/tools/libxl/libxl_types.idl b/tools/libxl/libxl_types.idl index 47af340..e56f231 100644 --- a/tools/libxl/libxl_types.idl +++ b/tools/libxl/libxl_types.idl @@ -542,6 +542,37 @@ libxl_device_channel = Struct("device_channel", [ ])), ]) +libxl_vscsi_pdev_type = Enumeration("vscsi_pdev_type", [ + (0, "INVALID"), + (1, "DEV"), + (2, "WWN"), + (3, "HCTL"), + ], init_val = "LIBXL_VSCSI_PDEV_TYPE_INVALID") + +libxl_vscsi_hctl = Struct("vscsi_hctl", [ + ("hst", uint32), + ("chn", uint32), + ("tgt", uint32), + ("lun", uint32), + ]) + +libxl_vscsi_dev = Struct("vscsi_dev", [ + ("vscsi_dev_id", libxl_devid), + ("remove", bool), + ("p_devname", string), + ("pdev_type", libxl_vscsi_pdev_type), + ("pdev", libxl_vscsi_hctl), + ("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")), + ("feature_host", bool), + ]) + libxl_domain_config = Struct("domain_config", [ ("c_info", libxl_domain_create_info), ("b_info", libxl_domain_build_info), @@ -551,6 +582,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 @@ -585,6 +617,23 @@ 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), + ("p_devname", string), + ("pdev", libxl_vscsi_hctl), + ("vdev", libxl_vscsi_hctl), + ("vscsi_dev_id", libxl_devid), + ("feature_host", 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..ab4cb5f --- /dev/null +++ b/tools/libxl/libxl_vscsi.c @@ -0,0 +1,375 @@ +#include "libxl_osdeps.h" /* must come before any other headers */ +#include "libxl_internal.h" + +int libxl_device_vscsi_parse_hctl(libxl__gc *gc, 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 *vscsi_trim_string(char *s) +{ + unsigned int len; + + while (isspace(*s)) + s++; + len = strlen(s); + while (len-- > 1 && isspace(s[len])) + s[len] = '\0'; + return s; +} + +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; +} + +int libxl_device_vscsi_parse(libxl_ctx *ctx, const char *cfg, + libxl_device_vscsi *new_host, + libxl_vscsi_dev *new_dev) +{ + GC_INIT(ctx); + int rc; + unsigned int lun; + char wwn[16 + 1], *buf, *pdev, *vdev, *fhost; + + buf = libxl__strdup(gc, cfg); + + pdev = strtok(buf, ","); + vdev = strtok(NULL, ","); + fhost = strtok(NULL, ","); + if (!(pdev && vdev)) { + LOG(ERROR, "invalid vscsi= devspec: '%s'\n", cfg); + rc = ERROR_INVAL; + goto out; + } + + pdev = vscsi_trim_string(pdev); + vdev = vscsi_trim_string(vdev); + + new_dev->pdev_type = LIBXL_VSCSI_PDEV_TYPE_INVALID; + if (strncmp(pdev, "/dev/", 5) == 0) { + if (libxl_device_vscsi_parse_pdev(gc, pdev, &new_dev->pdev) == 0) + new_dev->pdev_type = LIBXL_VSCSI_PDEV_TYPE_DEV; + } else if (strncmp(pdev, "naa.", 4) == 0) { + memset(wwn, 0, sizeof(wwn)); + if (sscanf(pdev, "naa.%16c:%u", wwn, &lun) == 2 && vscsi_wwn_valid(wwn)) + new_dev->pdev_type = LIBXL_VSCSI_PDEV_TYPE_WWN; + } else if (libxl_device_vscsi_parse_hctl(gc, pdev, &new_dev->pdev) == 0) { + new_dev->pdev_type = LIBXL_VSCSI_PDEV_TYPE_HCTL; + } + + switch (new_dev->pdev_type) { + case LIBXL_VSCSI_PDEV_TYPE_WWN: + new_dev->pdev.lun = lun; + /* Fall through. */ + case LIBXL_VSCSI_PDEV_TYPE_DEV: + case LIBXL_VSCSI_PDEV_TYPE_HCTL: + new_dev->p_devname = libxl__strdup(NOGC, pdev); + break; + case LIBXL_VSCSI_PDEV_TYPE_INVALID: + LOG(ERROR, "vscsi: invalid pdev '%s'", pdev); + rc = ERROR_INVAL; + goto out; + } + + + if (libxl_device_vscsi_parse_hctl(gc, vdev, &new_dev->vdev)) { + LOG(ERROR, "vscsi: 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 = vscsi_trim_string(fhost); + new_host->feature_host = strcmp(fhost, "feature-host") == 0; + if (!new_host->feature_host) { + LOG(ERROR, "vscsi: invalid option '%s', expecting %s", fhost, "feature-host"); + rc = ERROR_INVAL; + goto out; + } + } + rc = 0; + +out: + GC_FREE; + return rc; +} + +void libxl_device_vscsi_append_dev(libxl_ctx *ctx, libxl_device_vscsi *hst, + libxl_vscsi_dev *dev) +{ + GC_INIT(ctx); + hst->vscsi_devs = libxl__realloc(NOGC, hst->vscsi_devs, + sizeof(*dev) * (hst->num_vscsi_devs + 1)); + libxl_vscsi_dev_init(hst->vscsi_devs + hst->num_vscsi_devs); + dev->vscsi_dev_id = hst->num_vscsi_devs; + libxl_vscsi_dev_copy(ctx, hst->vscsi_devs + hst->num_vscsi_devs, dev); + hst->num_vscsi_devs++; + GC_FREE; +} + +int libxl_device_vscsi_get_host(libxl_ctx *ctx, uint32_t domid, const char *cfg, libxl_device_vscsi **vscsi_host) +{ + GC_INIT(ctx); + libxl_vscsi_dev *new_dev = NULL; + libxl_device_vscsi *new_host, *vscsi_hosts = NULL, *tmp; + int rc, found_host = -1, i, j; + int num_hosts; + + GCNEW(new_host); + libxl_device_vscsi_init(new_host); + + GCNEW(new_dev); + libxl_vscsi_dev_init(new_dev); + + if (libxl_device_vscsi_parse(ctx, cfg, new_host, new_dev)) { + rc = ERROR_INVAL; + goto out; + } + + /* FIXME: foreach domain, because pdev is not multiplexed by backend */ + /* FIXME: other device types do not have the multiplexing issue */ + /* FIXME: pci can solve it by unbinding the native driver */ + + /* 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) { + for (j = 0; j < vscsi_hosts[i].num_vscsi_devs; j++) { + if (vscsi_hosts[i].vscsi_devs[j].pdev.hst == new_dev->pdev.hst && + vscsi_hosts[i].vscsi_devs[j].pdev.chn == new_dev->pdev.chn && + vscsi_hosts[i].vscsi_devs[j].pdev.tgt == new_dev->pdev.tgt && + vscsi_hosts[i].vscsi_devs[j].pdev.lun == new_dev->pdev.lun) { + LOG(ERROR, "Host device '%u:%u:%u:%u' is already in use" + " by guest vscsi specification '%u:%u:%u:%u'.\n", + new_dev->pdev.hst, new_dev->pdev.chn, new_dev->pdev.tgt, new_dev->pdev.lun, + new_dev->vdev.hst, new_dev->vdev.chn, new_dev->vdev.tgt, new_dev->vdev.lun); + rc = ERROR_INVAL; + goto out; + } + } + 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->devid = 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].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) { + fprintf(stderr, "Target vscsi specification '%u:%u:%u:%u' is already taken\n", + new_dev->vdev.hst, new_dev->vdev.chn, new_dev->vdev.tgt, new_dev->vdev.lun); + rc = ERROR_INVAL; + goto out; + } + } + } + + /* The caller gets a copy along with appended new_dev */ + *vscsi_host = libxl__malloc(NOGC, sizeof(*new_host)); + libxl_device_vscsi_init(*vscsi_host); + libxl_device_vscsi_copy(ctx, *vscsi_host, tmp); + libxl_device_vscsi_append_dev(ctx, *vscsi_host, new_dev); + + rc = 0; + +out: + if (vscsi_hosts) { + for (i = 0; i < num_hosts; ++i){ + libxl_device_vscsi_dispose(&vscsi_hosts[i]); + } + free(vscsi_hosts); + } + libxl_device_vscsi_dispose(new_host); + libxl_vscsi_dev_dispose(new_dev); + GC_FREE; + return rc; +} + +libxl_device_vscsi *libxl_device_vscsi_list(libxl_ctx *ctx, uint32_t domid, int *num) +{ + GC_INIT(ctx); + libxl_vscsi_dev *v_dev; + libxl_device_vscsi *v_hst, *vscsi_hosts = NULL; + char *fe_path, *tmp, *c, *p, *v; + char **dir, **devs_dir; + const char *devs_path, *be_path; + int r; + bool parsed_ok; + unsigned int ndirs = 0, ndevs_dirs = 0, i; + unsigned int vscsi_dev_id; + + fe_path = libxl__sprintf(gc, "%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) { + devs_path = libxl__sprintf(gc, "%s/vscsi-devs", be_path); + devs_dir = libxl__xs_directory(gc, XBT_NULL, devs_path, &ndevs_dirs); + } else { + devs_dir = NULL; + } + + if (devs_dir && ndevs_dirs) { + v_hst->vscsi_devs = libxl__malloc(NOGC, ndevs_dirs * sizeof(*v_dev)); + v_hst->num_vscsi_devs = ndevs_dirs; + /* Fill each device connected to the host */ + for (i = 0; i < ndevs_dirs; i++, devs_dir++) { + v_dev = &v_hst->vscsi_devs[i]; + libxl_vscsi_dev_init(v_dev); + parsed_ok = false; + r = sscanf(*devs_dir, "dev-%u", &vscsi_dev_id); + if (r == 1) { + c = libxl__xs_read(gc, XBT_NULL, + GCSPRINTF("%s/vscsi-devs/dev-%u/p-devname", + be_path, vscsi_dev_id)); + p = libxl__xs_read(gc, XBT_NULL, + GCSPRINTF("%s/vscsi-devs/dev-%u/p-dev", + be_path, vscsi_dev_id)); + v = libxl__xs_read(gc, XBT_NULL, + GCSPRINTF("%s/vscsi-devs/dev-%u/v-dev", + be_path, vscsi_dev_id)); + if (c && p && v) { + v_dev->p_devname = libxl__strdup(NOGC, c); + if (libxl_device_vscsi_parse_hctl(gc, p, &v_dev->pdev) == 0 && + libxl_device_vscsi_parse_hctl(gc, v, &v_dev->vdev) == 0) + parsed_ok = true; + v_dev->vscsi_dev_id = vscsi_dev_id; + v_hst->v_hst = v_dev->vdev.hst; + } + } + + if (!parsed_ok) { + /* FIXME what if xenstore is broken? */ + LIBXL__LOG(ctx, LIBXL__LOG_ERROR, "%s/scsi-devs/%s failed to parse", be_path, *devs_dir); + continue; + } + } + } + } + +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_hctl_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/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 5c40e84..da5d95b 100644 --- a/tools/libxl/xl_cmdimpl.c +++ b/tools/libxl/xl_cmdimpl.c @@ -985,7 +985,7 @@ static void parse_config_data(const char *config_source, const char *buf; long l; 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; @@ -1488,6 +1488,59 @@ 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) { + 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); + + if (libxl_device_vscsi_parse(ctx, buf, &v_hst, &v_dev)) + exit (-1); + + if (d_config->num_vscsis) { + for (i = 0; i < d_config->num_vscsis; i++) { + if (d_config->vscsis[i].v_hst == v_hst.v_hst) { + tmp = &d_config->vscsis[i]; + libxl_device_vscsi_append_dev(ctx, tmp, &v_dev); + hst_found = true; + break; + } + } + } + + if (!hst_found || !d_config->num_vscsis) { + d_config->vscsis = realloc(d_config->vscsis, sizeof(v_hst) * (d_config->num_vscsis + 1)); + tmp = &d_config->vscsis[d_config->num_vscsis]; + libxl_device_vscsi_init(tmp); + + v_hst.devid = d_config->num_vscsis; + libxl_device_vscsi_copy(ctx, tmp, &v_hst); + + libxl_device_vscsi_append_dev(ctx, tmp, &v_dev); + + d_config->num_vscsis++; + } + + libxl_vscsi_dev_dispose(&v_dev); + libxl_device_vscsi_dispose(&v_hst); + num_vscsi_items++; + } + } + if (!xlu_cfg_get_list(config, "vtpm", &vtpms, 0, 0)) { d_config->num_vtpms = 0; d_config->vtpms = NULL; @@ -6439,6 +6492,200 @@ int main_blockdetach(int argc, char **argv) return rc; } +int main_vscsiattach(int argc, char **argv) +{ + uint32_t domid; + int opt, rc; + libxl_device_vscsi *vscsi_host = NULL; + char *cfg = 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(&cfg, "%s,%s%s", argv[2], argv[3], feat_buf ?: "") < 0) { + perror("asprintf"); + rc = 1; + goto out;; + } + + /* Parse config string and store result */ + rc = libxl_device_vscsi_get_host(ctx, domid, cfg, &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 (vscsi_host) + libxl_device_vscsi_dispose(vscsi_host); + free(vscsi_host); + free(cfg); + 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]; + snprintf(pdev, sizeof(pdev), "%u:%u:%u:%u", + vscsiinfo.pdev.hst, vscsiinfo.pdev.chn, vscsiinfo.pdev.tgt, vscsiinfo.pdev.lun); + 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; + libxl_vscsi_dev v_dev = { }, *vd; + libxl_device_vscsi v_hst = { }, *vh; + libxl_device_vscsi *vscsi_hosts; + char *tmp = NULL, *dom = argv[1], *vdev = argv[2]; + uint32_t domid; + int num_hosts, h, d, 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; + } + + vscsi_hosts = libxl_device_vscsi_list(ctx, domid, &num_hosts); + if (!vscsi_hosts) + return 0; + + /* Create a dummy cfg */ + if (asprintf(&tmp, "0:0:0:0,%s", vdev) < 0) { + perror("asprintf"); + goto done; + } + + libxl_vscsi_dev_init(&v_dev); + libxl_device_vscsi_init(&v_hst); + if (libxl_device_vscsi_parse(ctx, tmp, &v_hst, &v_dev)) + goto done; + + for (h = 0; h < num_hosts; ++h) { + vh = &vscsi_hosts[h]; + for (d = 0; !found && d < vh->num_vscsi_devs; d++) { +#define CMP(member) (vd->vdev.member == v_dev.vdev.member) + vd = &vh->vscsi_devs[d]; + if (CMP(hst) && CMP(chn) && CMP(tgt) && CMP(lun)) { + if (vh->num_vscsi_devs > 1) { + vd->remove = true; + if (libxl_device_vscsi_add(ctx, domid, vh, 0)) { + fprintf(stderr, "libxl_device_vscsi_remove failed.\n"); + goto done; + } + } else { + libxl_device_vscsi_remove(ctx, domid, vh, 0); + } + found = 1; + } +#undef CMP + } + } + if (!found) + fprintf(stderr, "%s(%u) vdev %s does not exist in domain %s\n", __func__, __LINE__, vdev, dom); +done: + if (vscsi_hosts) { + for (h = 0; h < num_hosts; ++h) + libxl_device_vscsi_dispose(&vscsi_hosts[h]); + free(vscsi_hosts); + } + free(tmp); + 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 22ab63b..54b7a3a 100644 --- a/tools/libxl/xl_cmdtable.c +++ b/tools/libxl/xl_cmdtable.c @@ -365,6 +365,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 |