* local-fate316613-pvscsi-staging-4.4
 git://github.com/olafhering/xen.git : local-fate316613-pvscsi-staging-4.4

Implement pvscsi in xl/libxl
fate#316613 , https://fate.suse.com/316613

10bd594 pvscsi: move parse_vscsi_config code block to avoid fuzz
06914e1 Merge pull request #4 from aaannz/pvscsi
fe65eb3 define SUSE PVSCSI extension for 3rd party use
8a34a98 pvscsi: check null pointer in libxl__add_vscsis
09fa151 pvscsi: avoid double assignment of host devices
aa88928 pvscsi: fix double free in scsi-attach
2de1507 pvscsi: update comments about libxl.so ABI
483f7e9 Merge pull request #3 from aaannz/pvscsi
d98458c Xen4.2 ABI compat
73744a5 preserve Xen4.2 ABI, WIP
0f8e701 fix minor memory leaks
1b1c55d pvscsi: correct comment for DEFINE_DEVICES_ADD
6f50972 pvscsi: move libxl__add_vscsis call
6fd1327 pvscsi: add comment for DEFINE_DEVICES_ADD
e4bf1fd pvscsi: fix DEFINE_DEVICE_REMOVE destroy
51b63a6 pvscsi: man pages
e461042 pvscsi: implememnt single device scsi-detach
540e524 Merge pull request #2 from aaannz/pvscsi
919a851 implement vscsi-attach
e07db68 fix indentation
b087b9d pvscsi: implement simple scsi-detach
824f286 pvscsi: simplify sysfs parsing in parse_vscsi_config
977d81d pvscsi: include stddef in xl_cmdimpl.c to get offsetof
ee2e7e5 Merge pull request #1 from aaannz/pvscsi
7de6f49 support character devices too
c84381b allow /dev/sda as scsi devspec
f11e3a2 pvscsi
--- a/docs/man/xl.cfg.pod.5
+++ b/docs/man/xl.cfg.pod.5
@@ -411,6 +411,36 @@ value is optional if this is a guest dom
 
 =back
 
+=item B<vscsi=[ "VSCSI_SPEC_STRING", "VSCSI_SPEC_STRING", ...]>
+
+Specifies the PVSCSI devices to be provided to the guest. PVSCSI passes
+dom0 SCSI devices as-is to the guest.
+
+Each B<VSCSI_SPEC_STRING> is a mapping from dom0 SCSI devices to guest visible
+SCSI devices, like 'pvdev,vdev[,option]'. Example: '/dev/sdm,3:0:4:5,feature-host'
+
+=over 4
+
+=item C<pdev>
+
+Specifies the dom0 visible SCSI device. The string can be either a device path
+like to a block device like /dev/disk/by-id/scsi-XYZ. Or it can be a device path
+to a char device like /dev/sg5. Or it can be specified in the SCSI notation
+HOST:CHANNEL:TARGET:LUN. Note that the latter format is unreliable because
+the HOST value can change across dom0 reboots.
+
+=item C<vdev>
+
+Specifies how the SCSI device is mapped into the guest. The notation is in
+SCSI notation HOST:CHANNEL:TARGET:LUN. HOST in this case means a virthal
+SCSI host within the guest.
+
+=item C<option>
+
+Right now only one option is recognized: feature-host.
+
+=back
+
 =item B<vfb=[ "VFB_SPEC_STRING", "VFB_SPEC_STRING", ...]>
 
 Specifies the paravirtual framebuffer devices which should be supplied
--- a/docs/man/xl.pod.1
+++ b/docs/man/xl.pod.1
@@ -1208,6 +1208,26 @@ List virtual trusted platform modules fo
 
 =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>.
+Note that the whole virtual SCSI host with all its devices is removed.
+This is a BUG!
+
+=item B<vscsi-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
--- a/tools/libxl/libxl.c
+++ b/tools/libxl/libxl.c
@@ -2021,6 +2021,273 @@ int libxl_devid_to_device_vtpm(libxl_ctx
     return rc;
 }
 
