summaryrefslogtreecommitdiff
path: root/libxl.pvscsi.patch
diff options
context:
space:
mode:
authorCoprDistGit <infra@openeuler.org>2023-10-12 04:00:49 +0000
committerCoprDistGit <infra@openeuler.org>2023-10-12 04:00:49 +0000
commitc22f60e6e55f1bf300dd76d2222a93911f3b2bb2 (patch)
treeef665e7018377f53612ac2751dcaea35a1c587b6 /libxl.pvscsi.patch
parent39a4763249cd6289e5019acfe0c98dbb169f5f2e (diff)
automatic import of xenopeneuler22.03_LTS
Diffstat (limited to 'libxl.pvscsi.patch')
-rw-r--r--libxl.pvscsi.patch2538
1 files changed, 2538 insertions, 0 deletions
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 <olaf@aepfle.de>
+Cc: Ian Jackson <ian.jackson@eu.citrix.com>
+Cc: Stefano Stabellini <stefano.stabellini@eu.citrix.com>
+Cc: Ian Campbell <ian.campbell@citrix.com>
+Cc: Wei Liu <wei.liu2@citrix.com>
+---
+ 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<vscsi=[ "VSCSI_SPEC_STRING", "VSCSI_SPEC_STRING", ...]>
++
++Specifies the PVSCSI devices to be provided to the guest. PVSCSI passes
++SCSI devices from the backend domain to the guest.
++
++Each VSCSI_SPEC_STRING consists of "pdev,vdev[,options]".
++'pdev' describes the physical device, preferable in a persistent format
++such as /dev/disk/by-*/*.
++'vdev' is the domU device in vHOST:CHANNEL:TARGET:LUN notation, all integers.
++'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<Linux>
++
++=over 4
++
++=item C<pdev>
++
++The backend driver in the pvops kernel is part of the Linux-IO Target framework
++(LIO). As such the SCSI devices have to be configured first with the tools
++provided by this framework, such as a xen-scsiback aware targetcli. The "pdev"
++in domU.cfg has to refer to a config item in that framework instead of the raw
++device. Usually this is a WWN in the form of "naa.WWN:LUN".
++
++=item C<options>
++
++No options recognized.
++
++=back
++
++=item B<Linux based on classic Xen kernel>
++
++=over 4
++
++=item C<pdev>
++
++The dom0 device in either /dev/scsidev or pHOST:CHANNEL:TARGET:LUN notation.
++
++It's recommended to use persistent names "/dev/disk/by-*/*" to refer to a "pdev".
++The toolstack will translate this internally to "h:c:t:l" notation, which is how
++the backend driver will access the device. Using the "h:c:t:l" notation for
++"pdev" in domU.cfg is discouraged because this value will change across reboots,
++depending on the detection order in the OS.
++
++=item C<options>
++
++Currently only the option value "feature-host" is recognized. SCSI command
++emulation in backend driver is bypassed when "feature-host" is specified.
++
++=back
++
++=back
++
+ =item B<vfb=[ "VFB_SPEC_STRING", "VFB_SPEC_STRING", ...]>
+
+ Specifies the paravirtual framebuffer devices which should be supplied
+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<scsi-attach> I<domain-id> I<pdev> I<vdev>,I<[feature-host]>
++
++Creates a new vscsi device in the domain specified by I<domain-id>.
++
++=item B<scsi-detach> I<domain-id> I<vdev>
++
++Removes the vscsi device from domain specified by I<domain-id>.
++
++=item B<scsi-list> I<domain-id> I<[domain-id] ...>
++
++List vscsi devices for the domain specified by I<domain-id>.
++
++=back
++
+ =head1 PCI PASS-THROUGH
+
+ =over 4
+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 <olaf@aepfle.de>
++ *
++ * 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 <olaf@aepfle.de>
++ * Author Ondřej Holeček <aaannz@gmail.com>
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU Lesser General Public License as published
++ * by the Free Software Foundation; version 2.1 only. with the special
++ * exception on linking described in file LICENSE.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU Lesser General Public License for more details.
++ */
++#include "libxl_osdeps.h" /* must come before any other headers */
++#include <unistd.h>
++#include <ctype.h>
++#include <dirent.h>
++#include <sys/stat.h>
++#include <sys/sysmacros.h>
++#include <fcntl.h>
++#include "libxlu_internal.h"
++
++#ifdef __linux__
++#define LOG(_c, _x, _a...) \
++ if((_c) && (_c)->report) fprintf((_c)->report, "%s(%u): " _x "\n", __func__, __LINE__, ##_a)
++
++#define XLU_SYSFS_TARGET_PVSCSI "/sys/kernel/config/target/xen-pvscsi"
++#define XLU_WWN_LEN 16
++struct xlu__vscsi_target {
++ XLU_Config *cfg;
++ libxl_vscsi_hctl *pdev_hctl;
++ libxl_vscsi_pdev *pdev;
++ char path[PATH_MAX];
++ char udev_path[PATH_MAX];
++ char wwn[XLU_WWN_LEN + 1];
++ unsigned 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.<wwn>/tpgt_1/lun/lun_0/<X>/udev_path
++ */
++static bool xlu__vscsi_compare_udev(struct xlu__vscsi_target *tgt)
++{
++ bool ret;
++ int fd;
++ ssize_t read_sz;
++ libxl_vscsi_hctl udev_hctl;
++
++ libxl_vscsi_hctl_init(&udev_hctl);
++
++ fd = open(tgt->path, O_RDONLY);
++ if (fd < 0){
++ ret = false;
++ goto out;
++ }
++ read_sz = read(fd, &tgt->udev_path, sizeof(tgt->udev_path) - 1);
++ close(fd);
++
++ if (read_sz <= 0 || read_sz > sizeof(tgt->udev_path) - 1) {
++ ret = false;
++ goto out;
++ }
++ tgt->udev_path[read_sz] = '\0';
++ read_sz--;
++ if (tgt->udev_path[read_sz] == '\n')
++ tgt->udev_path[read_sz] = '\0';
++
++ if (xlu__vscsi_parse_dev(tgt->cfg, tgt->udev_path, &udev_hctl)) {
++ ret = false;
++ goto out;
++ }
++ ret = xlu__vscsi_compare_hctl(tgt->pdev_hctl, &udev_hctl);
++
++out:
++ libxl_vscsi_hctl_dispose(&udev_hctl);
++ return ret;
++}
++
++/* /sys/kernel/config/target/xen-pvscsi/naa.<wwn>/tpgt_1/lun/lun_0/<X>/udev_path */
++static bool xlu__vscsi_walk_dir_lun(struct xlu__vscsi_target *tgt)
++{
++ bool found;
++ DIR *dirp;
++ struct dirent *de;
++ size_t path_len = strlen(tgt->path);
++ char *subdir = &tgt->path[path_len];
++
++ dirp = opendir(tgt->path);
++ if (!dirp)
++ return false;
++
++ found = false;
++ while ((de = readdir(dirp))) {
++ if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, ".."))
++ continue;
++
++ snprintf(subdir, sizeof(tgt->path) - path_len, "/%s/udev_path", de->d_name);
++
++ found = xlu__vscsi_compare_udev(tgt);
++ if (found)
++ break;
++
++ *subdir = '\0';
++ }
++ closedir(dirp);
++ return found;
++}
++
++/* /sys/kernel/config/target/xen-pvscsi/naa.<wwn>/tpgt_1/lun/lun_0 */
++static bool xlu__vscsi_walk_dir_luns(struct xlu__vscsi_target *tgt)
++{
++ bool found;
++ DIR *dirp;
++ struct dirent *de;
++ size_t path_len = strlen(tgt->path);
++ char *subdir = &tgt->path[path_len];
++
++ dirp = opendir(tgt->path);
++ if (!dirp)
++ return false;
++
++ found = false;
++ while ((de = readdir(dirp))) {
++ if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, ".."))
++ continue;
++
++ if (sscanf(de->d_name, "lun_%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.<wwn>/tpgt_1 */
++static bool xlu__vscsi_walk_dir_naa(struct xlu__vscsi_target *tgt)
++{
++ bool found;
++ DIR *dirp;
++ struct dirent *de;
++ size_t path_len = strlen(tgt->path);
++ char *subdir = &tgt->path[path_len];
++ unsigned int tpgt;
++
++ dirp = opendir(tgt->path);
++ if (!dirp)
++ return false;
++
++ found = false;
++ while ((de = readdir(dirp))) {
++ if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, ".."))
++ continue;
++
++ if (sscanf(de->d_name, "tpgt_%u", &tpgt) != 1)
++ continue;
++
++ snprintf(subdir, sizeof(tgt->path) - path_len, "/%s/lun", de->d_name);
++
++ found = xlu__vscsi_walk_dir_luns(tgt);
++ if (found)
++ break;
++
++ *subdir = '\0';
++ }
++ closedir(dirp);
++ return found;
++}
++
++/* /sys/kernel/config/target/xen-pvscsi/naa.<wwn> */
++static bool xlu__vscsi_find_target_wwn(struct xlu__vscsi_target *tgt)
++{
++ bool found;
++ DIR *dirp;
++ struct dirent *de;
++ size_t path_len = strlen(tgt->path);
++ char *subdir = &tgt->path[path_len];
++
++ dirp = opendir(tgt->path);
++ if (!dirp)
++ return false;
++
++ found = false;
++ while ((de = readdir(dirp))) {
++ if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, ".."))
++ continue;
++
++ if (sscanf(de->d_name, "naa.%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 <stdlib.h>
++#include <stdio.h>
++
++#include <libxl.h>
++#include <libxl_utils.h>
++#include <libxlutil.h>
++
++#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",
+ "<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",