From c22f60e6e55f1bf300dd76d2222a93911f3b2bb2 Mon Sep 17 00:00:00 2001 From: CoprDistGit Date: Thu, 12 Oct 2023 04:00:49 +0000 Subject: automatic import of xen --- libxl.pvscsi.patch | 2538 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 2538 insertions(+) create mode 100644 libxl.pvscsi.patch (limited to 'libxl.pvscsi.patch') diff --git a/libxl.pvscsi.patch b/libxl.pvscsi.patch new file mode 100644 index 0000000..4b868ca --- /dev/null +++ b/libxl.pvscsi.patch @@ -0,0 +1,2538 @@ +Subject: [PATCH v12 1/2] libxl: add support for vscsi +Date: Wed, 13 Apr 2016 08:56:59 +0000 +Message-Id: <1460537820-15398-2-git-send-email-olaf@aepfle.de> +fate#316613 , https://fate.suse.com/316613 + +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 +Cc: Ian Jackson +Cc: Stefano Stabellini +Cc: Ian Campbell +Cc: Wei Liu +--- + docs/man/xl.cfg.pod.5 | 56 + + docs/man/xl.pod.1 | 18 + tools/libxl/Makefile | 2 + tools/libxl/libxl.c | 9 + tools/libxl/libxl.h | 42 + + tools/libxl/libxl_create.c | 41 + + tools/libxl/libxl_device.c | 2 + tools/libxl/libxl_internal.h | 8 + tools/libxl/libxl_types.idl | 53 + + tools/libxl/libxl_types_internal.idl | 1 + tools/libxl/libxl_vscsi.c | 1169 +++++++++++++++++++++++++++++++++++ + tools/libxl/libxlu_vscsi.c | 667 +++++++++++++++++++ + tools/libxl/libxlutil.h | 19 + tools/libxl/xl.h | 3 + tools/libxl/xl_cmdimpl.c | 225 ++++++ + tools/libxl/xl_cmdtable.c | 15 + 16 files changed, 2326 insertions(+), 4 deletions(-) + +Index: xen-4.13.0-testing/docs/man/xl.cfg.5.pod.in +=================================================================== +--- xen-4.13.0-testing.orig/docs/man/xl.cfg.5.pod.in ++++ xen-4.13.0-testing/docs/man/xl.cfg.5.pod.in +@@ -756,6 +756,62 @@ frontend to backend. It can be used as a + For more information about the protocol, see + https://xenbits.xenproject.org/docs/unstable/misc/pvcalls.html. + ++=item B ++ ++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. ++'options' lists additional flags which a backend may recognize. ++ ++The supported values for "pdev" and "options" depends on the backend driver used: ++ ++=over 4 ++ ++=item B ++ ++=over 4 ++ ++=item C ++ ++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. Usually this is a WWN in the form of "naa.WWN:LUN". ++ ++=item C ++ ++No options recognized. ++ ++=back ++ ++=item B ++ ++=over 4 ++ ++=item C ++ ++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 ++ ++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 + + Specifies the paravirtual framebuffer devices which should be supplied +Index: xen-4.13.0-testing/docs/man/xl.1.pod.in +=================================================================== +--- xen-4.13.0-testing.orig/docs/man/xl.1.pod.in ++++ xen-4.13.0-testing/docs/man/xl.1.pod.in +@@ -1575,6 +1575,24 @@ List virtual network interfaces for a do + + =back + ++=head2 PVSCSI DEVICES ++ ++=over 4 ++ ++=item B I I I,I<[feature-host]> ++ ++Creates a new vscsi device in the domain specified by I. ++ ++=item B I I ++ ++Removes the vscsi device from domain specified by I. ++ ++=item B I I<[domain-id] ...> ++ ++List vscsi devices for the domain specified by I. ++ ++=back ++ + =head1 PCI PASS-THROUGH + + =over 4 +Index: xen-4.13.0-testing/tools/libxl/Makefile +=================================================================== +--- xen-4.13.0-testing.orig/tools/libxl/Makefile ++++ xen-4.13.0-testing/tools/libxl/Makefile +@@ -127,6 +127,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 \ +@@ -173,6 +174,7 @@ AUTOINCS= libxlu_cfg_y.h libxlu_cfg_l.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 + +Index: xen-4.13.0-testing/tools/libxl/libxl.h +=================================================================== +--- xen-4.13.0-testing.orig/tools/libxl/libxl.h ++++ xen-4.13.0-testing/tools/libxl/libxl.h +@@ -1134,6 +1134,13 @@ void libxl_mac_copy(libxl_ctx *ctx, libx + #define LIBXL_HAVE_PCITOPOLOGY 1 + + /* ++ * LIBXL_HAVE_VSCSI ++ * ++ * If this is defined, the PV SCSI feature is supported. ++ */ ++#define LIBXL_HAVE_VSCSI 1 ++ ++/* + * LIBXL_HAVE_SOCKET_BITMAP + * + * If this is defined, then libxl_socket_bitmap_alloc and +@@ -2150,6 +2157,41 @@ int libxl_device_channel_getinfo(libxl_c + const libxl_device_channel *channel, + libxl_channelinfo *channelinfo); + ++/* Virtual SCSI */ ++int libxl_device_vscsictrl_add(libxl_ctx *ctx, uint32_t domid, ++ libxl_device_vscsictrl *vscsi, ++ const libxl_asyncop_how *ao_how) ++ LIBXL_EXTERNAL_CALLERS_ONLY; ++int libxl_device_vscsictrl_remove(libxl_ctx *ctx, uint32_t domid, ++ libxl_device_vscsictrl *vscsi, ++ const libxl_asyncop_how *ao_how) ++ LIBXL_EXTERNAL_CALLERS_ONLY; ++int libxl_device_vscsictrl_destroy(libxl_ctx *ctx, uint32_t domid, ++ libxl_device_vscsictrl *vscsi, ++ const libxl_asyncop_how *ao_how) ++ LIBXL_EXTERNAL_CALLERS_ONLY; ++ ++libxl_device_vscsictrl *libxl_device_vscsictrl_list(libxl_ctx *ctx, uint32_t domid, int *num); ++int libxl_device_vscsictrl_getinfo(libxl_ctx *ctx, uint32_t domid, ++ libxl_device_vscsictrl *vscsictrl, ++ libxl_device_vscsidev *vscsidev, ++ libxl_vscsiinfo *vscsiinfo); ++int libxl_device_vscsidev_add(libxl_ctx *ctx, uint32_t domid, ++ libxl_device_vscsidev *dev, ++ const libxl_asyncop_how *ao_how) ++ LIBXL_EXTERNAL_CALLERS_ONLY; ++/* Remove vscsidev connected to vscsictrl */ ++int libxl_device_vscsidev_remove(libxl_ctx *ctx, uint32_t domid, ++ libxl_device_vscsidev *dev, ++ const libxl_asyncop_how *ao_how) ++ LIBXL_EXTERNAL_CALLERS_ONLY; ++void libxl_device_vscsictrl_append_vscsidev(libxl_ctx *ctx, ++ libxl_device_vscsictrl *ctrl, ++ libxl_device_vscsidev *dev); ++void libxl_device_vscsictrl_remove_vscsidev(libxl_ctx *ctx, ++ libxl_device_vscsictrl *ctrl, ++ unsigned int idx); ++ + /* Virtual TPMs */ + int libxl_device_vtpm_add(libxl_ctx *ctx, uint32_t domid, libxl_device_vtpm *vtpm, + const libxl_asyncop_how *ao_how) +Index: xen-4.13.0-testing/tools/libxl/libxl_create.c +=================================================================== +--- xen-4.13.0-testing.orig/tools/libxl/libxl_create.c ++++ xen-4.13.0-testing/tools/libxl/libxl_create.c +@@ -1655,6 +1655,7 @@ const libxl__device_type *device_type_tb + &libxl__disk_devtype, + &libxl__nic_devtype, + &libxl__vtpm_devtype, ++ &libxl__vscsictrl_devtype, + &libxl__usbctrl_devtype, + &libxl__usbdev_devtype, + &libxl__pcidev_devtype, +Index: xen-4.13.0-testing/tools/libxl/libxl_internal.h +=================================================================== +--- xen-4.13.0-testing.orig/tools/libxl/libxl_internal.h ++++ xen-4.13.0-testing/tools/libxl/libxl_internal.h +@@ -3936,6 +3936,7 @@ extern const libxl__device_type libxl__v + extern const libxl__device_type libxl__disk_devtype; + extern const libxl__device_type libxl__nic_devtype; + extern const libxl__device_type libxl__vtpm_devtype; ++extern const libxl__device_type libxl__vscsictrl_devtype; + extern const libxl__device_type libxl__usbctrl_devtype; + extern const libxl__device_type libxl__usbdev_devtype; + extern const libxl__device_type libxl__pcidev_devtype; +Index: xen-4.13.0-testing/tools/libxl/libxl_types.idl +=================================================================== +--- xen-4.13.0-testing.orig/tools/libxl/libxl_types.idl ++++ xen-4.13.0-testing/tools/libxl/libxl_types.idl +@@ -929,6 +929,43 @@ libxl_device_vsnd = Struct("device_vsnd" + ("pcms", Array(libxl_vsnd_pcm, "num_vsnd_pcms")) + ]) + ++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", uint64), ++ ]) ++ ++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_device_vscsidev = Struct("device_vscsidev", [ ++ ("vscsidev_id", libxl_devid), ++ ("pdev", libxl_vscsi_pdev), ++ ("vdev", libxl_vscsi_hctl), ++ ]) ++ ++libxl_device_vscsictrl = Struct("device_vscsictrl", [ ++ ("backend_domid", libxl_domid), ++ ("devid", libxl_devid), ++ ("idx", libxl_devid), ++ ("vscsidevs", Array(libxl_device_vscsidev, "num_vscsidevs")), ++ ("scsi_raw_cmds", libxl_defbool), ++ ]) ++ + libxl_domain_config = Struct("domain_config", [ + ("c_info", libxl_domain_create_info), + ("b_info", libxl_domain_build_info), +@@ -940,6 +977,7 @@ libxl_domain_config = Struct("domain_con + ("dtdevs", Array(libxl_device_dtdev, "num_dtdevs")), + ("vfbs", Array(libxl_device_vfb, "num_vfbs")), + ("vkbs", Array(libxl_device_vkb, "num_vkbs")), ++ ("vscsictrls", Array(libxl_device_vscsictrl, "num_vscsictrls")), + ("vtpms", Array(libxl_device_vtpm, "num_vtpms")), + ("p9s", Array(libxl_device_p9, "num_p9s")), + ("pvcallsifs", Array(libxl_device_pvcallsif, "num_pvcallsifs")), +@@ -981,6 +1019,21 @@ 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), ++ ("idx", libxl_devid), ++ ("vscsidev_id", libxl_devid), ++ ("scsi_raw_cmds", bool), ++ ("vscsictrl_state", integer), ++ ("vscsidev_state", integer), ++ ], dir=DIR_OUT) ++ + libxl_vtpminfo = Struct("vtpminfo", [ + ("backend", string), + ("backend_id", uint32), +Index: xen-4.13.0-testing/tools/libxl/libxl_types_internal.idl +=================================================================== +--- xen-4.13.0-testing.orig/tools/libxl/libxl_types_internal.idl ++++ xen-4.13.0-testing/tools/libxl/libxl_types_internal.idl +@@ -32,6 +32,7 @@ libxl__device_kind = Enumeration("device + (14, "PVCALLS"), + (15, "VSND"), + (16, "VINPUT"), ++ (17, "VSCSI"), + ]) + + libxl__console_backend = Enumeration("console_backend", [ +Index: xen-4.13.0-testing/tools/libxl/libxl_vscsi.c +=================================================================== +--- /dev/null ++++ xen-4.13.0-testing/tools/libxl/libxl_vscsi.c +@@ -0,0 +1,1185 @@ ++/* ++ * Copyright (C) 2016 SUSE Linux GmbH ++ * Author Olaf Hering ++ * ++ * 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" ++ ++typedef struct vscsidev_rm { ++ libxl_device_vscsictrl *ctrl; ++ char *be_path; ++ int dev_wait; ++ libxl__device dev; ++} vscsidev_rm_t; ++ ++typedef void (*vscsictrl_add)(libxl__egc *egc, ++ libxl__ao_device *aodev, ++ libxl_device_vscsictrl *vscsictrl, ++ libxl_domain_config *d_config); ++ ++#define LIBXL_CTRL_INDEX "libxl_ctrl_index" ++ ++#define XLU_WWN_LEN 16 ++ ++static int vscsi_parse_hctl(char *str, libxl_vscsi_hctl *hctl) ++{ ++ unsigned int hst, chn, tgt; ++ unsigned long long lun; ++ ++ if (sscanf(str, "%u:%u:%u:%llu", &hst, &chn, &tgt, &lun) != 4) ++ return ERROR_INVAL; ++ ++ hctl->hst = hst; ++ hctl->chn = chn; ++ hctl->tgt = tgt; ++ hctl->lun = lun; ++ return 0; ++} ++ ++/* Translate p-dev back into pdev.type */ ++static bool vscsi_parse_pdev(libxl__gc *gc, libxl_device_vscsidev *dev, ++ char *c, char *p, char *v) ++{ ++ libxl_vscsi_hctl hctl; ++ unsigned long long lun; ++ char wwn[XLU_WWN_LEN + 1]; ++ bool parsed_ok = false; ++ ++ libxl_vscsi_hctl_init(&hctl); ++ ++ 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.%16[0-9a-fA-F]:%llu", wwn, &lun) == 2) { ++ libxl_vscsi_pdev_init_type(&dev->pdev, LIBXL_VSCSI_PDEV_TYPE_WWN); ++ 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(&dev->pdev, LIBXL_VSCSI_PDEV_TYPE_HCTL); ++ libxl_vscsi_hctl_copy(CTX, &dev->pdev.u.hctl.m, &hctl); ++ parsed_ok = true; ++ } ++ ++ if (parsed_ok && vscsi_parse_hctl(v, &dev->vdev) != 0) ++ parsed_ok = false; ++ ++ libxl_vscsi_hctl_dispose(&hctl); ++ ++ return parsed_ok; ++} ++ ++static bool vscsi_fill_dev(libxl__gc *gc, ++ xs_transaction_t t, ++ const char *devs_path, ++ const char *dev_dir, ++ libxl_device_vscsidev *dev) ++{ ++ char *path, *c, *p, *v, *s; ++ unsigned int devid; ++ int r; ++ ++ r = sscanf(dev_dir, "dev-%u", &devid); ++ if (r != 1) { ++ LOG(ERROR, "expected dev-N, got '%s'", dev_dir); ++ return false; ++ } ++ dev->vscsidev_id = devid; ++ ++ path = GCSPRINTF("%s/%s", devs_path, dev_dir); ++ c = libxl__xs_read(gc, t, GCSPRINTF("%s/p-devname", path)); ++ p = libxl__xs_read(gc, t, GCSPRINTF("%s/p-dev", path)); ++ v = libxl__xs_read(gc, t, GCSPRINTF("%s/v-dev", path)); ++ s = libxl__xs_read(gc, t, GCSPRINTF("%s/state", path)); ++ LOG(DEBUG, "%s/state is %s", path, s); ++ if (!(c && p && v && s)) { ++ LOG(ERROR, "p-devname '%s' p-dev '%s' v-dev '%s'", c, p, v); ++ return false; ++ } ++ ++ if (!vscsi_parse_pdev(gc, dev, c, p, v)) { ++ LOG(ERROR, "failed to parse %s: %s %s %s %s", path, c, p, v, s); ++ return false; ++ } ++ ++ return true; ++} ++ ++static bool vscsi_fill_ctrl(libxl__gc *gc, ++ uint32_t tgt_domid, ++ xs_transaction_t t, ++ const char *fe_path, ++ const char *dir, ++ libxl_device_vscsictrl *ctrl) ++{ ++ libxl_device_vscsidev dev; ++ char *tmp, *devs_path; ++ const char *be_path; ++ char **dev_dirs; ++ unsigned int ndev_dirs, dev_dir; ++ uint32_t be_domid, fe_domid; ++ char be_type[16]; ++ int r; ++ bool ok; ++ ++ ctrl->devid = atoi(dir); ++ ++ tmp = GCSPRINTF("%s/%s/backend", fe_path, dir); ++ r = libxl__xs_read_checked(gc, t, tmp, &be_path); ++ if (r || !be_path) ++ goto out; ++ ++ r = sscanf(be_path, "/local/domain/%u/backend/%15[^/]/%u", ++ &be_domid, be_type, &fe_domid); ++ if (r != 3 || fe_domid != tgt_domid) ++ goto out; ++ ctrl->backend_domid = be_domid; ++ ++ tmp = libxl__xs_read(gc, t, GCSPRINTF("%s/" LIBXL_CTRL_INDEX, be_path)); ++ if (!tmp) ++ goto out; ++ ctrl->idx = atoi(tmp); ++ ++ tmp = libxl__xs_read(gc, t, GCSPRINTF("%s/feature-host", be_path)); ++ if (!tmp) ++ goto out; ++ ok = atoi(tmp) != 0; ++ libxl_defbool_set(&ctrl->scsi_raw_cmds, ok); ++ ++ ok = true; ++ devs_path = GCSPRINTF("%s/vscsi-devs", be_path); ++ dev_dirs = libxl__xs_directory(gc, t, devs_path, &ndev_dirs); ++ for (dev_dir = 0; dev_dirs && dev_dir < ndev_dirs; dev_dir++) { ++ libxl_device_vscsidev_init(&dev); ++ ok = vscsi_fill_dev(gc, t, devs_path, dev_dirs[dev_dir], &dev); ++ if (ok == true) ++ ok = ctrl->idx == dev.vdev.hst; ++ if (ok == true) ++ libxl_device_vscsictrl_append_vscsidev(CTX, ctrl, &dev); ++ libxl_device_vscsidev_dispose(&dev); ++ if (ok == false) ++ break; ++ } ++ ++ return ok; ++ ++out: ++ libxl_defbool_set(&ctrl->scsi_raw_cmds, false); ++ return false; ++} ++ ++/* return an array of vscsictrls with num elements */ ++static int vscsi_collect_ctrls(libxl__gc *gc, ++ uint32_t domid, ++ libxl_device_vscsictrl **ctrls, ++ int *num) ++{ ++ xs_transaction_t t = XBT_NULL; ++ libxl_device_vscsictrl ctrl; ++ char *fe_path; ++ char **dirs; ++ unsigned int ndirs = 0, dir; ++ int rc; ++ ++ fe_path = GCSPRINTF("%s/device/vscsi", libxl__xs_get_dompath(gc, domid)); ++ ++ for (;;) { ++ *num = 0; ++ ++ rc = libxl__xs_transaction_start(gc, &t); ++ if (rc) goto out; ++ ++ dirs = libxl__xs_directory(gc, t, fe_path, &ndirs); ++ /* Nothing to do */ ++ if (!(dirs && ndirs)) ++ break; ++ ++ /* List of ctrls to be returned to the caller */ ++ *ctrls = libxl__malloc(NOGC, ndirs * sizeof(**ctrls)); ++ ++ for (dir = 0; dir < ndirs; dir++) { ++ libxl_device_vscsictrl_init(*ctrls + dir); ++ ++ libxl_device_vscsictrl_init(&ctrl); ++ if (vscsi_fill_ctrl(gc, domid, t, fe_path, dirs[dir], &ctrl)) { ++ libxl_device_vscsictrl_copy(CTX, *ctrls + *num, &ctrl); ++ (*num)++; ++ } ++ libxl_device_vscsictrl_dispose(&ctrl); ++ } ++ ++ rc = libxl__xs_transaction_commit(gc, &t); ++ if (!rc) break; ++ ++ if (rc < 0) { ++ for (dir = 0; dir < ndirs; dir++) ++ libxl_device_vscsictrl_dispose(*ctrls + dir); ++ free(*ctrls); ++ *ctrls = NULL; ++ *num = 0; ++ goto out; ++ } ++ } ++ ++out: ++ libxl__xs_transaction_abort(gc, &t); ++ return rc; ++} ++ ++/* Simplified variant of device_addrm_aocomplete */ ++static void vscsi_aodev_complete(libxl__egc *egc, libxl__ao_device *aodev) ++{ ++ STATE_AO_GC(aodev->ao); ++ libxl__ao_complete(egc, ao, aodev->rc); ++} ++ ++static int libxl__device_from_vscsictrl(libxl__gc *gc, uint32_t domid, ++ libxl_device_vscsictrl *vscsictrl, ++ libxl__device *device) ++{ ++ device->backend_devid = vscsictrl->devid; ++ device->backend_domid = vscsictrl->backend_domid; ++ device->devid = vscsictrl->devid; ++ device->domid = domid; ++ device->backend_kind = LIBXL__DEVICE_KIND_VSCSI; ++ device->kind = LIBXL__DEVICE_KIND_VSCSI; ++ ++ return 0; ++} ++ ++static int vscsictrl_remove(libxl_ctx *ctx, ++ uint32_t domid, ++ libxl_device_vscsictrl *vscsictrl, ++ const libxl_asyncop_how *ao_how, ++ int force) ++{ ++ AO_CREATE(ctx, domid, ao_how); ++ libxl__device *device; ++ libxl__ao_device *aodev; ++ int rc; ++ ++ GCNEW(device); ++ rc = libxl__device_from_vscsictrl(gc, domid, vscsictrl, 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 = vscsi_aodev_complete; ++ aodev->force = force; ++ libxl__initiate_device_generic_remove(egc, aodev); ++ ++out: ++ if (rc) return AO_CREATE_FAIL(rc); ++ return AO_INPROGRESS; ++} ++ ++static int vscsidev_be_set_rm(libxl__gc *gc, ++ libxl_device_vscsidev *v, ++ flexarray_t *back) ++{ ++ int rc; ++ char *dir; ++ ++ dir = GCSPRINTF("vscsi-devs/dev-%u", v->vscsidev_id); ++ rc = flexarray_append_pair(back, ++ GCSPRINTF("%s/state", dir), ++ GCSPRINTF("%d", XenbusStateClosing)); ++ return rc; ++} ++ ++static int vscsictrl_reconfigure_rm(libxl__ao_device *aodev, ++ const char *state_path, ++ int *be_wait) ++ ++{ ++ STATE_AO_GC(aodev->ao); ++ vscsidev_rm_t *vscsidev_rm = CONTAINER_OF(aodev->dev, *vscsidev_rm, dev); ++ libxl_device_vscsictrl *ctrl = vscsidev_rm->ctrl; ++ const char *be_path = vscsidev_rm->be_path; ++ int rc, i, be_state; ++ char *dev_path, *state_val; ++ flexarray_t *back; ++ libxl_device_vscsidev *v; ++ xs_transaction_t t = XBT_NULL; ++ ++ /* Prealloc key+value: 1 toplevel + 1 per device */ ++ i = 2 * (1 + 1); ++ back = flexarray_make(gc, i, 1); ++ ++ 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_NOTFOUND; ++ 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; ++ vscsidev_rm->dev_wait = XenbusStateClosing; ++ break; ++ case XenbusStateConnected: ++ /* The backend can handle reconfigure */ ++ *be_wait = XenbusStateConnected; ++ vscsidev_rm->dev_wait = XenbusStateClosed; ++ flexarray_append_pair(back, "state", ++ GCSPRINTF("%d", XenbusStateReconfiguring)); ++ break; ++ } ++ ++ /* Append new vscsidev or skip existing */ ++ for (i = 0; i < ctrl->num_vscsidevs; i++) { ++ unsigned int nb = 0; ++ v = ctrl->vscsidevs + i; ++ dev_path = GCSPRINTF("%s/vscsi-devs/dev-%u", be_path, v->vscsidev_id); ++ if (!libxl__xs_directory(gc, XBT_NULL, dev_path, &nb)) { ++ /* FIXME Sanity check */ ++ LOG(DEBUG, "%s does not exist anymore", dev_path); ++ continue; ++ } ++ rc = vscsidev_be_set_rm(gc, v, back); ++ if (rc) goto out; ++ } ++ ++ libxl__xs_writev(gc, t, be_path, libxl__xs_kvs_of_flexarray(gc, back)); ++ ++ rc = libxl__xs_transaction_commit(gc, &t); ++ if (!rc) break; ++ if (rc < 0) goto out; ++ } ++ ++ rc = 0; ++ ++out: ++ libxl__xs_transaction_abort(gc, &t); ++ return rc; ++} ++ ++static void vscsictrl_remove_be_dev(libxl__gc *gc, ++ libxl_device_vscsidev *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->vscsidev_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 void vscsictrl_remove_be_cb(libxl__egc *egc, ++ libxl__ev_devstate *ds, ++ int rc) ++{ ++ libxl__ao_device *aodev = CONTAINER_OF(ds, *aodev, backend_ds); ++ STATE_AO_GC(aodev->ao); ++ vscsidev_rm_t *vscsidev_rm = CONTAINER_OF(aodev->dev, *vscsidev_rm, dev); ++ libxl_device_vscsictrl *ctrl = vscsidev_rm->ctrl; ++ xs_transaction_t t = XBT_NULL; ++ int i; ++ ++ for (;;) { ++ rc = libxl__xs_transaction_start(gc, &t); ++ if (rc) goto out; ++ ++ for (i = 0; i < ctrl->num_vscsidevs; i++) ++ vscsictrl_remove_be_dev(gc, ctrl->vscsidevs + i, t, ++ vscsidev_rm->be_path, ++ vscsidev_rm->dev_wait); ++ ++ rc = libxl__xs_transaction_commit(gc, &t); ++ if (!rc) break; ++ if (rc < 0) goto out; ++ } ++ ++out: ++ aodev->rc = rc; ++ aodev->callback(egc, aodev); ++} ++ ++static void vscsidev__remove(libxl__egc *egc, libxl__ao_device *aodev) ++{ ++ STATE_AO_GC(aodev->ao); ++ vscsidev_rm_t *vscsidev_rm = CONTAINER_OF(aodev->dev, *vscsidev_rm, dev); ++ char *state_path; ++ int rc, be_wait; ++ ++ vscsidev_rm->be_path = libxl__device_backend_path(gc, aodev->dev); ++ state_path = GCSPRINTF("%s/state", vscsidev_rm->be_path); ++ ++ rc = vscsictrl_reconfigure_rm(aodev, state_path, &be_wait); ++ if (rc) goto out; ++ ++ rc = libxl__ev_devstate_wait(ao, &aodev->backend_ds, ++ vscsictrl_remove_be_cb, ++ state_path, be_wait, ++ LIBXL_DESTROY_TIMEOUT * 1000); ++ if (rc) { ++ LOG(ERROR, "unable to wait for %s", state_path); ++ goto out; ++ } ++ ++ return; ++ ++out: ++ aodev->rc = rc; ++ /* Notify that this is done */ ++ aodev->callback(egc, aodev); ++} ++ ++static int vscsidev_remove(libxl_ctx *ctx, ++ uint32_t domid, ++ libxl_device_vscsictrl *vscsictrl, ++ const libxl_asyncop_how *ao_how) ++{ ++ AO_CREATE(ctx, domid, ao_how); ++ libxl__ao_device *aodev; ++ vscsidev_rm_t *vscsidev_rm; ++ libxl__device *device; ++ int rc; ++ ++ GCNEW(aodev); ++ ++ GCNEW(vscsidev_rm); ++ vscsidev_rm->ctrl = vscsictrl; ++ device = &vscsidev_rm->dev; ++ ++ rc = libxl__device_from_vscsictrl(gc, domid, vscsictrl, device); ++ if (rc) goto out; ++ ++ libxl__prepare_ao_device(ao, aodev); ++ aodev->dev = device; ++ aodev->action = LIBXL__DEVICE_ACTION_REMOVE; ++ aodev->callback = vscsi_aodev_complete; ++ ++ vscsidev__remove(egc, aodev); ++ ++out: ++ if (rc) AO_CREATE_FAIL(rc); ++ return AO_INPROGRESS; ++} ++ ++static int vscsidev_backend_add(libxl__gc *gc, ++ libxl_device_vscsidev *v, ++ flexarray_t *back) ++{ ++ int rc; ++ char *dir; ++ unsigned int hst, chn, tgt; ++ unsigned long long lun; ++ ++ ++ dir = GCSPRINTF("vscsi-devs/dev-%u", v->vscsidev_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: ++ hst = v->pdev.u.hctl.m.hst; ++ chn = v->pdev.u.hctl.m.chn; ++ tgt = v->pdev.u.hctl.m.tgt; ++ lun = v->pdev.u.hctl.m.lun; ++ flexarray_append_pair(back, ++ GCSPRINTF("%s/p-dev", dir), ++ GCSPRINTF("%u:%u:%u:%llu", hst, chn, tgt, lun)); ++ break; ++ case LIBXL_VSCSI_PDEV_TYPE_INVALID: ++ /* fallthrough */ ++ default: ++ rc = ERROR_FAIL; ++ goto out; ++ } ++ flexarray_append_pair(back, ++ GCSPRINTF("%s/p-devname", dir), ++ v->pdev.p_devname); ++ hst = v->vdev.hst; ++ chn = v->vdev.chn; ++ tgt = v->vdev.tgt; ++ lun = v->vdev.lun; ++ flexarray_append_pair(back, ++ GCSPRINTF("%s/v-dev", dir), ++ GCSPRINTF("%u:%u:%u:%llu", hst, chn, tgt, lun)); ++ flexarray_append_pair(back, ++ GCSPRINTF("%s/state", dir), ++ GCSPRINTF("%d", XenbusStateInitialising)); ++ rc = 0; ++out: ++ return rc; ++} ++ ++static void vscsictrl_new_backend(libxl__egc *egc, ++ libxl__ao_device *aodev, ++ libxl_device_vscsictrl *vscsictrl, ++ libxl_domain_config *d_config) ++{ ++ STATE_AO_GC(aodev->ao); ++ int rc, i; ++ flexarray_t *back; ++ flexarray_t *front; ++ libxl_device_vscsidev *v; ++ xs_transaction_t t = XBT_NULL; ++ ++ /* Prealloc key+value: 4 toplevel + 4 per device */ ++ i = 2 * (4 + (4 * vscsictrl->num_vscsidevs)); ++ 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, ++ LIBXL_CTRL_INDEX, ++ GCSPRINTF("%d", vscsictrl->idx)); ++ flexarray_append_pair(back, "feature-host", ++ libxl_defbool_val(vscsictrl->scsi_raw_cmds) ? ++ "1" : "0"); ++ ++ flexarray_append_pair(front, ++ "backend-id", ++ GCSPRINTF("%d", vscsictrl->backend_domid)); ++ flexarray_append_pair(front, ++ "state", ++ GCSPRINTF("%d", XenbusStateInitialising)); ++ ++ for (i = 0; i < vscsictrl->num_vscsidevs; i++) { ++ v = vscsictrl->vscsidevs + i; ++ rc = vscsidev_backend_add(gc, v, back); ++ if (rc) goto out; ++ } ++ ++ 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), ++ libxl__xs_kvs_of_flexarray(gc, front), ++ NULL); ++ ++ rc = libxl__xs_transaction_commit(gc, &t); ++ if (!rc) break; ++ if (rc < 0) goto out; ++ } ++ ++ libxl__wait_device_connection(egc, aodev); ++ return; ++ ++out: ++ libxl__xs_transaction_abort(gc, &t); ++ aodev->rc = rc; ++ aodev->callback(egc, aodev); ++} ++ ++static void vscsictrl_do_reconfigure_add_cb(libxl__egc *egc, ++ libxl__ev_devstate *ds, ++ int rc) ++{ ++ libxl__ao_device *aodev = CONTAINER_OF(ds, *aodev, backend_ds); ++ STATE_AO_GC(aodev->ao); ++ aodev->rc = rc; ++ aodev->callback(egc, aodev); ++} ++ ++static void vscsictrl_do_reconfigure_add(libxl__egc *egc, ++ libxl__ao_device *aodev, ++ libxl_device_vscsictrl *vscsictrl, ++ libxl_domain_config *d_config) ++{ ++ STATE_AO_GC(aodev->ao); ++ int rc, i, be_state, be_wait; ++ const char *be_path; ++ char *dev_path, *state_path, *state_val; ++ flexarray_t *back; ++ libxl_device_vscsidev *v; ++ xs_transaction_t t = XBT_NULL; ++ bool do_reconfigure = false; ++ ++ /* Prealloc key+value: 1 toplevel + 4 per device */ ++ i = 2 * (1 + (4 * vscsictrl->num_vscsidevs)); ++ back = flexarray_make(gc, i, 1); ++ ++ be_path = libxl__device_backend_path(gc, aodev->dev); ++ 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; ++ do_reconfigure = false; ++ break; ++ case XenbusStateConnected: ++ /* The backend can handle reconfigure */ ++ be_wait = XenbusStateConnected; ++ flexarray_append_pair(back, "state", GCSPRINTF("%d", XenbusStateReconfiguring)); ++ do_reconfigure = true; ++ break; ++ } ++ ++ /* Append new vscsidev or skip existing */ ++ for (i = 0; i < vscsictrl->num_vscsidevs; i++) { ++ unsigned int nb = 0; ++ v = vscsictrl->vscsidevs + i; ++ dev_path = GCSPRINTF("%s/vscsi-devs/dev-%u", be_path, v->vscsidev_id); ++ if (libxl__xs_directory(gc, XBT_NULL, dev_path, &nb)) { ++ /* FIXME Sanity check */ ++ LOG(DEBUG, "%s exists already with %u entries", dev_path, nb); ++ continue; ++ } ++ rc = vscsidev_backend_add(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)); ++ ++ rc = libxl__xs_transaction_commit(gc, &t); ++ if (!rc) break; ++ if (rc < 0) goto out; ++ } ++ ++ if (do_reconfigure) { ++ rc = libxl__ev_devstate_wait(ao, &aodev->backend_ds, ++ vscsictrl_do_reconfigure_add_cb, ++ state_path, be_wait, ++ LIBXL_INIT_TIMEOUT * 1000); ++ if (rc) goto out; ++ } ++ return; ++ ++out: ++ libxl__xs_transaction_abort(gc, &t); ++ aodev->rc = rc; ++ aodev->callback(egc, aodev); ++} ++ ++static int vscsictrl_next_vscsidev_id(libxl__gc *gc, ++ const char *libxl_path, ++ libxl_devid *vscsidev_id) ++{ ++ const char *val; ++ xs_transaction_t t = XBT_NULL; ++ unsigned int id; ++ int rc; ++ ++ for (;;) { ++ rc = libxl__xs_transaction_start(gc, &t); ++ if (rc) goto out; ++ ++ val = libxl__xs_read(gc, t, libxl_path); ++ id = val ? strtoul(val, NULL, 10) : 0; ++ ++ LOG(DEBUG, "%s = %s vscsidev_id %u", libxl_path, val, id); ++ ++ val = GCSPRINTF("%u", id + 1); ++ rc = libxl__xs_write_checked(gc, t, libxl_path, val); ++ if (rc) goto out; ++ ++ rc = libxl__xs_transaction_commit(gc, &t); ++ if (!rc) break; ++ if (rc < 0) goto out; ++ } ++ ++ *vscsidev_id = id; ++ rc = 0; ++ ++out: ++ libxl__xs_transaction_abort(gc, &t); ++ return rc; ++} ++ ++static int vscsictrl_assign_vscsidev_ids(libxl__gc *gc, ++ uint32_t domid, ++ libxl_device_vscsictrl *vscsictrl) ++{ ++ libxl_device_vscsidev *dev; ++ libxl_devid vscsidev_id; ++ const char *libxl_path; ++ int rc, i; ++ ++ libxl_path = GCSPRINTF("%s/vscsi/%u/next_vscsidev_id", ++ libxl__xs_libxl_path(gc, domid), ++ vscsictrl->devid); ++ for (i = 0; i < vscsictrl->num_vscsidevs; i++) { ++ dev = &vscsictrl->vscsidevs[i]; ++ if (dev->vscsidev_id >= 0) ++ continue; ++ rc = vscsictrl_next_vscsidev_id(gc, libxl_path, &vscsidev_id); ++ if (rc) { ++ LOG(ERROR, "failed to assign vscsidev_id to %s for %s", ++ libxl_path, dev->pdev.p_devname); ++ goto out; ++ } ++ dev->vscsidev_id = vscsidev_id; ++ } ++ ++ rc = 0; ++out: ++ return rc; ++} ++ ++static void vscsictrl_update_json(libxl__egc *egc, ++ libxl__ao_device *aodev, ++ libxl_device_vscsictrl *vscsictrl, ++ vscsictrl_add fn) ++{ ++ STATE_AO_GC(aodev->ao); ++ int rc; ++ uint32_t domid = aodev->dev->domid; ++ libxl_device_vscsictrl vscsictrl_saved; ++ libxl_domain_config d_config; ++ libxl__domain_userdata_lock *lock = NULL; ++ ++ libxl_domain_config_init(&d_config); ++ libxl_device_vscsictrl_init(&vscsictrl_saved); ++ ++ libxl_device_vscsictrl_copy(CTX, &vscsictrl_saved, vscsictrl); ++ ++ rc = vscsictrl_assign_vscsidev_ids(gc, domid, &vscsictrl_saved); ++ 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_domain_config(gc, &d_config, &libxl__vscsictrl_devtype, ++ &vscsictrl_saved); ++ } ++ ++ fn(egc, aodev, &vscsictrl_saved, &d_config); ++ ++out: ++ if (lock) libxl__unlock_domain_userdata(lock); ++ libxl_device_vscsictrl_dispose(&vscsictrl_saved); ++ libxl_domain_config_dispose(&d_config); ++ if (rc) { ++ aodev->rc = rc; ++ aodev->callback(egc, aodev); ++ } ++} ++ ++static void vscsictrl__reconfigure_add(libxl__egc *egc, ++ uint32_t domid, ++ libxl_device_vscsictrl *vscsictrl, ++ libxl__ao_device *aodev) ++{ ++ STATE_AO_GC(aodev->ao); ++ libxl__device *device; ++ vscsictrl_add fn; ++ int rc; ++ ++ GCNEW(device); ++ rc = libxl__device_from_vscsictrl(gc, domid, vscsictrl, device); ++ if (rc) goto out; ++ aodev->dev = device; ++ ++ fn = vscsictrl_do_reconfigure_add; ++ vscsictrl_update_json(egc, aodev, vscsictrl, fn); ++ return; ++ ++out: ++ aodev->rc = rc; ++ aodev->callback(egc, aodev); ++} ++ ++static int vscsictrl_reconfigure_add(libxl_ctx *ctx, ++ uint32_t domid, ++ libxl_device_vscsictrl *vscsictrl, ++ const libxl_asyncop_how *ao_how) ++{ ++ AO_CREATE(ctx, domid, ao_how); ++ libxl__ao_device *aodev; ++ ++ GCNEW(aodev); ++ libxl__prepare_ao_device(ao, aodev); ++ aodev->action = LIBXL__DEVICE_ACTION_ADD; ++ aodev->callback = vscsi_aodev_complete; ++ aodev->update_json = true; ++ vscsictrl__reconfigure_add(egc, domid, vscsictrl, aodev); ++ ++ return AO_INPROGRESS; ++} ++ ++static LIBXL_DEFINE_UPDATE_DEVID(vscsictrl) ++ ++static int libxl__device_vscsictrl_setdefault(libxl__gc *gc, uint32_t domid, ++ libxl_device_vscsictrl *vscsictrl, bool hotplug) ++{ ++ return 0; ++} ++ ++static void libxl__device_vscsictrl_add(libxl__egc *egc, uint32_t domid, ++ libxl_device_vscsictrl *vscsictrl, ++ libxl__ao_device *aodev) ++{ ++ STATE_AO_GC(aodev->ao); ++ libxl__device *device; ++ vscsictrl_add fn; ++ int rc; ++ ++ if (vscsictrl->devid == -1) { ++ if ((vscsictrl->devid = libxl__device_nextid(gc, domid, LIBXL__DEVICE_KIND_VSCSI)) < 0) { ++ rc = ERROR_FAIL; ++ goto out; ++ } ++ } ++ ++ GCNEW(device); ++ rc = libxl__device_from_vscsictrl(gc, domid, vscsictrl, device); ++ if (rc) goto out; ++ aodev->dev = device; ++ ++ fn = vscsictrl_new_backend; ++ vscsictrl_update_json(egc, aodev, vscsictrl, fn); ++ return; ++ ++out: ++ aodev->rc = rc; ++ aodev->callback(egc, aodev); ++} ++ ++int libxl_device_vscsictrl_remove(libxl_ctx *ctx, uint32_t domid, ++ libxl_device_vscsictrl *vscsictrl, ++ const libxl_asyncop_how *ao_how) ++{ ++ return vscsictrl_remove(ctx, domid, vscsictrl, ao_how, 0); ++} ++ ++int libxl_device_vscsictrl_destroy(libxl_ctx *ctx, uint32_t domid, ++ libxl_device_vscsictrl *vscsictrl, ++ const libxl_asyncop_how *ao_how) ++{ ++ return vscsictrl_remove(ctx, domid, vscsictrl, ao_how, 1); ++} ++ ++libxl_device_vscsictrl *libxl_device_vscsictrl_list(libxl_ctx *ctx, ++ uint32_t domid, ++ int *num) ++{ ++ GC_INIT(ctx); ++ libxl_device_vscsictrl *ctrls = NULL; ++ int rc, num_ctrls = 0; ++ ++ *num = 0; ++ ++ rc = vscsi_collect_ctrls(gc, domid, &ctrls, &num_ctrls); ++ if (rc == 0) ++ *num = num_ctrls; ++ ++ GC_FREE; ++ return ctrls; ++} ++ ++int libxl_device_vscsictrl_getinfo(libxl_ctx *ctx, uint32_t domid, ++ libxl_device_vscsictrl *vscsictrl, ++ libxl_device_vscsidev *vscsidev, ++ 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 = vscsictrl->devid; ++ vscsiinfo->vscsidev_id = vscsidev->vscsidev_id; ++ libxl_vscsi_pdev_copy(ctx, &vscsiinfo->pdev, &vscsidev->pdev); ++ libxl_vscsi_hctl_copy(ctx, &vscsiinfo->vdev, &vscsidev->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->vscsictrl_state = val ? strtoul(val, NULL, 10) : -1; ++ ++ val = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/" LIBXL_CTRL_INDEX, vscsipath)); ++ vscsiinfo->idx = 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, vscsidev->vscsidev_id)); ++ vscsiinfo->vscsidev_state = val ? strtoul(val, NULL, 10) : -1; ++ ++ rc = 0; ++out: ++ GC_FREE; ++ return rc; ++} ++ ++int libxl_device_vscsidev_add(libxl_ctx *ctx, uint32_t domid, ++ libxl_device_vscsidev *vscsidev, ++ const libxl_asyncop_how *ao_how) ++{ ++ GC_INIT(ctx); ++ libxl_device_vscsictrl *vc, *ctrls = NULL; ++ libxl_device_vscsidev *vd; ++ int c, d, rc, num_ctrls = 0; ++ int duplicate = 0; ++ ++ rc = vscsi_collect_ctrls(gc, domid, &ctrls, &num_ctrls); ++ if (rc != 0) goto out; ++ ++ ++ for (c = 0; c < num_ctrls; ++c) { ++ vc = ctrls + c; ++ if (vc->idx != vscsidev->vdev.hst) ++ continue; ++ ++ for (d = 0; d < vc->num_vscsidevs; d++) { ++ vd = vc->vscsidevs + d; ++ if (vd->vdev.hst == vscsidev->vdev.hst && ++ vd->vdev.chn == vscsidev->vdev.chn && ++ vd->vdev.tgt == vscsidev->vdev.tgt && ++ vd->vdev.lun == vscsidev->vdev.lun) { ++ unsigned long long lun = vd->vdev.lun; ++ LOG(ERROR, "vdev '%u:%u:%u:%llu' is already used.\n", ++ vd->vdev.hst, vd->vdev.chn, vd->vdev.tgt, lun); ++ rc = ERROR_DEVICE_EXISTS; ++ duplicate = 1; ++ break; ++ } ++ } ++ ++ if (!duplicate) { ++ /* Append vscsidev to this vscsictrl, trigger reconfigure */ ++ libxl_device_vscsictrl_append_vscsidev(ctx, vc, vscsidev); ++ rc = vscsictrl_reconfigure_add(ctx, domid, vc, ao_how); ++ } ++ break; ++ } ++ ++ for (c = 0; c < num_ctrls; ++c) ++ libxl_device_vscsictrl_dispose(ctrls + c); ++ free(ctrls); ++ ++out: ++ GC_FREE; ++ return rc; ++} ++ ++int libxl_device_vscsidev_remove(libxl_ctx *ctx, uint32_t domid, ++ libxl_device_vscsidev *vscsidev, ++ const libxl_asyncop_how *ao_how) ++{ ++ GC_INIT(ctx); ++ libxl_device_vscsictrl *vc, *ctrls = NULL; ++ libxl_device_vscsidev *vd; ++ int c, d, rc, num_ctrls = 0; ++ int found = 0, idx; ++ int head, tail, i; ++ ++ rc = vscsi_collect_ctrls(gc, domid, &ctrls, &num_ctrls); ++ if (rc != 0) goto out; ++ ++ ++ for (c = 0; c < num_ctrls; ++c) { ++ vc = ctrls + c; ++ ++ for (d = 0; d < vc->num_vscsidevs; d++) { ++ vd = vc->vscsidevs + d; ++ if (vd->vdev.hst == vscsidev->vdev.hst && ++ vd->vdev.chn == vscsidev->vdev.chn && ++ vd->vdev.tgt == vscsidev->vdev.tgt && ++ vd->vdev.lun == vscsidev->vdev.lun) { ++ found = 1; ++ idx = d; ++ break; ++ } ++ } ++ ++ if (found) { ++ if (vc->num_vscsidevs > 1) { ++ /* Prepare vscsictrl, leave only desired vscsidev */ ++ head = idx; ++ tail = vc->num_vscsidevs - idx - 1; ++ for (i = 0; i < head; i++) ++ libxl_device_vscsictrl_remove_vscsidev(ctx, vc, 0); ++ for (i = 0; i < tail; i++) ++ libxl_device_vscsictrl_remove_vscsidev(ctx, vc, 1); ++ ++ /* Remove single vscsidev connected to this vscsictrl */ ++ rc = vscsidev_remove(ctx, domid, vc, ao_how); ++ } else { ++ /* Wipe entire vscsictrl */; ++ rc = vscsictrl_remove(ctx, domid, vc, ao_how, 0); ++ } ++ break; ++ } ++ } ++ ++ for (c = 0; c < num_ctrls; ++c) ++ libxl_device_vscsictrl_dispose(ctrls + c); ++ free(ctrls); ++ ++ if (!found) ++ rc = ERROR_NOTFOUND; ++ ++out: ++ GC_FREE; ++ return rc; ++} ++ ++void libxl_device_vscsictrl_append_vscsidev(libxl_ctx *ctx, ++ libxl_device_vscsictrl *ctrl, ++ libxl_device_vscsidev *dev) ++{ ++ GC_INIT(ctx); ++ ctrl->vscsidevs = libxl__realloc(NOGC, ctrl->vscsidevs, sizeof(*dev) * (ctrl->num_vscsidevs + 1)); ++ libxl_device_vscsidev_init(ctrl->vscsidevs + ctrl->num_vscsidevs); ++ libxl_device_vscsidev_copy(CTX, ctrl->vscsidevs + ctrl->num_vscsidevs, dev); ++ ctrl->num_vscsidevs++; ++ GC_FREE; ++} ++ ++void libxl_device_vscsictrl_remove_vscsidev(libxl_ctx *ctx, ++ libxl_device_vscsictrl *ctrl, ++ unsigned int idx) ++{ ++ GC_INIT(ctx); ++ if (idx >= ctrl->num_vscsidevs) ++ return; ++ libxl_device_vscsidev_dispose(&ctrl->vscsidevs[idx]); ++ if (ctrl->num_vscsidevs > idx + 1) ++ memmove(&ctrl->vscsidevs[idx], ++ &ctrl->vscsidevs[idx + 1], ++ (ctrl->num_vscsidevs - idx - 1) * sizeof(*ctrl->vscsidevs)); ++ ctrl->vscsidevs = libxl__realloc(NOGC, ctrl->vscsidevs, sizeof(*ctrl->vscsidevs) * (ctrl->num_vscsidevs - 1)); ++ ctrl->num_vscsidevs--; ++ GC_FREE; ++} ++ ++static int libxl_device_vscsictrl_compare(libxl_device_vscsictrl *d1, ++ libxl_device_vscsictrl *d2) ++{ ++ return COMPARE_DEVID(d1, d2); ++} ++ ++LIBXL_DEFINE_DEVICE_ADD(vscsictrl) ++static LIBXL_DEFINE_DEVICES_ADD(vscsictrl) ++//LIBXL_DEFINE_DEVICE_REMOVE(vscsictrl) ++DEFINE_DEVICE_TYPE_STRUCT(vscsictrl, VSCSI, ); ++ ++/* ++ * Local variables: ++ * mode: C ++ * c-basic-offset: 4 ++ * indent-tabs-mode: nil ++ * End: ++ */ +Index: xen-4.13.0-testing/tools/libxl/libxlu_vscsi.c +=================================================================== +--- /dev/null ++++ xen-4.13.0-testing/tools/libxl/libxlu_vscsi.c +@@ -0,0 +1,668 @@ ++/* ++ * libxlu_vscsi.c - xl configuration file parsing: setup and helper functions ++ * ++ * Copyright (C) 2016 SUSE Linux GmbH ++ * Author Olaf Hering ++ * Author Ondřej Holeček ++ * ++ * 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 ++#include ++#include ++#include ++#include ++#include ++#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 long long lun; ++}; ++ ++static int xlu__vscsi_parse_hctl(char *str, libxl_vscsi_hctl *hctl) ++{ ++ unsigned int hst, chn, tgt; ++ unsigned long long lun; ++ ++ if (sscanf(str, "%u:%u:%u:%llu", &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_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./tpgt_1/lun/lun_0//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./tpgt_1/lun/lun_0//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./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_%llu", &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./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. */ ++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.%16[0-9a-fA-F]", tgt->wwn) != 1) ++ 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:%llu", 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 long long lun; ++ char wwn[XLU_WWN_LEN + 1]; ++ ++ memset(wwn, 0, sizeof(wwn)); ++ if (sscanf(str, "naa.%16[0-9a-fA-F]:%llu", wwn, &lun) == 2) { ++ 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_vscsictrl *new_ctrl, ++ libxl_device_vscsidev *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; ++ } ++ ++ new_ctrl->idx = new_dev->vdev.hst; ++ ++ if (fhost) { ++ fhost = xlu__vscsi_trim_string(fhost); ++ if (strcmp(fhost, "feature-host") == 0) { ++ libxl_defbool_set(&new_ctrl->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_ctrl->scsi_raw_cmds, false); ++ rc = 0; ++ ++out: ++ free(tmp); ++ return rc; ++} ++ ++int xlu_vscsi_get_ctrl(XLU_Config *cfg, libxl_ctx *ctx, uint32_t domid, ++ const char *str, ++ libxl_device_vscsictrl *ctrl, ++ libxl_device_vscsidev *dev, ++ libxl_device_vscsictrl *existing, ++ bool *found_existing) ++{ ++ libxl_device_vscsictrl *vscsictrls = NULL, *tmp; ++ int rc, found_ctrl = -1, i; ++ int num_ctrls; ++ ++ ++ rc = xlu_vscsi_parse(cfg, ctx, str, ctrl, dev); ++ if (rc) ++ goto out; ++ ++ /* Look for existing vscsictrl for given domain */ ++ vscsictrls = libxl_device_vscsictrl_list(ctx, domid, &num_ctrls); ++ if (vscsictrls) { ++ for (i = 0; i < num_ctrls; ++i) { ++ if (vscsictrls[i].idx == dev->vdev.hst) { ++ found_ctrl = i; ++ break; ++ } ++ } ++ } ++ ++ if (found_ctrl == -1) { ++ *found_existing = false; ++ } else { ++ *found_existing = true; ++ tmp = vscsictrls + found_ctrl; ++ ++ /* Check if the vdev address is already taken */ ++ for (i = 0; i < tmp->num_vscsidevs; ++i) { ++ if (tmp->vscsidevs[i].vdev.chn == dev->vdev.chn && ++ tmp->vscsidevs[i].vdev.tgt == dev->vdev.tgt && ++ tmp->vscsidevs[i].vdev.lun == dev->vdev.lun) { ++ unsigned long long lun = dev->vdev.lun; ++ LOG(cfg, "vdev '%u:%u:%u:%llu' is already used.\n", ++ dev->vdev.hst, dev->vdev.chn, dev->vdev.tgt, lun); ++ rc = ERROR_INVAL; ++ goto out; ++ } ++ } ++ ++ if (libxl_defbool_val(ctrl->scsi_raw_cmds) != ++ libxl_defbool_val(tmp->scsi_raw_cmds)) { ++ LOG(cfg, "different feature-host setting: " ++ "existing ctrl has it %s, new ctrl has it %s\n", ++ libxl_defbool_val(ctrl->scsi_raw_cmds) ? "set" : "unset", ++ libxl_defbool_val(tmp->scsi_raw_cmds) ? "set" : "unset"); ++ rc = ERROR_INVAL; ++ goto out; ++ } ++ ++ libxl_device_vscsictrl_copy(ctx, existing, tmp); ++ } ++ ++ rc = 0; ++ ++out: ++ if (vscsictrls) { ++ for (i = 0; i < num_ctrls; ++i) ++ libxl_device_vscsictrl_dispose(vscsictrls + i); ++ free(vscsictrls); ++ } ++ return rc; ++} ++ ++int xlu_vscsi_detach(XLU_Config *cfg, libxl_ctx *ctx, uint32_t domid, char *str) ++{ ++ libxl_device_vscsidev dev = { }; ++ libxl_device_vscsictrl ctrl = { }; ++ int rc; ++ char *tmp = NULL; ++ ++ libxl_device_vscsictrl_init(&ctrl); ++ libxl_device_vscsidev_init(&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); ++ rc = ERROR_FAIL; ++ goto out; ++ } ++ ++ rc = xlu_vscsi_parse(cfg, ctx, tmp, &ctrl, &dev); ++ if (rc) goto out; ++ ++ rc = libxl_device_vscsidev_remove(ctx, domid, &dev, NULL); ++ switch (rc) { ++ case ERROR_NOTFOUND: ++ LOG(cfg, "detach failed: %s does not exist in domid %u", str, domid); ++ break; ++ default: ++ break; ++ } ++ ++out: ++ free(tmp); ++ libxl_device_vscsidev_dispose(&dev); ++ libxl_device_vscsictrl_dispose(&ctrl); ++ return rc; ++} ++ ++int xlu_vscsi_config_add(XLU_Config *cfg, ++ libxl_ctx *ctx, ++ const char *str, ++ int *num_vscsis, ++ libxl_device_vscsictrl **vscsis) ++{ ++ int rc, i; ++ libxl_device_vscsidev dev = { }; ++ libxl_device_vscsictrl *tmp_ctrl, ctrl = { }; ++ bool ctrl_found = false; ++ ++ /* ++ * #1: parse the devspec and place it in temporary ctrl+dev part ++ * #2: find existing vscsictrl with number vdev.hst ++ * if found, append the vscsidev to this vscsictrl ++ * #3: otherwise, create new vscsictrl and append vscsidev ++ * Note: vdev.hst does not represent the index named "num_vscsis", ++ * it is a private index used just in the config file ++ */ ++ libxl_device_vscsictrl_init(&ctrl); ++ libxl_device_vscsidev_init(&dev); ++ ++ rc = xlu_vscsi_parse(cfg, ctx, str, &ctrl, &dev); ++ if (rc) ++ goto out; ++ ++ if (*num_vscsis) { ++ for (i = 0; i < *num_vscsis; i++) { ++ tmp_ctrl = *vscsis + i; ++ if (tmp_ctrl->idx == dev.vdev.hst) { ++ libxl_device_vscsictrl_append_vscsidev(ctx, tmp_ctrl, &dev); ++ ctrl_found = true; ++ break; ++ } ++ } ++ } ++ ++ if (!ctrl_found || !*num_vscsis) { ++ tmp_ctrl = realloc(*vscsis, sizeof(ctrl) * (*num_vscsis + 1)); ++ if (!tmp_ctrl) { ++ LOG(cfg, "realloc #%d failed", *num_vscsis + 1); ++ rc = ERROR_NOMEM; ++ goto out; ++ } ++ *vscsis = tmp_ctrl; ++ tmp_ctrl = *vscsis + *num_vscsis; ++ libxl_device_vscsictrl_init(tmp_ctrl); ++ ++ libxl_device_vscsictrl_copy(ctx, tmp_ctrl, &ctrl); ++ ++ libxl_device_vscsictrl_append_vscsidev(ctx, tmp_ctrl, &dev); ++ ++ (*num_vscsis)++; ++ } ++ ++ rc = 0; ++out: ++ libxl_device_vscsidev_dispose(&dev); ++ libxl_device_vscsictrl_dispose(&ctrl); ++ return rc; ++} ++#else /* ! __linux__ */ ++int xlu_vscsi_get_ctrl(XLU_Config *cfg, libxl_ctx *ctx, uint32_t domid, ++ const char *str, ++ libxl_device_vscsictrl *ctrl, ++ libxl_device_vscsidev *dev, ++ libxl_device_vscsictrl *existing, ++ bool *found_existing) ++{ ++ return ERROR_INVAL; ++} ++ ++int xlu_vscsi_parse(XLU_Config *cfg, ++ libxl_ctx *ctx, ++ const char *str, ++ libxl_device_vscsictrl *new_ctrl, ++ libxl_device_vscsidev *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_vscsictrl **vscsis) ++{ ++ return ERROR_INVAL; ++} ++#endif +Index: xen-4.13.0-testing/tools/libxl/libxlutil.h +=================================================================== +--- xen-4.13.0-testing.orig/tools/libxl/libxlutil.h ++++ xen-4.13.0-testing/tools/libxl/libxlutil.h +@@ -125,6 +125,25 @@ int xlu_rdm_parse(XLU_Config *cfg, libxl + int xlu_vif_parse_rate(XLU_Config *cfg, const char *rate, + libxl_device_nic *nic); + ++/* Fill ctrl/dev with device described in str (pdev,vdev[,options]) */ ++int xlu_vscsi_get_ctrl(XLU_Config *cfg, libxl_ctx *ctx, uint32_t domid, ++ const char *str, ++ libxl_device_vscsictrl *ctrl, ++ libxl_device_vscsidev *dev, ++ libxl_device_vscsictrl *existing, ++ bool *found_existing); ++/* Parse config string and fill provided vscsi ctrl and vscsi device */ ++int xlu_vscsi_parse(XLU_Config *cfg, libxl_ctx *ctx, const char *str, ++ libxl_device_vscsictrl *new_ctrl, ++ libxl_device_vscsidev *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_vscsictrl **vscsis); + #endif /* LIBXLUTIL_H */ + + /* +Index: xen-4.13.0-testing/tools/xl/Makefile +=================================================================== +--- xen-4.13.0-testing.orig/tools/xl/Makefile ++++ xen-4.13.0-testing/tools/xl/Makefile +@@ -18,7 +18,7 @@ CFLAGS_XL += -Wshadow + XL_OBJS-$(CONFIG_X86) = xl_psr.o + XL_OBJS = xl.o xl_cmdtable.o xl_sxp.o xl_utils.o $(XL_OBJS-y) + XL_OBJS += xl_parse.o xl_cpupool.o xl_flask.o +-XL_OBJS += xl_vtpm.o xl_block.o xl_nic.o xl_usb.o ++XL_OBJS += xl_vtpm.o xl_vscsi.o xl_block.o xl_nic.o xl_usb.o + XL_OBJS += xl_sched.o xl_pci.o xl_vcpu.o xl_cdrom.o xl_mem.o + XL_OBJS += xl_info.o xl_console.o xl_misc.o + XL_OBJS += xl_vmcontrol.o xl_saverestore.o xl_migrate.o +Index: xen-4.13.0-testing/tools/xl/xl.h +=================================================================== +--- xen-4.13.0-testing.orig/tools/xl/xl.h ++++ xen-4.13.0-testing/tools/xl/xl.h +@@ -165,6 +165,9 @@ int main_channellist(int argc, char **ar + 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); +Index: xen-4.13.0-testing/tools/xl/xl_parse.c +=================================================================== +--- xen-4.13.0-testing.orig/tools/xl/xl_parse.c ++++ xen-4.13.0-testing/tools/xl/xl_parse.c +@@ -1212,7 +1212,8 @@ void parse_config_data(const char *confi + long l, vcpus = 0; + XLU_Config *config; + XLU_ConfigList *cpus, *vbds, *nics, *pcis, *cvfbs, *cpuids, *vtpms, +- *usbctrls, *usbdevs, *p9devs, *vdispls, *pvcallsifs_devs; ++ *usbctrls, *usbdevs, *p9devs, *vdispls, *pvcallsifs_devs, ++ *vscsictrls; + XLU_ConfigList *channels, *ioports, *irqs, *iomem, *viridian, *dtdevs, + *mca_caps; + int num_ioports, num_irqs, num_iomem, num_cpus, num_viridian, num_mca_caps; +@@ -2045,6 +2046,17 @@ void parse_config_data(const char *confi + } + } + ++ if (!xlu_cfg_get_list(config, "vscsi", &vscsictrls, 0, 0)) { ++ int num_vscsi_items = 0; ++ d_config->num_vscsictrls = 0; ++ d_config->vscsictrls = NULL; ++ while ((buf = xlu_cfg_get_listitem (vscsictrls, num_vscsi_items)) != NULL) { ++ if (xlu_vscsi_config_add(config, ctx, buf, &d_config->num_vscsictrls, &d_config->vscsictrls)) ++ exit(1); ++ num_vscsi_items++; ++ } ++ } ++ + if (!xlu_cfg_get_list(config, "vtpm", &vtpms, 0, 0)) { + d_config->num_vtpms = 0; + d_config->vtpms = NULL; +Index: xen-4.13.0-testing/tools/xl/xl_vscsi.c +=================================================================== +--- /dev/null ++++ xen-4.13.0-testing/tools/xl/xl_vscsi.c +@@ -0,0 +1,229 @@ ++/* ++ * Copyright 2009-2017 Citrix Ltd and other contributors ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU Lesser General Public License as published ++ * by the Free Software Foundation; version 2.1 only. with the special ++ * exception on linking described in file LICENSE. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU Lesser General Public License for more details. ++ */ ++ ++#include ++#include ++ ++#include ++#include ++#include ++ ++#include "xl.h" ++#include "xl_utils.h" ++#include "xl_parse.h" ++ ++int main_vscsiattach(int argc, char **argv) ++{ ++ uint32_t domid; ++ int opt, rc; ++ XLU_Config *config = NULL; ++ libxl_device_vscsictrl ctrl, existing; ++ libxl_device_vscsidev dev; ++ bool found_existing = false; ++ char *str = NULL, *feat_buf = NULL; ++ char *json; ++ ++ 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) ++ xasprintf(&feat_buf, ",%s", argv[4]); ++ ++ xasprintf(&str, "%s,%s%s", argv[2], argv[3], feat_buf ?: ""); ++ ++ libxl_device_vscsictrl_init(&existing); ++ libxl_device_vscsictrl_init(&ctrl); ++ libxl_device_vscsidev_init(&dev); ++ ++ 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_ctrl(config, ctx, domid, str, &ctrl, &dev, &existing, &found_existing); ++ if (rc < 0) ++ goto out; ++ ++ if (dryrun_only) { ++ libxl_device_vscsictrl *tmp = found_existing ? &existing : &ctrl; ++ libxl_device_vscsictrl_append_vscsidev(ctx, tmp , &dev); ++ json = libxl_device_vscsictrl_to_json(ctx, tmp); ++ 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 (found_existing) { ++ if (libxl_device_vscsidev_add(ctx, domid, &dev, NULL)) { ++ fprintf(stderr, "libxl_device_vscsidev_add failed\n"); ++ rc = 1; ++ goto out; ++ } ++ } else { ++ libxl_device_vscsictrl_append_vscsidev(ctx, &ctrl, &dev); ++ if (libxl_device_vscsictrl_add(ctx, domid, &ctrl, NULL)) { ++ fprintf(stderr, "libxl_device_vscsictrl_add failed.\n"); ++ rc = 1; ++ goto out; ++ } ++ } ++ ++ rc = 0; ++out: ++ if (config) ++ xlu_cfg_destroy(config); ++ libxl_device_vscsictrl_dispose(&existing); ++ libxl_device_vscsictrl_dispose(&ctrl); ++ libxl_device_vscsidev_dispose(&dev); ++ free(str); ++ free(feat_buf); ++ return rc; ++} ++ ++int main_vscsilist(int argc, char **argv) ++{ ++ int opt; ++ uint32_t domid; ++ libxl_device_vscsictrl *vscsictrls; ++ libxl_vscsiinfo vscsiinfo; ++ int num_ctrls, h, d; ++ ++ SWITCH_FOREACH_OPT(opt, "", NULL, "scsi-list", 1) { ++ /* No options */ ++ } ++ if (argc < 2) { ++ help("scsi-list"); ++ return 1; ++ } ++ ++ /* Idx BE state ctrl p_hst v_hst state */ ++ printf("%-3s %-3s %-5s %-5s %-10s %-10s %-5s\n", ++ "Idx", "BE", "state", "ctrl", "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; ++ } ++ vscsictrls = libxl_device_vscsictrl_list(ctx, domid, &num_ctrls); ++ if (!vscsictrls) ++ continue; ++ ++ for (h = 0; h < num_ctrls; ++h) { ++ for (d = 0; d < vscsictrls[h].num_vscsidevs; d++) { ++ if (!libxl_device_vscsictrl_getinfo(ctx, domid, &vscsictrls[h], ++ &vscsictrls[h].vscsidevs[d], ++ &vscsiinfo)) { ++ char pdev[64], vdev[64]; ++ unsigned long long lun; ++ switch (vscsiinfo.pdev.type) { ++ case LIBXL_VSCSI_PDEV_TYPE_HCTL: ++ lun = vscsiinfo.pdev.u.hctl.m.lun; ++ snprintf(pdev, sizeof(pdev), "%u:%u:%u:%llu", ++ vscsiinfo.pdev.u.hctl.m.hst, ++ vscsiinfo.pdev.u.hctl.m.chn, ++ vscsiinfo.pdev.u.hctl.m.tgt, ++ lun); ++ break; ++ case LIBXL_VSCSI_PDEV_TYPE_WWN: ++ snprintf(pdev, sizeof(pdev), "%s", ++ vscsiinfo.pdev.u.wwn.m); ++ break; ++ default: ++ pdev[0] = '\0'; ++ break; ++ } ++ lun = vscsiinfo.vdev.lun; ++ snprintf(vdev, sizeof(vdev), "%u:%u:%u:%llu", ++ vscsiinfo.vdev.hst, ++ vscsiinfo.vdev.chn, ++ vscsiinfo.vdev.tgt, ++ lun); ++ /* Idx BE state Sta */ ++ printf("%-3d %-3d %-5d %-5d %-10s %-10s %d\n", ++ vscsiinfo.devid, ++ vscsiinfo.backend_id, ++ vscsiinfo.vscsictrl_state, ++ vscsiinfo.backend_id, ++ pdev, vdev, ++ vscsiinfo.vscsidev_state); ++ ++ } ++ libxl_vscsiinfo_dispose(&vscsiinfo); ++ } ++ libxl_device_vscsictrl_dispose(&vscsictrls[h]); ++ } ++ free(vscsictrls); ++ ++ } ++ ++ 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 rc = 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; ++ } ++ ++ rc = xlu_vscsi_detach(config, ctx, domid, str); ++ if (rc) ++ fprintf(stderr, "scsi-detach %s %s failed: %d\n", dom, str, rc); ++ ++out: ++ if (config) ++ xlu_cfg_destroy(config); ++ return !!rc; ++} ++ +Index: xen-4.13.0-testing/tools/xl/xl_cmdtable.c +=================================================================== +--- xen-4.13.0-testing.orig/tools/xl/xl_cmdtable.c ++++ xen-4.13.0-testing/tools/xl/xl_cmdtable.c +@@ -368,6 +368,21 @@ struct cmd_spec cmd_table[] = { + "Destroy a domain's virtual block device", + " ", + }, ++ { "scsi-attach", ++ &main_vscsiattach, 1, 1, ++ "Attach a dom0 SCSI device to a domain.", ++ " ", ++ }, ++ { "scsi-list", ++ &main_vscsilist, 0, 0, ++ "List all dom0 SCSI devices currently attached to a domain.", ++ "", ++ }, ++ { "scsi-detach", ++ &main_vscsidetach, 0, 1, ++ "Detach a specified SCSI device from a domain.", ++ " ", ++ }, + { "vtpm-attach", + &main_vtpmattach, 1, 1, + "Create a new virtual TPM device", -- cgit v1.2.3