+/******************************************************************************/
+static int libxl__device_from_vscsi(libxl__gc *gc, uint32_t domid,
+                                   libxl_device_vscsi *vscsi,
+                                   libxl__device *device)
+{
+   device->backend_domid   = vscsi->backend_domid;
+   device->devid           = vscsi->devid;
+   device->domid           = domid;
+   device->backend_kind    = LIBXL__DEVICE_KIND_VSCSI;
+   device->kind            = LIBXL__DEVICE_KIND_VSCSI;
+
+   return 0;
+}
+
+void libxl__device_vscsi_add(libxl__egc *egc, uint32_t domid,
+                           libxl_device_vscsi *vscsi,
+                           libxl__ao_device *aodev)
+{
+    STATE_AO_GC(aodev->ao);
+    flexarray_t *front;
+    flexarray_t *back;
+    libxl__device *device;
+    unsigned int rc, i;
+
+    i = 2 * (4 + (3 * vscsi->num_vscsi_devs));
+    front = flexarray_make(gc, 4, 1);
+    back = flexarray_make(gc, i, 1);
+
+    if (vscsi->devid == -1) {
+        rc = ERROR_FAIL;
+        goto out;
+    }
+
+    GCNEW(device);
+    rc = libxl__device_from_vscsi(gc, domid, vscsi, device);
+    if ( rc != 0 ) goto out;
+
+    /* get backend device path to check if is already present */
+    char *backend_path;
+    unsigned int ndirs = 0;
+    backend_path = libxl__device_backend_path(gc, device);
+    if (!libxl__xs_directory(gc, XBT_NULL, backend_path, &ndirs) || !ndirs) {
+        /* backend does not exist => vscsi host does not exist */
+        flexarray_append_pair(back, "frontend-id", GCSPRINTF("%d", domid));
+        flexarray_append_pair(back, "online", "1");
+        flexarray_append_pair(back, "state", "1");
+        flexarray_append_pair(back, "feature-host", GCSPRINTF("%d", !!vscsi->feature_host));
+
+        flexarray_append_pair(front, "backend-id", GCSPRINTF("%d", vscsi->backend_domid));
+        flexarray_append_pair(front, "state", "1");
+    }
+
+    for (i = 0; i < vscsi->num_vscsi_devs; i++) {
+        libxl_vscsi_dev *v = vscsi->vscsi_devs + i;
+        if (ndirs) {
+            unsigned int nb = 0;
+            /* vhost exist, check if not overwriting records */
+            if (libxl__xs_directory(gc, XBT_NULL,
+                   GCSPRINTF("%s/vscsi-devs/dev-%u", backend_path, v->vscsi_dev_id),
+                            &nb) && nb) {
+                /* trigger device removal by forwarding state to XenbusStateClosing */
+                if (v->remove)
+                    flexarray_append_pair(back, GCSPRINTF("vscsi-devs/dev-%u/state", v->vscsi_dev_id), "5");
+                continue;
+            }
+        }
+        flexarray_append_pair(back, GCSPRINTF("vscsi-devs/dev-%u/p-dev", v->vscsi_dev_id),
+                              GCSPRINTF("%u:%u:%u:%u", v->p_hst, v->p_chn, v->p_tgt, v->p_lun));
+        flexarray_append_pair(back, GCSPRINTF("vscsi-devs/dev-%u/v-dev", v->vscsi_dev_id),
+                              GCSPRINTF("%u:%u:%u:%u", vscsi->v_hst, v->v_chn, v->v_tgt, v->v_lun));
+        flexarray_append_pair(back, GCSPRINTF("vscsi-devs/dev-%u/state", v->vscsi_dev_id), "1");
+    }
+
+    aodev->dev = device;
+    if (ndirs == 0) {
+        libxl__device_generic_add(gc, XBT_NULL, device,
+                                libxl__xs_kvs_of_flexarray(gc, back, back->count),
+                                libxl__xs_kvs_of_flexarray(gc, front, front->count),
+                                NULL);
+
+        aodev->action = LIBXL__DEVICE_ACTION_ADD;
+        libxl__wait_device_connection(egc, aodev);
+    }
+    else {
+        /* we added only new vscsi devices, write them and trigger vscsi host reconfiguration */
+        libxl_ctx *ctx = libxl__gc_owner(gc);
+        xs_transaction_t t;
+retry_transaction:
+        t = xs_transaction_start(ctx->xsh);
+        libxl__xs_writev(gc, t, backend_path,
+                         libxl__xs_kvs_of_flexarray(gc, back, back->count));
+        xs_write(ctx->xsh, t, GCSPRINTF("%s/state", backend_path), "7", 2);
+        if(!xs_transaction_end(ctx->xsh, t, 0)) {
+            if (errno == EAGAIN) {
+                goto retry_transaction;
+            }
+            else {
+                LOGE(ERROR, "xs transaction failed");
+                return;
+            }
+        }
+        libxl__wait_for_backend(gc, backend_path, "4");
+
+retry_transaction2:
+        t = xs_transaction_start(ctx->xsh);
+        for (i = 0; i < vscsi->num_vscsi_devs; i++) {
+            libxl_vscsi_dev *v = vscsi->vscsi_devs + i;
+            if (v->remove) {
+                char *tmppath, *tmpval;
+                tmppath = GCSPRINTF("%s/vscsi-devs/dev-%u/state", backend_path, v->vscsi_dev_id);
+                tmpval = libxl__xs_read(gc, t, tmppath);
+                if (tmpval && strcmp(tmpval, "6") == 0) {
+                    tmppath = GCSPRINTF("%s/vscsi-devs/dev-%u/state", backend_path, v->vscsi_dev_id);
+                    xs_rm(ctx->xsh, t, tmppath);
+                    tmppath = GCSPRINTF("%s/vscsi-devs/dev-%u/p-dev", backend_path, v->vscsi_dev_id);
+                    xs_rm(ctx->xsh, t, tmppath);
+                    tmppath = GCSPRINTF("%s/vscsi-devs/dev-%u/v-dev", backend_path, v->vscsi_dev_id);
+                    xs_rm(ctx->xsh, t, tmppath);
+                    tmppath = GCSPRINTF("%s/vscsi-devs/dev-%u", backend_path, v->vscsi_dev_id);
+                    xs_rm(ctx->xsh, t, tmppath);
+                } else {
+                    LOGE(ERROR, "%s: %s has %s, expected 6", __func__, tmppath, tmpval);
+                }
+            }
+        }
+        if(!xs_transaction_end(ctx->xsh, t, 0)) {
+            if (errno == EAGAIN) {
+                goto retry_transaction2;
+            }
+            else {
+                LOGE(ERROR, "xs transaction failed");
+                return;
+            }
+        }
+        /* as we are not adding new device, skip waiting for it */
+        libxl__ao_complete(egc, aodev->ao, 0);
+    }
+
+    rc = 0;
+out:
+    aodev->rc = rc;
+    if(rc) aodev->callback(egc, aodev);
+    return;
+}
+
+libxl_device_vscsi *libxl_device_vscsi_list(libxl_ctx *ctx, uint32_t domid, int *num)
+{
+    GC_INIT(ctx);
+
+    libxl_device_vscsi *vscsi_hosts = NULL;
+    char *fe_path;
+    char **dir;
+    unsigned int ndirs = 0;
+
+    fe_path = libxl__sprintf(gc, "%s/device/vscsi", libxl__xs_get_dompath(gc, domid));
+    dir = libxl__xs_directory(gc, XBT_NULL, fe_path, &ndirs);
+    if (dir && ndirs) {
+        libxl_device_vscsi *vscsi, *end;
+        vscsi_hosts = calloc(ndirs, sizeof(*vscsi_hosts));
+        for (vscsi = vscsi_hosts, end = vscsi_hosts + ndirs; vscsi_hosts && vscsi < end; ++vscsi, ++dir) {
+            unsigned int vd_dirs = 0, i;
+            char *tmp;
+            char **vscsi_devs_dir;
+            const char *vscsi_devs_path, *be_path = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/%s/backend", fe_path, *dir));
+
+            libxl_device_vscsi_init(vscsi);
+
+            vscsi->devid = atoi(*dir);
+
+            tmp = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/%s/backend-id", fe_path, *dir));
+            vscsi->backend_domid = atoi(tmp);
+
+            vscsi_devs_path = libxl__sprintf(gc, "%s/vscsi-devs", be_path);
+            vscsi_devs_dir = libxl__xs_directory(gc, XBT_NULL, vscsi_devs_path, &vd_dirs);
+            if (vscsi_devs_dir && vd_dirs) {
+                vscsi->vscsi_devs = calloc(vd_dirs, sizeof(*vscsi->vscsi_devs));
+                vscsi->num_vscsi_devs = vd_dirs;
+                for (i = 0; i < vd_dirs; i++, vscsi_devs_dir++) {
+                    unsigned int vscsi_dev_id;
+                    if (sscanf(*vscsi_devs_dir, "dev-%u", &vscsi_dev_id) != 1) {
+                        LIBXL__LOG(ctx, LIBXL__LOG_ERROR, "%s/scsi-devs/%s failed to parse", be_path, *vscsi_devs_dir);
+                        continue;
+                    }
+                    vscsi->vscsi_devs[i].vscsi_dev_id = vscsi_dev_id;
+                    tmp = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/vscsi-devs/dev-%u/p-dev", be_path, vscsi_dev_id));
+                    if (tmp)
+                       sscanf(tmp, "%u:%u:%u:%u", &vscsi->vscsi_devs[i].p_hst, &vscsi->vscsi_devs[i].p_chn, &vscsi->vscsi_devs[i].p_tgt, &vscsi->vscsi_devs[i].p_lun);
+                    tmp = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/vscsi-devs/dev-%u/v-dev", be_path, vscsi_dev_id));
+                    if (tmp)
+                        sscanf(tmp, "%u:%u:%u:%u", &vscsi->v_hst, &vscsi->vscsi_devs[i].v_chn, &vscsi->vscsi_devs[i].v_tgt, &vscsi->vscsi_devs[i].v_lun);
+                }
+            }
+        }
+    }
+    *num = ndirs;
+
+    GC_FREE;
+    return vscsi_hosts;
+}
+
+int libxl_device_vscsi_getinfo(libxl_ctx *ctx,
+                              uint32_t domid,
+                              libxl_device_vscsi *vscsi_host,
+                              libxl_vscsi_dev *vscsi_dev,
+                              libxl_vscsiinfo *vscsiinfo)
+{
+    GC_INIT(ctx);
+    char *dompath, *vscsipath;
+    char *val;
+    int rc = 0;
+
+    libxl_vscsiinfo_init(vscsiinfo);
+    dompath = libxl__xs_get_dompath(gc, domid);
+    vscsiinfo->devid = vscsi_host->devid;
+    vscsiinfo->p_hst = vscsi_dev->p_hst;
+    vscsiinfo->p_chn = vscsi_dev->p_chn;
+    vscsiinfo->p_tgt = vscsi_dev->p_tgt;
+    vscsiinfo->p_lun = vscsi_dev->p_lun;
+    vscsiinfo->v_hst = vscsi_host->v_hst;
+    vscsiinfo->v_chn = vscsi_dev->v_chn;
+    vscsiinfo->v_tgt = vscsi_dev->v_tgt;
+    vscsiinfo->v_lun = vscsi_dev->v_lun;
+
+    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 err;
+    }
+    if(!libxl__xs_read(gc, XBT_NULL, vscsiinfo->backend)) {
+       goto err;
+    }
+
+    val = libxl__xs_read(gc, XBT_NULL,
+          GCSPRINTF("%s/backend-id", vscsipath));
+    vscsiinfo->backend_id = val ? strtoul(val, NULL, 10) : -1;
+
+    val = libxl__xs_read(gc, XBT_NULL,
+          GCSPRINTF("%s/state", vscsipath));
+    vscsiinfo->vscsi_host_state = val ? strtoul(val, NULL, 10) : -1;
+
+    val = libxl__xs_read(gc, XBT_NULL,
+          GCSPRINTF("%s/event-channel", vscsipath));
+    vscsiinfo->evtch = val ? strtoul(val, NULL, 10) : -1;
+
+    val = libxl__xs_read(gc, XBT_NULL,
+          GCSPRINTF("%s/ring-ref", vscsipath));
+    vscsiinfo->rref = val ? strtoul(val, NULL, 10) : -1;
+
+    vscsiinfo->frontend = xs_read(ctx->xsh, XBT_NULL,
+          GCSPRINTF("%s/frontend", vscsiinfo->backend), NULL);
+
+    val = libxl__xs_read(gc, XBT_NULL,
+          GCSPRINTF("%s/frontend-id", vscsiinfo->backend));
+    vscsiinfo->frontend_id = val ? strtoul(val, NULL, 10) : -1;
+
+    val = libxl__xs_read(gc, XBT_NULL,
+          GCSPRINTF("%s/vscsi-devs/dev-%u/state", vscsiinfo->backend, vscsi_dev->vscsi_dev_id));
+    vscsiinfo->vscsi_dev_state = val ? strtoul(val, NULL, 10) : -1;
+
+    goto exit;
+err:
+    rc = ERROR_FAIL;
+exit:
+    GC_FREE;
+    return rc;
+}
 
 /******************************************************************************/
 
@@ -3489,6 +3756,8 @@ out:
  * libxl_device_vkb_destroy
  * libxl_device_vfb_remove
  * libxl_device_vfb_destroy
+ * libxl_device_vscsi_remove
+ * libxl_device_vscsi_destroy
  */
 #define DEFINE_DEVICE_REMOVE(type, removedestroy, f)                    \
     int libxl_device_##type##_##removedestroy(libxl_ctx *ctx,           \
@@ -3540,6 +3809,10 @@ DEFINE_DEVICE_REMOVE(vfb, destroy, 1)
 DEFINE_DEVICE_REMOVE(vtpm, remove, 0)
 DEFINE_DEVICE_REMOVE(vtpm, destroy, 1)
 
+/* vscsi */
+DEFINE_DEVICE_REMOVE(vscsi, remove, 0)
+DEFINE_DEVICE_REMOVE(vscsi, destroy, 1)
+
 #undef DEFINE_DEVICE_REMOVE
 
 /******************************************************************************/
@@ -3549,6 +3822,7 @@ DEFINE_DEVICE_REMOVE(vtpm, destroy, 1)
  * libxl_device_disk_add
  * libxl_device_nic_add
  * libxl_device_vtpm_add
+ * libxl_device_vscsi_add
  */
 
 #define DEFINE_DEVICE_ADD(type)                                         \
@@ -3578,6 +3852,9 @@ DEFINE_DEVICE_ADD(nic)
 /* vtpm */
 DEFINE_DEVICE_ADD(vtpm)
 
+/* vscsi */
+DEFINE_DEVICE_ADD(vscsi)
+
 #undef DEFINE_DEVICE_ADD
 
 /******************************************************************************/
@@ -5689,6 +5966,20 @@ int libxl_fd_set_cloexec(libxl_ctx *ctx,
 int libxl_fd_set_nonblock(libxl_ctx *ctx, int fd, int nonblock)
   { return fd_set_flags(ctx,fd, F_GETFL,F_SETFL,"FL", O_NONBLOCK, nonblock); }
 
+/* libxl.so.4.4 ABI compatilibity hack - do not do this at home */
+static libxl_device_vscsi_suse* libxl__global_vscsi_list_suse;
+
+libxl_device_vscsi_suse* libxl_get_vscsi_devices_suse(void)
+{
+    return libxl__global_vscsi_list_suse;
+}
+
+void libxl_set_vscsi_devices_suse(libxl_device_vscsi_suse* vscsi_host)
+{
+    libxl__global_vscsi_list_suse = vscsi_host;
+}
+/* EOF libxl.so.4.4 ABI compat hack */
+
 /*
  * Local variables:
  * mode: C
--- a/tools/libxl/libxl.h
+++ b/tools/libxl/libxl.h
@@ -966,6 +966,26 @@ libxl_device_vtpm *libxl_device_vtpm_lis
 int libxl_device_vtpm_getinfo(libxl_ctx *ctx, uint32_t domid,
                                libxl_device_vtpm *vtpm, libxl_vtpminfo *vtpminfo);
 
+/* Virtual SCSI */
+int libxl_device_vscsi_add(libxl_ctx *ctx, uint32_t domid, libxl_device_vscsi *vscsi,
+                          const libxl_asyncop_how *ao_how)
+                          LIBXL_EXTERNAL_CALLERS_ONLY;
+int libxl_device_vscsi_remove(libxl_ctx *ctx, uint32_t domid,
+                            libxl_device_vscsi *vscsi,
+                            const libxl_asyncop_how *ao_how)
+                            LIBXL_EXTERNAL_CALLERS_ONLY;
+int libxl_device_vscsi_destroy(libxl_ctx *ctx, uint32_t domid,
+                              libxl_device_vscsi *vscsi,
+                              const libxl_asyncop_how *ao_how)
+                              LIBXL_EXTERNAL_CALLERS_ONLY;
+
+libxl_device_vscsi *libxl_device_vscsi_list(libxl_ctx *ctx, uint32_t domid, int *num);
+int libxl_device_vscsi_getinfo(libxl_ctx *ctx,
+                              uint32_t domid,
+                              libxl_device_vscsi *vscsi_host,
+                              libxl_vscsi_dev *vscsi_dev,
+                              libxl_vscsiinfo *vscsiinfo);
+
 /* Keyboard */
 int libxl_device_vkb_add(libxl_ctx *ctx, uint32_t domid, libxl_device_vkb *vkb,
                          const libxl_asyncop_how *ao_how)
@@ -1181,6 +1201,27 @@ int libxl_fd_set_nonblock(libxl_ctx *ctx
 
 #include <libxl_event.h>
 
+/* libxl.so.4.4 ABI compatilibity hack - do not do this at home */
+
+/*
+ * LIBXL_HAVE_DEVICE_PVSCSI_SUSE
+ *
+ * If this is defined, set pvscsi devices through helpers
+ * libxl_get_vscsi_devices_suse and libxl_set_vscsi_devices_suse.
+ *
+ * If this is not defined, pvscsi is either unavailable or available through
+ * libxl_domain_config structure
+ */
+#define LIBXL_HAVE_DEVICE_PVSCSI_SUSE 1
+typedef struct {
+    libxl_device_vscsi* vscsi_devices;
+    int num_vscsi_devices;
+} libxl_device_vscsi_suse;
+
+libxl_device_vscsi_suse* libxl_get_vscsi_devices_suse(void);
+void libxl_set_vscsi_devices_suse(libxl_device_vscsi_suse* vscsi_host);
+/* EOF libxl.so.4.4 ABI compat hack */
+
 #endif /* LIBXL_H */
 
 /*
--- a/tools/libxl/libxl_create.c
+++ b/tools/libxl/libxl_create.c
@@ -1048,6 +1048,7 @@ static void domcreate_rebuild_done(libxl
     libxl__multidev_begin(ao, &dcs->multidev);
     dcs->multidev.callback = domcreate_launch_dm;
     libxl__add_disks(egc, ao, domid, d_config, &dcs->multidev);
+    libxl__add_vscsis(egc, ao, domid, d_config, &dcs->multidev);
     libxl__multidev_prepared(egc, &dcs->multidev, 0);
 
     return;
--- a/tools/libxl/libxl_device.c
+++ b/tools/libxl/libxl_device.c
@@ -523,6 +523,7 @@ void libxl__multidev_prepared(libxl__egc
  * The following functions are defined:
  * libxl__add_disks
  * libxl__add_nics
+ * libxl__add_vscsis
  * libxl__add_vtpms
  */
 
@@ -542,10 +543,32 @@ void libxl__multidev_prepared(libxl__egc
 
 DEFINE_DEVICES_ADD(disk)
 DEFINE_DEVICES_ADD(nic)
+// DEFINE_DEVICES_ADD(vscsi)
 DEFINE_DEVICES_ADD(vtpm)
 
 #undef DEFINE_DEVICES_ADD
 
+// to preserve libxl.so.4.4 ABI custom define libxl__add_vscsi
+void libxl__add_vscsis(libxl__egc *egc, libxl__ao *ao, uint32_t domid,
+                      libxl_domain_config *d_config,
+                      libxl__multidev *multidev)
+{
+    AO_GC;
+    int i;
+
+    libxl_device_vscsi *vhosts;
+    int num_vhosts;
+    libxl_device_vscsi_suse* vscsi_hosts_suse = libxl_get_vscsi_devices_suse();
+    if (!vscsi_hosts_suse)
+        return;
+    num_vhosts = vscsi_hosts_suse->num_vscsi_devices;
+    vhosts = vscsi_hosts_suse->vscsi_devices;
+    for (i = 0; i < num_vhosts; i++) {
+        libxl__ao_device *aodev = libxl__multidev_prepare(multidev);
+        libxl__device_vscsi_add(egc, domid, vhosts+i, aodev);
+    }
+}
+
 /******************************************************************************/
 
 int libxl__device_destroy(libxl__gc *gc, libxl__device *dev)
--- a/tools/libxl/libxl_internal.h
+++ b/tools/libxl/libxl_internal.h
@@ -982,6 +982,7 @@ _hidden int libxl__device_disk_setdefaul
 _hidden int libxl__device_nic_setdefault(libxl__gc *gc, libxl_device_nic *nic,
                                          uint32_t domid);
 _hidden int libxl__device_vtpm_setdefault(libxl__gc *gc, libxl_device_vtpm *vtpm);
+_hidden int libxl__device_vscsi_setdefault(libxl__gc *gc, libxl_device_vscsi *vscsi);
 _hidden int libxl__device_vfb_setdefault(libxl__gc *gc, libxl_device_vfb *vfb);
 _hidden int libxl__device_vkb_setdefault(libxl__gc *gc, libxl_device_vkb *vkb);
 _hidden int libxl__device_pci_setdefault(libxl__gc *gc, libxl_device_pci *pci);
@@ -2082,6 +2083,10 @@ _hidden void libxl__device_vtpm_add(libx
                                    libxl_device_vtpm *vtpm,
                                    libxl__ao_device *aodev);
 
+_hidden void libxl__device_vscsi_add(libxl__egc *egc, uint32_t domid,
+                                   libxl_device_vscsi *vscsi,
+                                   libxl__ao_device *aodev);
+
 /* Internal function to connect a vkb device */
 _hidden int libxl__device_vkb_add(libxl__gc *gc, uint32_t domid,
                                   libxl_device_vkb *vkb);
@@ -2522,6 +2527,10 @@ _hidden void libxl__add_vtpms(libxl__egc
                              libxl_domain_config *d_config,
                              libxl__multidev *multidev);
 
+_hidden void libxl__add_vscsis(libxl__egc *egc, libxl__ao *ao, uint32_t domid,
+                             libxl_domain_config *d_config,
+                             libxl__multidev *multidev);
+
 /*----- device model creation -----*/
 
 /* First layer; wraps libxl__spawn_spawn. */
--- a/tools/libxl/libxl_types.idl
+++ b/tools/libxl/libxl_types.idl
@@ -456,6 +456,26 @@ libxl_device_vtpm = Struct("device_vtpm"
     ("uuid",             libxl_uuid),
 ])
 
+libxl_vscsi_dev = Struct("vscsi_dev", [
+    ("vscsi_dev_id",     libxl_devid),
+    ("remove",           bool),
+    ("p_hst",            uint32),
+    ("p_chn",            uint32),
+    ("p_tgt",            uint32),
+    ("p_lun",            uint32),
+    ("v_chn",            uint32),
+    ("v_tgt",            uint32),
+    ("v_lun",            uint32),
+    ])
+
+libxl_device_vscsi = Struct("device_vscsi", [
+    ("backend_domid",    libxl_domid),
+    ("devid",            libxl_devid),
+    ("v_hst",            uint32),
+    ("vscsi_devs",       Array(libxl_vscsi_dev, "num_vscsi_devs")),
+    ("feature_host",     bool),
+    ])
+
 libxl_domain_config = Struct("domain_config", [
     ("c_info", libxl_domain_create_info),
     ("b_info", libxl_domain_build_info),
@@ -466,6 +486,8 @@ libxl_domain_config = Struct("domain_con
     ("vfbs", Array(libxl_device_vfb, "num_vfbs")),
     ("vkbs", Array(libxl_device_vkb, "num_vkbs")),
     ("vtpms", Array(libxl_device_vtpm, "num_vtpms")),
+# preserve libxl.so.4.4 ABI
+#   ("vscsis", Array(libxl_device_vscsi, "num_vscsis")),
 
     ("on_poweroff", libxl_action_on_shutdown),
     ("on_reboot", libxl_action_on_shutdown),
@@ -508,6 +530,28 @@ libxl_vtpminfo = Struct("vtpminfo", [
     ("uuid", libxl_uuid),
     ], dir=DIR_OUT)
 
+libxl_vscsiinfo = Struct("vscsiinfo", [
+    ("backend", string),
+    ("backend_id", uint32),
+    ("frontend", string),
+    ("frontend_id", uint32),
+    ("devid", libxl_devid),
+    ("p_hst", uint32),
+    ("p_chn", uint32),
+    ("p_tgt", uint32),
+    ("p_lun", uint32),
+    ("vscsi_dev_id", libxl_devid),
+    ("v_hst", uint32),
+    ("v_chn", uint32),
+    ("v_tgt", uint32),
+    ("v_lun", uint32),
+    ("feature_host", bool),
+    ("vscsi_host_state", integer),
+    ("vscsi_dev_state", integer),
+    ("evtch", integer),
+    ("rref", integer),
+    ], dir=DIR_OUT)
+
 libxl_vcpuinfo = Struct("vcpuinfo", [
     ("vcpuid", uint32),
     ("cpu", uint32),
--- a/tools/libxl/libxl_types_internal.idl
+++ b/tools/libxl/libxl_types_internal.idl
@@ -20,6 +20,7 @@ libxl__device_kind = Enumeration("device
     (6, "VKBD"),
     (7, "CONSOLE"),
     (8, "VTPM"),
+    (9, "VSCSI"),
     ])
 
 libxl__console_backend = Enumeration("console_backend", [
--- a/tools/libxl/xl.h
+++ b/tools/libxl/xl.h
@@ -81,6 +81,9 @@ int main_networkdetach(int argc, char **
 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);
--- a/tools/libxl/xl_cmdimpl.c
+++ b/tools/libxl/xl_cmdimpl.c
@@ -17,6 +17,7 @@
 #include "libxl_osdeps.h"
 
 #include <stdio.h>
+#include <stddef.h>
 #include <stdlib.h>
 #include <string.h>
 #include <unistd.h>
@@ -34,6 +35,7 @@
 #include <ctype.h>
 #include <inttypes.h>
 #include <limits.h>
+#include <dirent.h>
 
 #include "libxl.h"
 #include "libxl_utils.h"
@@ -544,6 +546,122 @@ static void parse_vif_rate(XLU_Config **
     }
 }
 
+static char *vscsi_trim_string(char *s)
+{
+    unsigned int len;
+
+    while (isspace(*s))
+        s++;
+    len = strlen(s);
+    while (len-- > 1 && isspace(s[len]))
+        s[len] = '\0';
+    return s;
+}
+
+static void parse_vscsi_config(libxl_device_vscsi *vscsi_host,
+                              libxl_vscsi_dev *vscsi_dev,
+                              char *buf)
+{
+    char *pdev, *vdev, *fhost;
+    unsigned int hst, chn, tgt, lun;
+
+    libxl_device_vscsi_init(vscsi_host);
+    pdev = strtok(buf, ",");
+    vdev = strtok(NULL, ",");
+    fhost = strtok(NULL, ",");
+    if (!(pdev && vdev)) {
+        fprintf(stderr, "invalid vscsi= devspec: '%s'\n", buf);
+        exit(1);
+    }
+
+    pdev = vscsi_trim_string(pdev);
+    vdev = vscsi_trim_string(vdev);
+
+    if (strncmp(pdev, "/dev/", 5) == 0) {
+#ifdef __linux__
+        struct stat pdev_stat;
+        char pdev_sysfs_path[PATH_MAX];
+        const char *type;
+        int result = 0;
+        DIR *dirp;
+        struct dirent *de;
+
+        /* stat pdev to get device's sysfs entry */
+        if (stat (pdev, &pdev_stat) == -1) {
+            fprintf(stderr, "vscsi: invalid %s '%s' in vscsi= devspec: '%s', device not found or cannot be read\n", "pdev", pdev, buf);
+            exit(1);
+        }
+        if (S_ISBLK (pdev_stat.st_mode)) {
+            type = "block";
+        } else if (S_ISCHR (pdev_stat.st_mode)) {
+            type = "char";
+        } else {
+            fprintf(stderr, "vscsi: invalid %s '%s' in vscsi= devspec: '%s', not a valid block or char device\n", "pdev", pdev, buf);
+            exit(1);
+        }
+
+        /* get pdev scsi address - subdir of scsi_device sysfs entry */
+        snprintf(pdev_sysfs_path, sizeof(pdev_sysfs_path), "/sys/dev/%s/%u:%u/device/scsi_device",
+                type,
+                major(pdev_stat.st_rdev),
+                minor(pdev_stat.st_rdev));
+
+        dirp = opendir(pdev_sysfs_path);
+        if (!dirp) {
+            fprintf(stderr, "vscsi: invalid %s '%s' in vscsi= devspec: '%s', cannot find scsi device\n", "pdev", pdev, buf);
+            exit(1);
+        }
+
+        while ((de = readdir(dirp))) {
+            if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, ".."))
+                continue;
+
+            if (sscanf(de->d_name, "%u:%u:%u:%u", &hst, &chn, &tgt, &lun) != 4) {
+                fprintf(stderr, "vscsi: ignoring unknown devspec '%s' for device '%s'\n",
+                        de->d_name, pdev);
+                continue;
+            }
+            result = 1;
+            break;
+        }
+        closedir(dirp);
+
+        if (!result) {
+            fprintf(stderr, "vscsi: invalid %s '%s' in vscsi= devspec: '%s', cannot find scsi device in sysfs\n", "pdev", pdev, buf);
+            exit(1);
+        }
+#else
+        fprintf(stderr, "vscsi: invalid %s '%s' in vscsi= devspec: '%s', expecting hst:chn:tgt:lun\n", "pdev", pdev, buf);
+        exit(1);
+#endif
+    } else if (sscanf(pdev, "%u:%u:%u:%u", &hst, &chn, &tgt, &lun) != 4) {
+        fprintf(stderr, "vscsi: invalid %s '%s' in vscsi= devspec: '%s', expecting hst:chn:tgt:lun\n", "pdev", pdev, buf);
+        exit(1);
+    }
+    vscsi_dev->p_hst = hst;
+    vscsi_dev->p_chn = chn;
+    vscsi_dev->p_tgt = tgt;
+    vscsi_dev->p_lun = lun;
+
+    if (sscanf(vdev, "%u:%u:%u:%u", &hst, &chn, &tgt, &lun) != 4) {
+        fprintf(stderr, "vscsi: invalid %s '%s' in vscsi= devspec: '%s', expecting hst:chn:tgt:lun\n", "vdev", vdev, buf);
+        exit(1);
+    }
+    vscsi_host->v_hst = hst;
+    vscsi_dev->v_chn = chn;
+    vscsi_dev->v_tgt = tgt;
+    vscsi_dev->v_lun = lun;
+
+    if (fhost) {
+        fhost = vscsi_trim_string(fhost);
+        vscsi_host->feature_host = strcmp(fhost, "feature-host") == 0;
+        if (!vscsi_host->feature_host) {
+            fprintf(stderr, "vscsi: invalid option '%s' in vscsi= devspec: '%s', expecting %s\n", fhost, buf, "feature-host");
+            exit(1);
+        }
+    }
+}
+
 static void split_string_into_string_list(const char *str,
                                           const char *delim,
                                           libxl_string_list *psl)
@@ -763,7 +881,7 @@ static void parse_config_data(const char
     const char *buf;
     long l;
     XLU_Config *config;
-    XLU_ConfigList *cpus, *vbds, *nics, *pcis, *cvfbs, *cpuids, *vtpms;
+    XLU_ConfigList *cpus, *vbds, *nics, *pcis, *cvfbs, *cpuids, *vtpms, *vscsis;
     XLU_ConfigList *ioports, *irqs, *iomem;
     int num_ioports, num_irqs, num_iomem;
     int pci_power_mgmt = 0;
@@ -1255,6 +1373,66 @@ static void parse_config_data(const char
         }
     }
 
+    if (!xlu_cfg_get_list(config, "vscsi", &vscsis, 0, 0)) {
+        int cnt_vscsi_devs = 0;
+
+        /*
+         * to preserve libxl.so.4.4 ABI, do not store vscsis to d_config
+         * we are using extra array for that
+         */
+        libxl_device_vscsi* vscsi_hosts = calloc(1, sizeof(libxl_device_vscsi));
+        int num_vscsi_hosts = 0;
+
+        while ((buf = xlu_cfg_get_listitem (vscsis, cnt_vscsi_devs)) != NULL) {
+            libxl_vscsi_dev vscsi_dev = { };
+            libxl_device_vscsi vscsi_host = { };
+            libxl_device_vscsi *host;
+            char *tmp_buf;
+            int num_vscsis, host_found = 0;
+
+            /*
+             * #1: parse the devspec and place it in temporary host+dev part
+             * #2: find existing vscsi_host with number v_hst
+             *     if found, append the vscsi_dev to this vscsi_host
+             * #3: otherwise, create new vscsi_host and append vscsi_dev
+             * Note: v_hst does not represent the index named "num_vscsis",
+             *       it is a private index used just in the config file
+             */
+            tmp_buf = strdup(buf);
+            parse_vscsi_config(&vscsi_host, &vscsi_dev, tmp_buf);
+            free(tmp_buf);
+
+            if (num_vscsi_hosts) {
+                for (num_vscsis = 0; num_vscsis < num_vscsi_hosts; num_vscsis++) {
+                    if (vscsi_hosts[num_vscsis].v_hst == vscsi_host.v_hst) {
+                        host = vscsi_hosts + num_vscsis;
+                        host->vscsi_devs = realloc(host->vscsi_devs, sizeof(libxl_vscsi_dev) * (host->num_vscsi_devs + 1));
+                        vscsi_dev.vscsi_dev_id = host->num_vscsi_devs;
+                        memcpy(host->vscsi_devs + host->num_vscsi_devs, &vscsi_dev, sizeof(vscsi_dev));
+                        host->num_vscsi_devs++;
+                        host_found = 1;
+                    break;
+                    }
+                }
+            }
+            if (!host_found || !num_vscsi_hosts) {
+                vscsi_hosts = realloc(vscsi_hosts, sizeof(libxl_device_vscsi) * (num_vscsi_hosts + 1));
+                vscsi_host.vscsi_devs = malloc(sizeof(libxl_vscsi_dev));
+                vscsi_dev.vscsi_dev_id = 0;
+                memcpy(vscsi_host.vscsi_devs, &vscsi_dev, sizeof(vscsi_dev));
+                vscsi_host.num_vscsi_devs++;
+                vscsi_host.devid = num_vscsi_hosts;
+                memcpy(vscsi_hosts + num_vscsi_hosts, &vscsi_host, sizeof(vscsi_host));
+                num_vscsi_hosts++;
+            }
+            cnt_vscsi_devs++;
+        }
+        libxl_device_vscsi_suse* vscsis_suse = calloc(1, sizeof(libxl_device_vscsi_suse));
+        vscsis_suse->vscsi_devices = vscsi_hosts;
+        vscsis_suse->num_vscsi_devices = num_vscsi_hosts;
+        libxl_set_vscsi_devices_suse(vscsis_suse);
+    }
+
     if (!xlu_cfg_get_list(config, "vtpm", &vtpms, 0, 0)) {
         d_config->num_vtpms = 0;
         d_config->vtpms = NULL;
@@ -6050,6 +6228,256 @@ int main_blockdetach(int argc, char **ar
     return rc;
 }
 
+int main_vscsiattach(int argc, char **argv)
+{
+    int opt;
+    int num_hosts, i = 0, found_host = -1, res = 0;
+    libxl_vscsi_dev *vscsi_dev;
+    libxl_device_vscsi *vscsi_host, *vscsi_tmphst;
+    libxl_device_vscsi *vscsi_hosts_existing = NULL;
+    uint32_t domid;
+    char *tmp_buf, *feat_buf = NULL;
+
+    SWITCH_FOREACH_OPT(opt, "", NULL, "scsi-attach", 1) {
+        /* No options */
+    }
+
+    if (argc < 4) {
+        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 < 4) {
+        fprintf(stderr, "scsi-attach: 3 options required.\n");
+        return 1;
+    }
+    if (argc == 5) {
+        if (asprintf(&feat_buf, ",%s", argv[4]) < 0) {
+        perror("asprintf");
+        return 1;
+        }
+    }
+    if (asprintf(&tmp_buf, "%s,%s%s", argv[2], argv[3], feat_buf ?: "") < 0) {
+        perror("asprintf");
+        return 1;
+    }
+
+    vscsi_dev = calloc(1, sizeof(*vscsi_dev));
+    vscsi_host = calloc(1, sizeof(*vscsi_host));
+    if (!(vscsi_dev && vscsi_host)) {
+        fprintf(stderr, "%s ENOMEM\n", __func__);
+        res = 1;
+        goto vscsi_attach_out;
+    }
+    parse_vscsi_config(vscsi_host, vscsi_dev, tmp_buf);
+
+    /* look for existing vscsi_host for given domain */
+    vscsi_hosts_existing = libxl_device_vscsi_list(ctx, domid, &num_hosts);
+    if (vscsi_hosts_existing) {
+        for (i = 0; i < num_hosts; ++i) {
+            int j;
+            for (j = 0; j < vscsi_hosts_existing[i].num_vscsi_devs; j++) {
+                if (vscsi_hosts_existing[i].vscsi_devs[j].p_hst == vscsi_dev->p_hst &&
+                    vscsi_hosts_existing[i].vscsi_devs[j].p_chn == vscsi_dev->p_chn &&
+                    vscsi_hosts_existing[i].vscsi_devs[j].p_tgt == vscsi_dev->p_tgt &&
+                    vscsi_hosts_existing[i].vscsi_devs[j].p_lun == vscsi_dev->p_lun) {
+                    fprintf(stderr, "Host device '%u:%u:%u:%u' is already in use"
+                            " by guest vscsi specification '%u:%u:%u:%u'.\n",
+                            vscsi_dev->p_hst, vscsi_dev->p_chn, vscsi_dev->p_tgt, vscsi_dev->p_lun,
+                            vscsi_hosts_existing[i].v_hst, vscsi_dev->v_chn, vscsi_dev->v_tgt, vscsi_dev->v_lun);
+                    res = 1;
+                    goto vscsi_attach_out;
+                }
+            }
+            if (vscsi_host->v_hst == vscsi_hosts_existing[i].v_hst) {
+                found_host = i;
+                break;
+            }
+        }
+    }
+    if (found_host == -1) {
+        vscsi_host->devid = i;
+        vscsi_host->vscsi_devs = vscsi_dev;
+        vscsi_host->num_vscsi_devs = 1;
+        vscsi_tmphst = vscsi_host;
+    } else {
+        /* look if the vdev address is not taken */
+        vscsi_tmphst = vscsi_hosts_existing + found_host;
+        for (i = 0; i < vscsi_tmphst->num_vscsi_devs; ++i) {
+            if (vscsi_tmphst->vscsi_devs[i].v_chn == vscsi_dev->v_chn &&
+                vscsi_tmphst->vscsi_devs[i].v_tgt == vscsi_dev->v_tgt &&
+                vscsi_tmphst->vscsi_devs[i].v_lun == vscsi_dev->v_lun) {
+                fprintf(stderr, "Target vscsi specification '%u:%u:%u:%u' is already taken\n",
+                        vscsi_tmphst->v_hst, vscsi_dev->v_chn,
+                        vscsi_dev->v_tgt, vscsi_dev->v_lun);
+                res = 1;
+                goto vscsi_attach_out;
+            }
+        }
+        vscsi_tmphst->vscsi_devs = realloc(vscsi_tmphst->vscsi_devs,
+                                         sizeof(libxl_vscsi_dev) *
+                                         (vscsi_tmphst->num_vscsi_devs + 1));
+        if (vscsi_tmphst->vscsi_devs == NULL) {
+            fprintf(stderr, "%s ENOMEM\n", __func__);
+            res = 1;
+            goto vscsi_attach_out;
+        }
+        vscsi_dev->vscsi_dev_id = vscsi_tmphst->num_vscsi_devs;
+        memcpy(vscsi_tmphst->vscsi_devs + vscsi_tmphst->num_vscsi_devs,
+               vscsi_dev, sizeof(*vscsi_dev));
+        vscsi_tmphst->num_vscsi_devs++;
+    }
+
+    if (dryrun_only) {
+        char* json = libxl_device_vscsi_to_json(ctx, vscsi_tmphst);
+        printf("vscsi: %s\n", json);
+        free(json);
+        if (ferror(stdout) || fflush(stdout)) { perror("stdout"); exit(-1); }
+    }
+    else if (libxl_device_vscsi_add(ctx, domid, vscsi_tmphst, 0)) {
+        fprintf(stderr, "libxl_device_vscsi_add failed.\n");
+        res = 1;
+    }
+
+vscsi_attach_out:
+    if (vscsi_hosts_existing) {
+        for (i = 0; i < num_hosts; ++i)
+            libxl_device_vscsi_dispose(vscsi_hosts_existing+i);
+        free(vscsi_hosts_existing);
+    }
+    free(vscsi_host);
+    free(vscsi_dev);
+    free(tmp_buf);
+    free(feat_buf);
+    return res;
+}
+
+int main_vscsilist(int argc, char **argv)
+{
+    int opt;
+    libxl_device_vscsi *vscsi_hosts;
+    libxl_vscsiinfo vscsiinfo;
+    int num_hosts, h, d;
+
+    SWITCH_FOREACH_OPT(opt, "", NULL, "scsi-list", 1) {
+        /* No options */
+    }
+    if (argc < 2) {
+        help("scsi-list");
+        return 1;
+    }
+    /*      Idx  BE  state host p_hst v_hst state */
+    printf("%-3s %-3s %-5s %-5s %-10s %-10s %-5s\n",
+           "Idx", "BE", "state", "host", "phy-hctl", "vir-hctl", "devstate");
+    for (argv += optind, argc -= optind; argc > 0; --argc, ++argv) {
+        uint32_t domid;
+        if (libxl_domain_qualifier_to_domid(ctx, *argv, &domid) < 0) {
+            fprintf(stderr, "%s is an invalid domain identifier\n", *argv);
+            continue;
+        }
+        if (!(vscsi_hosts = libxl_device_vscsi_list(ctx, domid, &num_hosts))) {
+            continue;
+        }
+        for (h = 0; h < num_hosts; ++h) {
+           for (d = 0; d < vscsi_hosts[h].num_vscsi_devs; d++) {
+               if (!libxl_device_vscsi_getinfo(ctx, domid, &vscsi_hosts[h], &vscsi_hosts[h].vscsi_devs[d], &vscsiinfo)) {
+                   char pdev[64], vdev[64];
+                   snprintf(pdev, sizeof(pdev), "%u:%u:%u:%u",
+                         vscsiinfo.p_hst, vscsiinfo.p_chn, vscsiinfo.p_tgt, vscsiinfo.p_lun);
+                   snprintf(vdev, sizeof(vdev), "%u:%u:%u:%u",
+                         vscsiinfo.v_hst, vscsiinfo.v_chn, vscsiinfo.v_tgt, vscsiinfo.v_lun);
+                   /*      Idx  BE  state Sta */
+                   printf("%-3d %-3d %-5d %-5d %-10s %-10s %d\n",
+                         vscsiinfo.devid,
+			 vscsiinfo.backend_id,
+                         vscsiinfo.vscsi_host_state,
+			 vscsiinfo.backend_id,
+                         pdev, vdev,
+                         vscsiinfo.vscsi_dev_state);
+
+                   libxl_vscsiinfo_dispose(&vscsiinfo);
+               }
+           }
+           libxl_device_vscsi_dispose(&vscsi_hosts[h]);
+        }
+        free(vscsi_hosts);
+    }
+    return 0;
+}
+
+int main_vscsidetach(int argc, char **argv)
+{
+    int opt;
+    libxl_vscsi_dev *vscsi_dev, *vd;
+    libxl_device_vscsi *vscsi_host, *vh;
+    libxl_device_vscsi *vscsi_hosts;
+    char *tmp_buf, *dom = argv[1], *vdev = argv[2];
+    uint32_t domid;
+    int num_hosts, h, d, found = 0;
+
+    SWITCH_FOREACH_OPT(opt, "", NULL, "scsi-detach", 1) {
+        /* No options */
+    }
+    if (argc < 3) {
+        help("scsi-detach");
+        return 1;
+    }
+    if (libxl_domain_qualifier_to_domid(ctx, dom, &domid) < 0) {
+        fprintf(stderr, "%s is an invalid domain identifier\n", dom);
+        return 1;
+    }
+    vscsi_hosts = libxl_device_vscsi_list(ctx, domid, &num_hosts);
+    if (!vscsi_hosts)
+        return 0;
+
+    if (asprintf(&tmp_buf, "0:0:0:0,%s", vdev) < 0) {
+        perror("asprintf");
+        return 1;
+    }
+    vscsi_dev = calloc(1, sizeof(*vscsi_dev));
+    vscsi_host = calloc(1, sizeof(*vscsi_host));
+    if (!(vscsi_dev && vscsi_host)) {
+        fprintf(stderr, "%s ENOMEM\n", __func__);
+        goto done;
+    }
+    parse_vscsi_config(vscsi_host, vscsi_dev, tmp_buf);
+
+    for (h = 0; h < num_hosts; ++h) {
+       vh = &vscsi_hosts[h];
+       for (d = 0; !found && d < vh->num_vscsi_devs; d++) {
+#define CMP(member) (vd->member == vscsi_dev->member)
+           vd = &vh->vscsi_devs[d];
+           if (vh->v_hst == vscsi_host->v_hst && CMP(v_chn) && CMP(v_tgt) && CMP(v_lun)) {
+               if (vh->num_vscsi_devs > 1) {
+                   vd->remove = true;
+                   if (libxl_device_vscsi_add(ctx, domid, vh, 0)) {
+                       fprintf(stderr, "libxl_device_vscsi_remove failed.\n");
+                       goto done;
+                   }
+               } else {
+                   libxl_device_vscsi_remove(ctx, domid, vh, 0);
+               }
+               found = 1;
+           }
+#undef CMP
+       }
+       libxl_device_vscsi_dispose(vh);
+    }
+    if (!found)
+        fprintf(stderr, "%s(%u) vdev %s does not exist in domain %s\n", __func__, __LINE__, vdev, dom);
+done:
+    free(vscsi_hosts);
+    free(vscsi_host);
+    free(vscsi_dev);
+    free(tmp_buf);
+    return !found;
+}
+
 int main_vtpmattach(int argc, char **argv)
 {
     int opt;
--- a/tools/libxl/xl_cmdtable.c
+++ b/tools/libxl/xl_cmdtable.c
@@ -354,6 +354,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.",
+      "<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",
