[PATCH 14/16] Add hostdev handling for bhyve

Ryan Moeller ryan at ixsystems.com
Sat Feb 22 06:38:53 UTC 2020


Handle PCI passthrough and virtio-scsi using hostdev devices.

Example PCI passthrough:
domain xml snippet
```
<memoryBacking>
  <locked/>
</memoryBacking>
<devices>
  <hostdev mode='subsystem' type='pci'>
    <source>
      <address domain='0x0000' bus='0x06' slot='0x02' function='0x00'/>
    </source>
    <address type='pci' domain='0x0000' bus='0x00' slot='0x02' function='0x00'/>
  </hostdev>
</devices>
```
loader.conf snippet
```
vmm_load="YES"
pptdevs="6/2/0"
```

Example SCSI passthrough:
domain xml snippet
```
<hostdev mode='subsystem' type='scsi_ctl' model='virtio'>
  <source protocol='ioctl' pp='5' vp='0'/>
  <address type='pci' domain='0x0000' bus='0x00' slot='0x02' function='0x00'/>
</hostdev>
```
ctl.conf snippet
```
portal-group "pg0" {
        discovery-auth-group "no-authentication"
        listen "127.0.0.1"
}

target iqn.2020-01.com.example:target0 {
        auth-group "no-authentication"
        portal-group "pg0"
        port ioctl/5/0

        lun 0 { path "/dev/zvol/storage/lun0" }
        lun 1 { path "/dev/zvol/storage/lun1" }
        lun 2 { path "/dev/zvol/storage/lun2" }
        lun 3 { path "/dev/zvol/storage/lun3" }
}
```

Signed-off-by: Ryan Moeller <ryan at iXsystems.com>
---
 docs/schemas/domaincommon.rng                 |  30 ++++
 src/bhyve/bhyve_capabilities.c                |  14 ++
 src/bhyve/bhyve_capabilities.h                |   1 +
 src/bhyve/bhyve_command.c                     | 121 ++++++++++++++++
 src/bhyve/bhyve_parse_command.c               |  90 ++++++++++++
 src/conf/domain_audit.c                       |   5 +
 src/conf/domain_conf.c                        | 131 ++++++++++++++++++
 src/conf/domain_conf.h                        |  29 +++-
 src/conf/virconftypes.h                       |   3 +
 src/qemu/qemu_command.c                       |   2 +
 src/qemu/qemu_domain.c                        |   5 +
 src/qemu/qemu_hostdev.c                       |   1 +
 src/qemu/qemu_hotplug.c                       |   2 +
 src/qemu/qemu_migration.c                     |   1 +
 src/security/security_apparmor.c              |   1 +
 src/security/security_dac.c                   |  28 ++++
 src/security/security_selinux.c               |   8 ++
 .../bhyveargv2xml-passthru.args               |   8 ++
 .../bhyveargv2xml-passthru.xml                |  26 ++++
 .../bhyveargv2xml-virtio-scsi.args            |   9 ++
 .../bhyveargv2xml-virtio-scsi.xml             |  20 +++
 tests/bhyveargv2xmltest.c                     |   2 +
 .../bhyvexml2argv-passthru.args               |  11 ++
 .../bhyvexml2argv-passthru.ldargs             |   1 +
 .../bhyvexml2argv-passthru.xml                |  22 +++
 .../bhyvexml2argv-virtio-scsi.args            |   9 ++
 .../bhyvexml2argv-virtio-scsi.ldargs          |   1 +
 .../bhyvexml2argv-virtio-scsi.xml             |  21 +++
 tests/bhyvexml2argvtest.c                     |   4 +-
 .../bhyvexml2xmlout-passthru.xml              |  29 ++++
 .../bhyvexml2xmlout-virtio-scsi.xml           |  23 +++
 tests/bhyvexml2xmltest.c                      |   2 +
 32 files changed, 658 insertions(+), 2 deletions(-)
 create mode 100644 tests/bhyveargv2xmldata/bhyveargv2xml-passthru.args
 create mode 100644 tests/bhyveargv2xmldata/bhyveargv2xml-passthru.xml
 create mode 100644 tests/bhyveargv2xmldata/bhyveargv2xml-virtio-scsi.args
 create mode 100644 tests/bhyveargv2xmldata/bhyveargv2xml-virtio-scsi.xml
 create mode 100644 tests/bhyvexml2argvdata/bhyvexml2argv-passthru.args
 create mode 100644 tests/bhyvexml2argvdata/bhyvexml2argv-passthru.ldargs
 create mode 100644 tests/bhyvexml2argvdata/bhyvexml2argv-passthru.xml
 create mode 100644 tests/bhyvexml2argvdata/bhyvexml2argv-virtio-scsi.args
 create mode 100644 tests/bhyvexml2argvdata/bhyvexml2argv-virtio-scsi.ldargs
 create mode 100644 tests/bhyvexml2argvdata/bhyvexml2argv-virtio-scsi.xml
 create mode 100644 tests/bhyvexml2xmloutdata/bhyvexml2xmlout-passthru.xml
 create mode 100644 tests/bhyvexml2xmloutdata/bhyvexml2xmlout-virtio-scsi.xml

diff --git a/docs/schemas/domaincommon.rng b/docs/schemas/domaincommon.rng
index c00ace7d9c..e5250e1f32 100644
--- a/docs/schemas/domaincommon.rng
+++ b/docs/schemas/domaincommon.rng
@@ -4697,6 +4697,7 @@
       </attribute>
     </optional>
     <choice>
+      <ref name="hostdevsubsysctl"/>
       <ref name="hostdevsubsyspci"/>
       <ref name="hostdevsubsysusb"/>
       <ref name="hostdevsubsysscsi"/>
@@ -4723,6 +4724,34 @@
   </define>
 
 
+  <define name="hostdevsubsysctl">
+    <attribute name="type">
+      <value>scsi_ctl</value>
+    </attribute>
+    <optional>
+      <attribute name="model">
+        <choice>
+          <value>virtio</value>
+        </choice>
+      </attribute>
+    </optional>
+    <element name="source">
+      <choice>
+        <group>
+          <attribute name="protocol">
+            <value>ioctl</value>     <!-- ioctl, required -->
+          </attribute>
+          <attribute name="pp">
+            <data type="unsignedInt"/>
+          </attribute>
+          <attribute name="vp">
+            <data type="unsignedInt"/>
+          </attribute>
+        </group>
+      </choice>
+    </element>
+  </define>
+
   <define name="hostdevsubsyspci">
     <attribute name="type">
       <value>pci</value>
@@ -4734,6 +4763,7 @@
             <choice>
               <value>kvm</value>
               <value>vfio</value>
+              <value>vmm</value>
               <value>xen</value>
             </choice>
           </attribute>
diff --git a/src/bhyve/bhyve_capabilities.c b/src/bhyve/bhyve_capabilities.c
index fb8829d571..fb6be0aaba 100644
--- a/src/bhyve/bhyve_capabilities.c
+++ b/src/bhyve/bhyve_capabilities.c
@@ -323,6 +323,17 @@ bhyveProbeCapsXHCIController(unsigned int *caps, char *binary)
 }
 
 
+static int
+bhyveProbeCapsVirtioSCSI(unsigned int *caps, char *binary)
+{
+    return bhyveProbeCapsDeviceHelper(caps, binary,
+                                      "-s",
+                                      "0,virtio-scsi",
+                                      "pci slot 0:0: unknown device \"virtio-scsi\"",
+                                      BHYVE_CAP_VIRTIOSCSI);
+}
+
+
 int
 virBhyveProbeCaps(unsigned int *caps)
 {
@@ -351,6 +362,9 @@ virBhyveProbeCaps(unsigned int *caps)
     if ((ret = bhyveProbeCapsXHCIController(caps, binary)))
         goto out;
 
+    if ((ret = bhyveProbeCapsVirtioSCSI(caps, binary)))
+        goto out;
+
  out:
     VIR_FREE(binary);
     return ret;
diff --git a/src/bhyve/bhyve_capabilities.h b/src/bhyve/bhyve_capabilities.h
index 12926cf423..bb62bdfb15 100644
--- a/src/bhyve/bhyve_capabilities.h
+++ b/src/bhyve/bhyve_capabilities.h
@@ -49,6 +49,7 @@ typedef enum {
     BHYVE_CAP_FBUF = 1 << 4,
     BHYVE_CAP_XHCI = 1 << 5,
     BHYVE_CAP_CPUTOPOLOGY = 1 << 6,
+    BHYVE_CAP_VIRTIOSCSI = 1 << 7,
 } virBhyveCapsFlags;
 
 int virBhyveProbeGrubCaps(virBhyveGrubCapsFlags *caps);
diff --git a/src/bhyve/bhyve_command.c b/src/bhyve/bhyve_command.c
index bf1cfef3ab..dbf6fbf168 100644
--- a/src/bhyve/bhyve_command.c
+++ b/src/bhyve/bhyve_command.c
@@ -366,6 +366,123 @@ bhyveBuildControllerArgStr(const virDomainDef *def,
     return 0;
 }
 
+static int
+bhyveBuildHostdevSubsysPCIArgStr(const virDomainDef *def,
+                                 virDomainHostdevDefPtr dev,
+                                 bhyveConnPtr driver G_GNUC_UNUSED,
+                                 virCommandPtr cmd)
+{
+    virDomainHostdevSubsysPCIPtr pcisrc = &dev->source.subsys.u.pci;
+
+    switch (pcisrc->backend) {
+    case VIR_DOMAIN_HOSTDEV_PCI_BACKEND_DEFAULT:
+        pcisrc->backend = VIR_DOMAIN_HOSTDEV_PCI_BACKEND_VMM;
+        G_GNUC_FALLTHROUGH;
+    case VIR_DOMAIN_HOSTDEV_PCI_BACKEND_VMM:
+        if (!def->mem.locked) {
+            /* TODO: maybe just configure it automatically? */
+            virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
+                           _("guest memory must be locked (wired) for "
+                             "PCI passthrough"));
+            return -1;
+        }
+        virCommandAddArg(cmd, "-s");
+        virCommandAddArgFormat(cmd, "%u:%u:%u,passthru,%u/%u/%u",
+                               dev->info->addr.pci.bus,
+                               dev->info->addr.pci.slot,
+                               dev->info->addr.pci.function,
+                               pcisrc->addr.bus,
+                               pcisrc->addr.slot,
+                               pcisrc->addr.function);
+        break;
+    default:
+        virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
+                       _("unsupported hostdev pci backend"));
+        return -1;
+    }
+    return 0;
+}
+
+static int
+bhyveBuildHostdevSubsysSCSICTLArgStr(const virDomainDef *def G_GNUC_UNUSED,
+                                     virDomainHostdevDefPtr dev,
+                                     bhyveConnPtr driver,
+                                     virCommandPtr cmd)
+{
+    virDomainHostdevSubsysSCSICTLPtr ctlsrc = &dev->source.subsys.u.scsi_ctl;
+
+    /* Actually CAM Target Layer (CTL), not VHost on FreeBSD. */
+    if (!(bhyveDriverGetBhyveCaps(driver) & BHYVE_CAP_VIRTIOSCSI)) {
+        virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
+                       _("Installed bhyve binary does not support "
+                         "defining virtio-scsi devices"));
+        return -1;
+    }
+    if (ctlsrc->protocol !=
+        VIR_DOMAIN_HOSTDEV_SUBSYS_SCSI_CTL_PROTOCOL_TYPE_IOCTL) {
+        virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
+                       _("unsupported hostdev scsi_ctl protocol"));
+        return -1;
+    }
+    switch (ctlsrc->model) {
+    case VIR_DOMAIN_HOSTDEV_SUBSYS_SCSI_CTL_MODEL_TYPE_DEFAULT:
+        ctlsrc->model =
+            VIR_DOMAIN_HOSTDEV_SUBSYS_SCSI_CTL_MODEL_TYPE_VIRTIO;
+        G_GNUC_FALLTHROUGH;
+    case VIR_DOMAIN_HOSTDEV_SUBSYS_SCSI_CTL_MODEL_TYPE_VIRTIO:
+        virCommandAddArg(cmd, "-s");
+        virCommandAddArgFormat(cmd, "%u:%u,virtio-scsi,/dev/cam/ctl%u.%u",
+                               dev->info->addr.pci.slot,
+                               dev->info->addr.pci.function,
+                               ctlsrc->pp, ctlsrc->vp);
+        break;
+    default:
+        virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
+                       _("unsupported hostdev scsi_ctl model"));
+        return -1;
+    }
+    return 0;
+}
+
+static int
+bhyveBuildHostdevSubsysArgStr(const virDomainDef *def,
+                              virDomainHostdevDefPtr dev,
+                              bhyveConnPtr driver,
+                              virCommandPtr cmd)
+{
+    switch (dev->source.subsys.type) {
+    case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI:
+        if (bhyveBuildHostdevSubsysPCIArgStr(def, dev, driver, cmd) < 0)
+            return -1;
+        break;
+    case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_SCSI_CTL:
+        if (bhyveBuildHostdevSubsysSCSICTLArgStr(def, dev, driver, cmd) < 0)
+            return -1;
+        break;
+    default:
+        virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
+                       _("unsupported hostdev subsystem type"));
+        return -1;
+    }
+    return 0;
+}
+
+static int
+bhyveBuildHostdevArgStr(const virDomainDef *def,
+                        virDomainHostdevDefPtr dev,
+                        bhyveConnPtr driver,
+                        virCommandPtr cmd)
+{
+    switch (dev->mode) {
+    case VIR_DOMAIN_HOSTDEV_MODE_SUBSYS:
+        return bhyveBuildHostdevSubsysArgStr(def, dev, driver, cmd);
+    default:
+        virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
+                       _("unsupported hostdev device mode"));
+        return -1;
+    }
+}
+
 static int
 bhyveBuildLPCArgStr(const virDomainDef *def G_GNUC_UNUSED,
                     virCommandPtr cmd)
@@ -617,6 +734,10 @@ virBhyveProcessBuildBhyveCmd(bhyveConnPtr driver, virDomainDefPtr def,
         if (bhyveBuildDiskArgStr(def, def->disks[i], cmd) < 0)
             goto error;
     }
+    for (i = 0; i < def->nhostdevs; i++) {
+        if (bhyveBuildHostdevArgStr(def, def->hostdevs[i], driver, cmd) < 0)
+            goto error;
+    }
 
     if (def->ngraphics && def->nvideos) {
         if (def->ngraphics == 1 && def->nvideos == 1) {
diff --git a/src/bhyve/bhyve_parse_command.c b/src/bhyve/bhyve_parse_command.c
index 76423730d9..d69d23ace8 100644
--- a/src/bhyve/bhyve_parse_command.c
+++ b/src/bhyve/bhyve_parse_command.c
@@ -476,6 +476,92 @@ bhyveParsePCIDisk(virDomainDefPtr def,
     return -1;
 }
 
+static int
+bhyveParsePCIPassthru(virDomainDefPtr def,
+                      unsigned vmbus,
+                      unsigned vmslot,
+                      unsigned vmfunction,
+                      char *config)
+{
+    /* -s bus:slot:function,passthru,BUS/SLOT/FUNCTION */
+    virDomainHostdevDefPtr dev = NULL;
+    virDomainHostdevSubsysPCIPtr pcisrc = NULL;
+    unsigned hostbus, hostslot, hostfunction;
+
+    hostslot = hostbus = hostfunction = 0;
+    if (sscanf(config, "%u/%u/%u", &hostbus, &hostslot, &hostfunction) != 3)
+        return -1;
+
+    if ((dev = virDomainHostdevDefNew()) == NULL)
+        return -1;
+    dev->info->type = VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI;
+    dev->info->addr.pci.bus = vmbus;
+    dev->info->addr.pci.slot = vmslot;
+    dev->info->addr.pci.function = vmfunction;
+    dev->mode = VIR_DOMAIN_HOSTDEV_MODE_SUBSYS;
+    dev->source.subsys.type = VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI;
+    pcisrc = &dev->source.subsys.u.pci;
+    pcisrc->backend = VIR_DOMAIN_HOSTDEV_PCI_BACKEND_VMM;
+    pcisrc->addr.bus = hostbus;
+    pcisrc->addr.slot = hostslot;
+    pcisrc->addr.function = hostfunction;
+
+    if (VIR_APPEND_ELEMENT(def->hostdevs, def->nhostdevs, dev) < 0)
+        goto error;
+
+    return 0;
+
+ error:
+    virDomainHostdevDefFree(dev);
+    return -1;
+}
+
+static int
+bhyveParseSCSICTL(virDomainDefPtr def,
+                  unsigned bus,
+                  unsigned slot,
+                  unsigned function,
+                  char *config)
+{
+    /* -s slot,virtio-scsi,[dev=]/dev/cam/ctlPP.VP[,scsi-device-options] */
+    virDomainHostdevDefPtr dev = NULL;
+    virDomainHostdevSubsysSCSICTLPtr ctlsrc = NULL;
+    unsigned pp, vp;
+
+    /* Skip [dev=] if present. */
+    if (STRPREFIX(config, "dev="))
+        config = strchr(config, '=') + 1;
+
+    pp = vp = 0;
+    if (sscanf(config, "/dev/cam/ctl%u.%u", &pp, &vp) != 2)
+        return -1;
+
+    if ((dev = virDomainHostdevDefNew()) == NULL)
+        return -1;
+    dev->info->type = VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI;
+    dev->info->addr.pci.bus = bus;
+    dev->info->addr.pci.slot = slot;
+    dev->info->addr.pci.function = function;
+    dev->mode = VIR_DOMAIN_HOSTDEV_MODE_SUBSYS;
+    dev->source.subsys.type = VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_SCSI_CTL;
+    ctlsrc = &dev->source.subsys.u.scsi_ctl;
+    ctlsrc->model =
+        VIR_DOMAIN_HOSTDEV_SUBSYS_SCSI_CTL_MODEL_TYPE_VIRTIO;
+    ctlsrc->protocol =
+        VIR_DOMAIN_HOSTDEV_SUBSYS_SCSI_CTL_PROTOCOL_TYPE_IOCTL;
+    ctlsrc->pp = pp;
+    ctlsrc->vp = vp;
+
+    if (VIR_APPEND_ELEMENT(def->hostdevs, def->nhostdevs, dev) < 0)
+        goto error;
+
+    return 0;
+
+ error:
+    virDomainHostdevDefFree(dev);
+    return -1;
+}
+
 static int
 bhyveParsePCINet(virDomainDefPtr def,
                  virDomainXMLOptionPtr xmlopt,
@@ -601,6 +687,8 @@ bhyveParseBhyvePCIArg(virDomainDefPtr def,
                           nvirtiodisk,
                           nahcidisk,
                           conf);
+    else if (STREQ(emulation, "passthru"))
+        bhyveParsePCIPassthru(def, bus, slot, function, conf);
     else if (STREQ(emulation, "virtio-blk"))
         bhyveParsePCIDisk(def, caps, bus, slot, function,
                           VIR_DOMAIN_DISK_BUS_VIRTIO,
@@ -611,6 +699,8 @@ bhyveParseBhyvePCIArg(virDomainDefPtr def,
     else if (STREQ(emulation, "virtio-net"))
         bhyveParsePCINet(def, xmlopt, caps, bus, slot, function,
                          VIR_DOMAIN_NET_MODEL_VIRTIO, conf);
+    else if (STREQ(emulation, "virtio-scsi"))
+        bhyveParseSCSICTL(def, bus, slot, function, conf);
     else if (STREQ(emulation, "e1000"))
         bhyveParsePCINet(def, xmlopt, caps, bus, slot, function,
                          VIR_DOMAIN_NET_MODEL_E1000, conf);
diff --git a/src/conf/domain_audit.c b/src/conf/domain_audit.c
index 1b0abb21a0..7322eca80c 100644
--- a/src/conf/domain_audit.c
+++ b/src/conf/domain_audit.c
@@ -348,6 +348,7 @@ virDomainAuditHostdev(virDomainObjPtr vm, virDomainHostdevDefPtr hostdev,
     virDomainHostdevSubsysUSBPtr usbsrc = &hostdev->source.subsys.u.usb;
     virDomainHostdevSubsysPCIPtr pcisrc = &hostdev->source.subsys.u.pci;
     virDomainHostdevSubsysSCSIPtr scsisrc = &hostdev->source.subsys.u.scsi;
+    virDomainHostdevSubsysSCSICTLPtr ctlsrc = &hostdev->source.subsys.u.scsi_ctl;
     virDomainHostdevSubsysSCSIVHostPtr hostsrc = &hostdev->source.subsys.u.scsi_host;
     virDomainHostdevSubsysMediatedDevPtr mdevsrc = &hostdev->source.subsys.u.mdev;
 
@@ -387,6 +388,10 @@ virDomainAuditHostdev(virDomainObjPtr vm, virDomainHostdevDefPtr hostdev,
             }
             break;
         }
+        case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_SCSI_CTL:
+            address = g_strdup_printf("/dev/cam/ctl%u.%u",
+                                      ctlsrc->pp, ctlsrc->vp);
+            break;
         case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_SCSI_HOST:
             address = g_strdup(hostsrc->wwpn);
             break;
diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c
index cef49df3f8..12f8bb43c0 100644
--- a/src/conf/domain_conf.c
+++ b/src/conf/domain_conf.c
@@ -898,6 +898,7 @@ VIR_ENUM_IMPL(virDomainHostdevSubsys,
               "usb",
               "pci",
               "scsi",
+              "scsi_ctl",
               "scsi_host",
               "mdev",
 );
@@ -908,6 +909,7 @@ VIR_ENUM_IMPL(virDomainHostdevSubsysPCIBackend,
               "kvm",
               "vfio",
               "xen",
+              "vmm",
 );
 
 VIR_ENUM_IMPL(virDomainHostdevSubsysSCSIProtocol,
@@ -916,6 +918,18 @@ VIR_ENUM_IMPL(virDomainHostdevSubsysSCSIProtocol,
               "iscsi",
 );
 
+VIR_ENUM_IMPL(virDomainHostdevSubsysSCSICTLProtocol,
+              VIR_DOMAIN_HOSTDEV_SUBSYS_SCSI_CTL_PROTOCOL_TYPE_LAST,
+              "none",
+              "ioctl",
+);
+
+VIR_ENUM_IMPL(virDomainHostdevSubsysSCSICTLModel,
+              VIR_DOMAIN_HOSTDEV_SUBSYS_SCSI_CTL_MODEL_TYPE_LAST,
+              "default",
+              "virtio",
+);
+
 VIR_ENUM_IMPL(virDomainHostdevSubsysSCSIHostProtocol,
               VIR_DOMAIN_HOSTDEV_SUBSYS_SCSI_HOST_PROTOCOL_TYPE_LAST,
               "none",
@@ -2958,6 +2972,7 @@ void virDomainHostdevDefClear(virDomainHostdevDefPtr def)
             break;
         case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB:
         case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI:
+        case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_SCSI_CTL:
         case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_MDEV:
         case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_LAST:
             break;
@@ -5046,6 +5061,7 @@ virDomainHostdevDefPostParse(virDomainHostdevDefPtr dev,
 
     case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB:
     case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI:
+    case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_SCSI_CTL:
     case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_SCSI_HOST:
     case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_LAST:
         break;
@@ -6484,6 +6500,16 @@ virDomainHostdevDefValidate(const virDomainHostdevDef *hostdev)
                 return -1;
             }
             break;
+        case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_SCSI_CTL:
+            if (hostdev->info->type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE &&
+                hostdev->info->type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_UNASSIGNED &&
+                hostdev->info->type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI) {
+                virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                               _("SCSI CTL host devices must use 'pci' or "
+                                 "'unassigned' address type"));
+                return -1;
+            }
+            break;
         case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_SCSI_HOST:
             if (hostdev->info->type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE &&
                 hostdev->info->type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI &&
@@ -8320,6 +8346,64 @@ virDomainHostdevSubsysSCSIVHostDefParseXML(xmlNodePtr sourcenode,
     return 0;
 }
 
+static int
+virDomainHostdevSubsysSCSICTLDefParseXML(xmlNodePtr sourcenode,
+                                         virDomainHostdevDefPtr def)
+{
+    virDomainHostdevSubsysSCSICTLPtr ctlsrc = &def->source.subsys.u.scsi_ctl;
+    g_autofree char *protocol = NULL;
+    g_autofree char *pp = NULL;
+    g_autofree char *vp = NULL;
+
+    if (!(protocol = virXMLPropString(sourcenode, "protocol"))) {
+        virReportError(VIR_ERR_XML_ERROR, "%s",
+                       _("Missing scsi_ctl subsystem protocol"));
+        return -1;
+    }
+
+    if ((ctlsrc->protocol =
+         virDomainHostdevSubsysSCSICTLProtocolTypeFromString(protocol)) <= 0) {
+        virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+                       _("Unknown scsi_ctl subsystem protocol '%s'"),
+                       protocol);
+        return -1;
+    }
+
+    switch ((virDomainHostdevSubsysSCSICTLProtocolType) ctlsrc->protocol) {
+    case VIR_DOMAIN_HOSTDEV_SUBSYS_SCSI_CTL_PROTOCOL_TYPE_IOCTL:
+        if (!(pp = virXMLPropString(sourcenode, "pp"))) {
+            virReportError(VIR_ERR_XML_ERROR, "%s",
+                           _("missing scsi_ctl hostdev source pp"));
+            return -1;
+        }
+        if (virStrToLong_ui(pp, NULL, 10, &ctlsrc->pp) < 0) {
+            virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                           _("Cannot parse scsi_ctl hostdev source 'pp' attribute"));
+            return -1;
+        }
+        if (!(vp = virXMLPropString(sourcenode, "vp"))) {
+            virReportError(VIR_ERR_XML_ERROR, "%s",
+                           _("missing scsi_ctl hostdev source vp"));
+            return -1;
+        }
+        if (virStrToLong_ui(vp, NULL, 10, &ctlsrc->vp) < 0) {
+            virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                           _("Cannot parse scsi_ctl hostdev source 'vp' attribute"));
+            return -1;
+        }
+        break;
+    case VIR_DOMAIN_HOSTDEV_SUBSYS_SCSI_CTL_PROTOCOL_TYPE_NONE:
+    case VIR_DOMAIN_HOSTDEV_SUBSYS_SCSI_CTL_PROTOCOL_TYPE_LAST:
+        virReportError(VIR_ERR_XML_ERROR,
+                       _("Invalid hostdev protocol '%s'"),
+                       virDomainHostdevSubsysSCSICTLProtocolTypeToString(ctlsrc->protocol));
+        return -1;
+        break;
+    }
+
+    return 0;
+}
+
 static int
 virDomainHostdevSubsysMediatedDevDefParseXML(virDomainHostdevDefPtr def,
                                              xmlXPathContextPtr ctxt)
@@ -8363,6 +8447,7 @@ virDomainHostdevDefParseXMLSubsys(xmlNodePtr node,
     int backend;
     virDomainHostdevSubsysPCIPtr pcisrc = &def->source.subsys.u.pci;
     virDomainHostdevSubsysSCSIPtr scsisrc = &def->source.subsys.u.scsi;
+    virDomainHostdevSubsysSCSICTLPtr scsictlsrc = &def->source.subsys.u.scsi_ctl;
     virDomainHostdevSubsysSCSIVHostPtr scsihostsrc = &def->source.subsys.u.scsi_host;
     virDomainHostdevSubsysMediatedDevPtr mdevsrc = &def->source.subsys.u.mdev;
     g_autofree char *managed = NULL;
@@ -8453,6 +8538,7 @@ virDomainHostdevDefParseXMLSubsys(xmlNodePtr node,
     }
 
     if (def->source.subsys.type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_MDEV &&
+        def->source.subsys.type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_SCSI_CTL &&
         def->source.subsys.type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_SCSI_HOST) {
         if (model) {
             virReportError(VIR_ERR_XML_ERROR,
@@ -8471,6 +8557,14 @@ virDomainHostdevDefParseXMLSubsys(xmlNodePtr node,
                            model);
             return -1;
         }
+    } else if (def->source.subsys.type == VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_SCSI_CTL) {
+        if (model &&
+            ((scsictlsrc->model = virDomainHostdevSubsysSCSICTLModelTypeFromString(model)) < 0)) {
+            virReportError(VIR_ERR_XML_ERROR,
+                           _("unknown hostdev model '%s'"),
+                           model);
+            return -1;
+        }
     } else if (def->source.subsys.type == VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_MDEV) {
         if (!model) {
             virReportError(VIR_ERR_XML_ERROR, "%s",
@@ -8533,10 +8627,16 @@ virDomainHostdevDefParseXMLSubsys(xmlNodePtr node,
             return -1;
         break;
 
+    case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_SCSI_CTL:
+        if (virDomainHostdevSubsysSCSICTLDefParseXML(sourcenode, def) < 0)
+            return -1;
+        break;
+
     case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_SCSI_HOST:
         if (virDomainHostdevSubsysSCSIVHostDefParseXML(sourcenode, def) < 0)
             return -1;
         break;
+
     case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_MDEV:
         if (virDomainHostdevSubsysMediatedDevDefParseXML(def, ctxt) < 0)
             return -1;
@@ -15914,6 +16014,7 @@ virDomainHostdevDefParseXML(virDomainXMLOptionPtr xmlopt,
 
         case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB:
         case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI:
+        case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_SCSI_CTL:
         case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_SCSI_HOST:
         case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_MDEV:
         case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_LAST:
@@ -16963,6 +17064,13 @@ virDomainHostdevMatchSubsys(virDomainHostdevDefPtr a,
             return virDomainHostdevMatchSubsysSCSIiSCSI(a, b);
         else
             return virDomainHostdevMatchSubsysSCSIHost(a, b);
+    case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_SCSI_CTL:
+        return ((a->source.subsys.u.scsi_ctl.protocol ==
+                 b->source.subsys.u.scsi_ctl.protocol) &&
+                (a->source.subsys.u.scsi_ctl.pp ==
+                 b->source.subsys.u.scsi_ctl.pp) &&
+                (a->source.subsys.u.scsi_ctl.vp ==
+                 b->source.subsys.u.scsi_ctl.vp)) ? 1 : 0;
     case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_SCSI_HOST:
         if (a->source.subsys.u.scsi_host.protocol !=
             b->source.subsys.u.scsi_host.protocol)
@@ -25354,6 +25462,7 @@ virDomainHostdevDefFormatSubsys(virBufferPtr buf,
     virDomainHostdevSubsysUSBPtr usbsrc = &def->source.subsys.u.usb;
     virDomainHostdevSubsysPCIPtr pcisrc = &def->source.subsys.u.pci;
     virDomainHostdevSubsysSCSIPtr scsisrc = &def->source.subsys.u.scsi;
+    virDomainHostdevSubsysSCSICTLPtr ctlsrc = &def->source.subsys.u.scsi_ctl;
     virDomainHostdevSubsysSCSIVHostPtr hostsrc = &def->source.subsys.u.scsi_host;
     virDomainHostdevSubsysMediatedDevPtr mdevsrc = &def->source.subsys.u.mdev;
     virDomainHostdevSubsysSCSIHostPtr scsihostsrc = &scsisrc->u.host;
@@ -25396,6 +25505,15 @@ virDomainHostdevDefFormatSubsys(virBufferPtr buf,
                           protocol, iscsisrc->src->path);
     }
 
+    if (def->source.subsys.type == VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_SCSI_CTL) {
+        const char *protocol =
+            virDomainHostdevSubsysSCSICTLProtocolTypeToString(ctlsrc->protocol);
+        closedSource = true;
+
+        virBufferAsprintf(buf, " protocol='%s' pp='%u' vp='%u'/",
+                          protocol, ctlsrc->pp, ctlsrc->vp);
+    }
+
     if (def->source.subsys.type == VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_SCSI_HOST) {
         const char *protocol =
             virDomainHostdevSubsysSCSIHostProtocolTypeToString(hostsrc->protocol);
@@ -25457,6 +25575,7 @@ virDomainHostdevDefFormatSubsys(virBufferPtr buf,
                               scsihostsrc->unit);
         }
         break;
+    case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_SCSI_CTL:
     case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_SCSI_HOST:
         break;
     case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_MDEV:
@@ -27560,6 +27679,7 @@ virDomainHostdevDefFormat(virBufferPtr buf,
     virDomainHostdevSubsysSCSIPtr scsisrc = &def->source.subsys.u.scsi;
     virDomainHostdevSubsysMediatedDevPtr mdevsrc = &def->source.subsys.u.mdev;
     virDomainHostdevSubsysSCSIVHostPtr scsihostsrc = &def->source.subsys.u.scsi_host;
+    virDomainHostdevSubsysSCSICTLPtr scsictlsrc = &def->source.subsys.u.scsi_ctl;
     const char *type;
 
     if (!mode) {
@@ -27610,6 +27730,12 @@ virDomainHostdevDefFormat(virBufferPtr buf,
                               virTristateBoolTypeToString(scsisrc->rawio));
         }
 
+        if (def->source.subsys.type == VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_SCSI_CTL &&
+            scsictlsrc->model) {
+            virBufferAsprintf(buf, " model='%s'",
+                              virDomainHostdevSubsysSCSICTLModelTypeToString(scsictlsrc->model));
+        }
+
         if (def->source.subsys.type == VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_SCSI_HOST &&
             scsihostsrc->model) {
             virBufferAsprintf(buf, " model='%s'",
@@ -31142,6 +31268,11 @@ virDomainNetDefActualToNetworkPort(virDomainDefPtr dom,
             port->plug.hostdevpci.driver = VIR_NETWORK_FORWARD_DRIVER_NAME_VFIO;
             break;
 
+        case VIR_DOMAIN_HOSTDEV_PCI_BACKEND_VMM:
+            virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
+                           _("Unexpected PCI backend 'vmm'"));
+            break;
+
         case VIR_DOMAIN_HOSTDEV_PCI_BACKEND_XEN:
             virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
                            _("Unexpected PCI backend 'xen'"));
diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h
index cdc4d25700..4e0fc01c2b 100644
--- a/src/conf/domain_conf.h
+++ b/src/conf/domain_conf.h
@@ -195,6 +195,7 @@ typedef enum {
     VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB,
     VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI,
     VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_SCSI,
+    VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_SCSI_CTL,
     VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_SCSI_HOST,
     VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_MDEV,
 
@@ -203,10 +204,11 @@ typedef enum {
 
 /* the backend driver used for PCI hostdev devices */
 typedef enum {
-    VIR_DOMAIN_HOSTDEV_PCI_BACKEND_DEFAULT, /* detect automatically, prefer VFIO */
+    VIR_DOMAIN_HOSTDEV_PCI_BACKEND_DEFAULT, /* detect automatically, prefer VFIO or VMM */
     VIR_DOMAIN_HOSTDEV_PCI_BACKEND_KVM,    /* force legacy kvm style */
     VIR_DOMAIN_HOSTDEV_PCI_BACKEND_VFIO,   /* force vfio */
     VIR_DOMAIN_HOSTDEV_PCI_BACKEND_XEN,    /* force legacy xen style, use pciback */
+    VIR_DOMAIN_HOSTDEV_PCI_BACKEND_VMM,    /* force vmm (FreeBSD bhyve) */
 
     VIR_DOMAIN_HOSTDEV_PCI_BACKEND_TYPE_LAST
 } virDomainHostdevSubsysPCIBackendType;
@@ -258,6 +260,30 @@ struct _virDomainHostdevSubsysSCSI {
     } u;
 };
 
+typedef enum {
+    VIR_DOMAIN_HOSTDEV_SUBSYS_SCSI_CTL_PROTOCOL_TYPE_NONE,
+    VIR_DOMAIN_HOSTDEV_SUBSYS_SCSI_CTL_PROTOCOL_TYPE_IOCTL,
+
+    VIR_DOMAIN_HOSTDEV_SUBSYS_SCSI_CTL_PROTOCOL_TYPE_LAST,
+} virDomainHostdevSubsysSCSICTLProtocolType;
+
+VIR_ENUM_DECL(virDomainHostdevSubsysSCSICTLProtocol);
+
+typedef enum {
+    VIR_DOMAIN_HOSTDEV_SUBSYS_SCSI_CTL_MODEL_TYPE_DEFAULT,
+    VIR_DOMAIN_HOSTDEV_SUBSYS_SCSI_CTL_MODEL_TYPE_VIRTIO,
+
+    VIR_DOMAIN_HOSTDEV_SUBSYS_SCSI_CTL_MODEL_TYPE_LAST,
+} virDomainHostdevSubsysSCSICTLModelType;
+
+VIR_ENUM_DECL(virDomainHostdevSubsysSCSICTLModel);
+
+struct _virDomainHostdevSubsysSCSICTL {
+    int protocol; /* enum virDomainHostdevSubsysSCSICTLProtocolType */
+    unsigned pp, vp;
+    int model; /* enum virDomainHostdevSubsysSCSICTLModelType */
+};
+
 struct _virDomainHostdevSubsysMediatedDev {
     int model;                          /* enum virMediatedDeviceModelType */
     int display; /* virTristateSwitch */
@@ -298,6 +324,7 @@ struct _virDomainHostdevSubsys {
         virDomainHostdevSubsysPCI pci;
         virDomainHostdevSubsysSCSI scsi;
         virDomainHostdevSubsysSCSIVHost scsi_host;
+        virDomainHostdevSubsysSCSICTL scsi_ctl;
         virDomainHostdevSubsysMediatedDev mdev;
     } u;
 };
diff --git a/src/conf/virconftypes.h b/src/conf/virconftypes.h
index 1c62cde251..66cf40f8f0 100644
--- a/src/conf/virconftypes.h
+++ b/src/conf/virconftypes.h
@@ -180,6 +180,9 @@ typedef virDomainHostdevSubsysPCI *virDomainHostdevSubsysPCIPtr;
 typedef struct _virDomainHostdevSubsysSCSI virDomainHostdevSubsysSCSI;
 typedef virDomainHostdevSubsysSCSI *virDomainHostdevSubsysSCSIPtr;
 
+typedef struct _virDomainHostdevSubsysSCSICTL virDomainHostdevSubsysSCSICTL;
+typedef virDomainHostdevSubsysSCSICTL *virDomainHostdevSubsysSCSICTLPtr;
+
 typedef struct _virDomainHostdevSubsysSCSIHost virDomainHostdevSubsysSCSIHost;
 typedef virDomainHostdevSubsysSCSIHost *virDomainHostdevSubsysSCSIHostPtr;
 
diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c
index f69a9e651c..fdd59a35a4 100644
--- a/src/qemu/qemu_command.c
+++ b/src/qemu/qemu_command.c
@@ -4677,6 +4677,7 @@ qemuBuildPCIHostdevDevStr(const virDomainDef *def,
 
     case VIR_DOMAIN_HOSTDEV_PCI_BACKEND_KVM:
     case VIR_DOMAIN_HOSTDEV_PCI_BACKEND_DEFAULT:
+    case VIR_DOMAIN_HOSTDEV_PCI_BACKEND_VMM:
     case VIR_DOMAIN_HOSTDEV_PCI_BACKEND_XEN:
     case VIR_DOMAIN_HOSTDEV_PCI_BACKEND_TYPE_LAST:
         virReportError(VIR_ERR_INTERNAL_ERROR,
@@ -5439,6 +5440,7 @@ qemuBuildHostdevCommandLine(virCommandPtr cmd,
 
             break;
 
+        case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_SCSI_CTL:
         case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_LAST:
             break;
         }
diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c
index af6817cc05..3ebb864b4c 100644
--- a/src/qemu/qemu_domain.c
+++ b/src/qemu/qemu_domain.c
@@ -6674,8 +6674,11 @@ qemuDomainDeviceDefValidateHostdev(const virDomainHostdevDef *hostdev,
                 return -1;
             }
             break;
+
         case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_MDEV:
             return qemuDomainMdevDefValidate(hostdev, def, qemuCaps);
+
+        case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_SCSI_CTL:
         case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_LAST:
         default:
             virReportEnumRangeError(virDomainHostdevSubsysType,
@@ -14031,6 +14034,8 @@ qemuDomainGetHostdevPath(virDomainHostdevDefPtr dev,
 
             perm = VIR_CGROUP_DEVICE_RW;
             break;
+
+        case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_SCSI_CTL:
         case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_LAST:
             break;
         }
diff --git a/src/qemu/qemu_hostdev.c b/src/qemu/qemu_hostdev.c
index 1774850640..0e1432f23d 100644
--- a/src/qemu/qemu_hostdev.c
+++ b/src/qemu/qemu_hostdev.c
@@ -203,6 +203,7 @@ qemuHostdevPreparePCIDevicesCheckSupport(virDomainHostdevDefPtr *hostdevs,
             return false;
             break;
 
+        case VIR_DOMAIN_HOSTDEV_PCI_BACKEND_VMM:
         case VIR_DOMAIN_HOSTDEV_PCI_BACKEND_XEN:
         case VIR_DOMAIN_HOSTDEV_PCI_BACKEND_TYPE_LAST:
             break;
diff --git a/src/qemu/qemu_hotplug.c b/src/qemu/qemu_hotplug.c
index 9800491755..94fa64f216 100644
--- a/src/qemu/qemu_hotplug.c
+++ b/src/qemu/qemu_hotplug.c
@@ -1576,6 +1576,7 @@ qemuDomainAttachHostPCIDevice(virQEMUDriverPtr driver,
     case VIR_DOMAIN_HOSTDEV_PCI_BACKEND_KVM:
         break;
 
+    case VIR_DOMAIN_HOSTDEV_PCI_BACKEND_VMM:
     case VIR_DOMAIN_HOSTDEV_PCI_BACKEND_XEN:
     case VIR_DOMAIN_HOSTDEV_PCI_BACKEND_TYPE_LAST:
         virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
@@ -4535,6 +4536,7 @@ qemuDomainRemoveHostDevice(virQEMUDriverPtr driver,
     case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_MDEV:
         qemuDomainRemoveMediatedDevice(driver, vm, hostdev);
         break;
+    case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_SCSI_CTL:
     case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_LAST:
         break;
     }
diff --git a/src/qemu/qemu_migration.c b/src/qemu/qemu_migration.c
index a307c5ebe2..93144b741f 100644
--- a/src/qemu/qemu_migration.c
+++ b/src/qemu/qemu_migration.c
@@ -1105,6 +1105,7 @@ qemuMigrationSrcIsAllowedHostdev(const virDomainDef *def)
                 continue;
 
             case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_SCSI:
+            case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_SCSI_CTL:
             case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_SCSI_HOST:
             case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_MDEV:
                 virReportError(VIR_ERR_OPERATION_UNSUPPORTED,
diff --git a/src/security/security_apparmor.c b/src/security/security_apparmor.c
index ca02631f7f..eadf2580b6 100644
--- a/src/security/security_apparmor.c
+++ b/src/security/security_apparmor.c
@@ -959,6 +959,7 @@ AppArmorSetSecurityHostdevLabel(virSecurityManagerPtr mgr,
         break;
     }
 
+    case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_SCSI_CTL:
     case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_LAST:
         ret = 0;
         break;
diff --git a/src/security/security_dac.c b/src/security/security_dac.c
index d75b18170b..de85e53a9a 100644
--- a/src/security/security_dac.c
+++ b/src/security/security_dac.c
@@ -1212,6 +1212,7 @@ virSecurityDACSetHostdevLabel(virSecurityManagerPtr mgr,
     virDomainHostdevSubsysUSBPtr usbsrc = &dev->source.subsys.u.usb;
     virDomainHostdevSubsysPCIPtr pcisrc = &dev->source.subsys.u.pci;
     virDomainHostdevSubsysSCSIPtr scsisrc = &dev->source.subsys.u.scsi;
+    virDomainHostdevSubsysSCSICTLPtr ctlsrc = &dev->source.subsys.u.scsi_ctl;
     virDomainHostdevSubsysSCSIVHostPtr hostsrc = &dev->source.subsys.u.scsi_host;
     virDomainHostdevSubsysMediatedDevPtr mdevsrc = &dev->source.subsys.u.mdev;
     int ret = -1;
@@ -1300,6 +1301,19 @@ virSecurityDACSetHostdevLabel(virSecurityManagerPtr mgr,
         break;
     }
 
+    case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_SCSI_CTL: {
+        char *ctldev = g_strdup_printf("/dev/cam/ctl%u.%u",
+                                       ctlsrc->pp, ctlsrc->vp);
+
+        if (!ctldev)
+            return -1;
+
+        ret = virSecurityDACSetHostdevLabelHelper(ctldev, true, &cbdata);
+
+        VIR_FREE(ctldev);
+        break;
+    }
+
     case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_SCSI_HOST: {
         virSCSIVHostDevicePtr host = virSCSIVHostDeviceNew(hostsrc->wwpn);
 
@@ -1386,6 +1400,7 @@ virSecurityDACRestoreHostdevLabel(virSecurityManagerPtr mgr,
     virDomainHostdevSubsysUSBPtr usbsrc = &dev->source.subsys.u.usb;
     virDomainHostdevSubsysPCIPtr pcisrc = &dev->source.subsys.u.pci;
     virDomainHostdevSubsysSCSIPtr scsisrc = &dev->source.subsys.u.scsi;
+    virDomainHostdevSubsysSCSICTLPtr ctlsrc = &dev->source.subsys.u.scsi_ctl;
     virDomainHostdevSubsysSCSIVHostPtr hostsrc = &dev->source.subsys.u.scsi_host;
     virDomainHostdevSubsysMediatedDevPtr mdevsrc = &dev->source.subsys.u.mdev;
     int ret = -1;
@@ -1463,6 +1478,19 @@ virSecurityDACRestoreHostdevLabel(virSecurityManagerPtr mgr,
         break;
     }
 
+    case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_SCSI_CTL: {
+        char *ctldev = g_strdup_printf("/dev/cam/ctl%u.%u",
+                                       ctlsrc->pp, ctlsrc->vp);
+
+        if (!ctldev)
+            return -1;
+
+        ret = virSecurityDACRestoreFileLabel(mgr, ctldev);
+
+        VIR_FREE(ctldev);
+        break;
+    }
+
     case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_SCSI_HOST: {
         virSCSIVHostDevicePtr host = virSCSIVHostDeviceNew(hostsrc->wwpn);
 
diff --git a/src/security/security_selinux.c b/src/security/security_selinux.c
index 3f6968a57a..96f483ee14 100644
--- a/src/security/security_selinux.c
+++ b/src/security/security_selinux.c
@@ -2149,6 +2149,10 @@ virSecuritySELinuxSetHostdevSubsysLabel(virSecurityManagerPtr mgr,
         break;
     }
 
+    case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_SCSI_CTL:
+        /* FreeBSD only */
+        return -1;
+
     case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_SCSI_HOST: {
         virSCSIVHostDevicePtr host = virSCSIVHostDeviceNew(hostsrc->wwpn);
 
@@ -2384,6 +2388,10 @@ virSecuritySELinuxRestoreHostdevSubsysLabel(virSecurityManagerPtr mgr,
         break;
     }
 
+    case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_SCSI_CTL:
+        /* FreeBSD only */
+        return -1;
+
     case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_SCSI_HOST: {
         virSCSIVHostDevicePtr host = virSCSIVHostDeviceNew(hostsrc->wwpn);
 
diff --git a/tests/bhyveargv2xmldata/bhyveargv2xml-passthru.args b/tests/bhyveargv2xmldata/bhyveargv2xml-passthru.args
new file mode 100644
index 0000000000..697bafd642
--- /dev/null
+++ b/tests/bhyveargv2xmldata/bhyveargv2xml-passthru.args
@@ -0,0 +1,8 @@
+/usr/sbin/bhyve \
+-c 1 \
+-m 214 \
+-H \
+-P \
+-S \
+-s 0:0,hostbridge \
+-s 0:1:0,passthru,5/0/7 bhyve
diff --git a/tests/bhyveargv2xmldata/bhyveargv2xml-passthru.xml b/tests/bhyveargv2xmldata/bhyveargv2xml-passthru.xml
new file mode 100644
index 0000000000..af99279448
--- /dev/null
+++ b/tests/bhyveargv2xmldata/bhyveargv2xml-passthru.xml
@@ -0,0 +1,26 @@
+<domain type='bhyve'>
+  <name>bhyve</name>
+  <uuid>c7a5fdbd-edaf-9455-926a-d65c16db1809</uuid>
+  <memory unit='KiB'>219136</memory>
+  <currentMemory unit='KiB'>219136</currentMemory>
+  <memoryBacking>
+    <locked/>
+  </memoryBacking>
+  <vcpu placement='static'>1</vcpu>
+  <os>
+    <type>hvm</type>
+  </os>
+  <clock offset='localtime'/>
+  <on_poweroff>destroy</on_poweroff>
+  <on_reboot>destroy</on_reboot>
+  <on_crash>destroy</on_crash>
+  <devices>
+    <hostdev mode='subsystem' type='pci' managed='no'>
+      <driver name='vmm'/>
+      <source>
+        <address domain='0x0000' bus='0x05' slot='0x00' function='0x7'/>
+      </source>
+      <address type='pci' domain='0x0000' bus='0x00' slot='0x01' function='0x0'/>
+    </hostdev>
+  </devices>
+</domain>
diff --git a/tests/bhyveargv2xmldata/bhyveargv2xml-virtio-scsi.args b/tests/bhyveargv2xmldata/bhyveargv2xml-virtio-scsi.args
new file mode 100644
index 0000000000..ae38208853
--- /dev/null
+++ b/tests/bhyveargv2xmldata/bhyveargv2xml-virtio-scsi.args
@@ -0,0 +1,9 @@
+/usr/sbin/bhyve \
+-c 1 \
+-m 214 \
+-H \
+-P \
+-l bootrom,/usr/local/share/uefi-firmware/BHYVE_UEFI_CODE-devel.fd \
+-s 0:0,hostbridge \
+-s 1:0,lpc \
+-s 2:0,virtio-scsi,/dev/cam/ctl5.0 bhyve
diff --git a/tests/bhyveargv2xmldata/bhyveargv2xml-virtio-scsi.xml b/tests/bhyveargv2xmldata/bhyveargv2xml-virtio-scsi.xml
new file mode 100644
index 0000000000..23ad90a2a5
--- /dev/null
+++ b/tests/bhyveargv2xmldata/bhyveargv2xml-virtio-scsi.xml
@@ -0,0 +1,20 @@
+<domain type='bhyve'>
+  <name>bhyve</name>
+  <uuid>c7a5fdbd-edaf-9455-926a-d65c16db1809</uuid>
+  <memory unit='KiB'>219136</memory>
+  <currentMemory unit='KiB'>219136</currentMemory>
+  <vcpu placement='static'>1</vcpu>
+  <os>
+    <type>hvm</type>
+  </os>
+  <clock offset='localtime'/>
+  <on_poweroff>destroy</on_poweroff>
+  <on_reboot>destroy</on_reboot>
+  <on_crash>destroy</on_crash>
+  <devices>
+    <hostdev mode='subsystem' type='scsi_ctl' managed='no' model='virtio'>
+      <source protocol='ioctl' pp='5' vp='0'/>
+      <address type='pci' domain='0x0000' bus='0x00' slot='0x02' function='0x0'/>
+    </hostdev>
+  </devices>
+</domain>
diff --git a/tests/bhyveargv2xmltest.c b/tests/bhyveargv2xmltest.c
index 735cc4b338..1cfe4e3ddb 100644
--- a/tests/bhyveargv2xmltest.c
+++ b/tests/bhyveargv2xmltest.c
@@ -173,6 +173,8 @@ mymain(void)
     DO_TEST("ahci-hd");
     DO_TEST("virtio-blk");
     DO_TEST("virtio-net");
+    DO_TEST("virtio-scsi");
+    DO_TEST("passthru");
     DO_TEST("e1000");
     DO_TEST_WARN("virtio-net2");
     DO_TEST_WARN("virtio-net3");
diff --git a/tests/bhyvexml2argvdata/bhyvexml2argv-passthru.args b/tests/bhyvexml2argvdata/bhyvexml2argv-passthru.args
new file mode 100644
index 0000000000..c268da957c
--- /dev/null
+++ b/tests/bhyvexml2argvdata/bhyvexml2argv-passthru.args
@@ -0,0 +1,11 @@
+/usr/sbin/bhyve \
+-c 1 \
+-m 214 \
+-S \
+-u \
+-H \
+-P \
+-s 0:0,hostbridge \
+-l bootrom,/usr/local/share/uefi-firmware/BHYVE_UEFI_CODE-devel.fd \
+-s 0:3:0,passthru,5/0/7 \
+-s 1,lpc bhyve
diff --git a/tests/bhyvexml2argvdata/bhyvexml2argv-passthru.ldargs b/tests/bhyvexml2argvdata/bhyvexml2argv-passthru.ldargs
new file mode 100644
index 0000000000..2995a4d0e7
--- /dev/null
+++ b/tests/bhyvexml2argvdata/bhyvexml2argv-passthru.ldargs
@@ -0,0 +1 @@
+dummy
\ No newline at end of file
diff --git a/tests/bhyvexml2argvdata/bhyvexml2argv-passthru.xml b/tests/bhyvexml2argvdata/bhyvexml2argv-passthru.xml
new file mode 100644
index 0000000000..ba0744f35d
--- /dev/null
+++ b/tests/bhyvexml2argvdata/bhyvexml2argv-passthru.xml
@@ -0,0 +1,22 @@
+<domain type='bhyve'>
+  <name>bhyve</name>
+  <uuid>df3be7e7-a104-11e3-aeb0-50e5492bd3dc</uuid>
+  <memory>219136</memory>
+  <memoryBacking>
+    <locked/>
+  </memoryBacking>
+  <vcpu>1</vcpu>
+  <os>
+    <type>hvm</type>
+    <loader readonly='yes' type='pflash'>/usr/local/share/uefi-firmware/BHYVE_UEFI_CODE-devel.fd</loader>
+  </os>
+  <devices>
+    <hostdev mode='subsystem' type='pci' managed='no'>
+      <driver name='vmm'/>
+      <source>
+        <address domain='0x0000' bus='0x05' slot='0x00' function='0x7'/>
+      </source>
+      <address type='pci' domain='0x0000' bus='0x00' slot='0x03' function='0x0'/>
+    </hostdev>
+  </devices>
+</domain>
diff --git a/tests/bhyvexml2argvdata/bhyvexml2argv-virtio-scsi.args b/tests/bhyvexml2argvdata/bhyvexml2argv-virtio-scsi.args
new file mode 100644
index 0000000000..372f989095
--- /dev/null
+++ b/tests/bhyvexml2argvdata/bhyvexml2argv-virtio-scsi.args
@@ -0,0 +1,9 @@
+/usr/sbin/bhyve \
+-c 1 \
+-m 214 \
+-H \
+-P \
+-s 0:0,hostbridge \
+-l bootrom,/usr/local/share/uefi-firmware/BHYVE_UEFI_CODE-devel.fd \
+-s 2:0,virtio-scsi,/dev/cam/ctl5.0 \
+-s 1,lpc bhyve
\ No newline at end of file
diff --git a/tests/bhyvexml2argvdata/bhyvexml2argv-virtio-scsi.ldargs b/tests/bhyvexml2argvdata/bhyvexml2argv-virtio-scsi.ldargs
new file mode 100644
index 0000000000..421376db9e
--- /dev/null
+++ b/tests/bhyvexml2argvdata/bhyvexml2argv-virtio-scsi.ldargs
@@ -0,0 +1 @@
+dummy
diff --git a/tests/bhyvexml2argvdata/bhyvexml2argv-virtio-scsi.xml b/tests/bhyvexml2argvdata/bhyvexml2argv-virtio-scsi.xml
new file mode 100644
index 0000000000..394ff20ffc
--- /dev/null
+++ b/tests/bhyvexml2argvdata/bhyvexml2argv-virtio-scsi.xml
@@ -0,0 +1,21 @@
+<domain type='bhyve'>
+  <name>bhyve</name>
+  <uuid>c7a5fdbd-edaf-9455-926a-d65c16db1809</uuid>
+  <memory unit='KiB'>219136</memory>
+  <currentMemory unit='KiB'>219136</currentMemory>
+  <vcpu placement='static'>1</vcpu>
+  <os>
+    <type>hvm</type>
+    <loader readonly='yes' type='pflash'>/usr/local/share/uefi-firmware/BHYVE_UEFI_CODE-devel.fd</loader>
+  </os>
+  <clock offset='localtime'/>
+  <on_poweroff>destroy</on_poweroff>
+  <on_reboot>destroy</on_reboot>
+  <on_crash>destroy</on_crash>
+  <devices>
+    <hostdev mode='subsystem' type='scsi_ctl' model='virtio'>
+      <source protocol='ioctl' pp='5' vp='0'/>
+      <address type='pci' domain='0x0000' bus='0x00' slot='0x02' function='0x0'/>
+    </hostdev>
+  </devices>
+</domain>
diff --git a/tests/bhyvexml2argvtest.c b/tests/bhyvexml2argvtest.c
index 9e7eb218b8..7ce2d5c6b1 100644
--- a/tests/bhyvexml2argvtest.c
+++ b/tests/bhyvexml2argvtest.c
@@ -175,7 +175,7 @@ mymain(void)
     driver.bhyvecaps = BHYVE_CAP_RTC_UTC | BHYVE_CAP_AHCI32SLOT | \
                        BHYVE_CAP_NET_E1000 | BHYVE_CAP_LPC_BOOTROM | \
                        BHYVE_CAP_FBUF | BHYVE_CAP_XHCI | \
-                       BHYVE_CAP_CPUTOPOLOGY;
+                       BHYVE_CAP_CPUTOPOLOGY | BHYVE_CAP_VIRTIOSCSI;
 
     DO_TEST("base");
     DO_TEST("wired");
@@ -210,6 +210,8 @@ mymain(void)
     DO_TEST_FAILURE("cputopology-nvcpu-mismatch");
     DO_TEST("commandline");
     DO_TEST("msrs");
+    DO_TEST("virtio-scsi");
+    DO_TEST("passthru");
 
     /* Address allocation tests */
     DO_TEST("addr-single-sata-disk");
diff --git a/tests/bhyvexml2xmloutdata/bhyvexml2xmlout-passthru.xml b/tests/bhyvexml2xmloutdata/bhyvexml2xmlout-passthru.xml
new file mode 100644
index 0000000000..0313fa0dfa
--- /dev/null
+++ b/tests/bhyvexml2xmloutdata/bhyvexml2xmlout-passthru.xml
@@ -0,0 +1,29 @@
+<domain type='bhyve'>
+  <name>bhyve</name>
+  <uuid>df3be7e7-a104-11e3-aeb0-50e5492bd3dc</uuid>
+  <memory unit='KiB'>219136</memory>
+  <currentMemory unit='KiB'>219136</currentMemory>
+  <memoryBacking>
+    <locked/>
+  </memoryBacking>
+  <vcpu placement='static'>1</vcpu>
+  <os>
+    <type arch='x86_64'>hvm</type>
+    <loader readonly='yes' type='pflash'>/usr/local/share/uefi-firmware/BHYVE_UEFI_CODE-devel.fd</loader>
+    <boot dev='hd'/>
+  </os>
+  <clock offset='utc'/>
+  <on_poweroff>destroy</on_poweroff>
+  <on_reboot>restart</on_reboot>
+  <on_crash>destroy</on_crash>
+  <devices>
+    <controller type='pci' index='0' model='pci-root'/>
+    <hostdev mode='subsystem' type='pci' managed='no'>
+      <driver name='vmm'/>
+      <source>
+        <address domain='0x0000' bus='0x05' slot='0x00' function='0x7'/>
+      </source>
+      <address type='pci' domain='0x0000' bus='0x00' slot='0x03' function='0x0'/>
+    </hostdev>
+  </devices>
+</domain>
diff --git a/tests/bhyvexml2xmloutdata/bhyvexml2xmlout-virtio-scsi.xml b/tests/bhyvexml2xmloutdata/bhyvexml2xmlout-virtio-scsi.xml
new file mode 100644
index 0000000000..771591689a
--- /dev/null
+++ b/tests/bhyvexml2xmloutdata/bhyvexml2xmlout-virtio-scsi.xml
@@ -0,0 +1,23 @@
+<domain type='bhyve'>
+  <name>bhyve</name>
+  <uuid>c7a5fdbd-edaf-9455-926a-d65c16db1809</uuid>
+  <memory unit='KiB'>219136</memory>
+  <currentMemory unit='KiB'>219136</currentMemory>
+  <vcpu placement='static'>1</vcpu>
+  <os>
+    <type arch='x86_64'>hvm</type>
+    <loader readonly='yes' type='pflash'>/usr/local/share/uefi-firmware/BHYVE_UEFI_CODE-devel.fd</loader>
+    <boot dev='hd'/>
+  </os>
+  <clock offset='localtime'/>
+  <on_poweroff>destroy</on_poweroff>
+  <on_reboot>destroy</on_reboot>
+  <on_crash>destroy</on_crash>
+  <devices>
+    <controller type='pci' index='0' model='pci-root'/>
+    <hostdev mode='subsystem' type='scsi_ctl' managed='no' model='virtio'>
+      <source protocol='ioctl' pp='5' vp='0'/>
+      <address type='pci' domain='0x0000' bus='0x00' slot='0x02' function='0x0'/>
+    </hostdev>
+  </devices>
+</domain>
diff --git a/tests/bhyvexml2xmltest.c b/tests/bhyvexml2xmltest.c
index a0c20a14c1..3a2a46014a 100644
--- a/tests/bhyvexml2xmltest.c
+++ b/tests/bhyvexml2xmltest.c
@@ -83,6 +83,7 @@ mymain(void)
     DO_TEST_DIFFERENT("acpiapic");
     DO_TEST_DIFFERENT("base");
     DO_TEST_DIFFERENT("wired");
+    DO_TEST_DIFFERENT("passthru");
     DO_TEST_DIFFERENT("bhyveload-bootorder");
     DO_TEST_DIFFERENT("bhyveload-bootorder1");
     DO_TEST_DIFFERENT("bhyveload-bootorder2");
@@ -94,6 +95,7 @@ mymain(void)
     DO_TEST_DIFFERENT("disk-cdrom");
     DO_TEST_DIFFERENT("disk-cdrom-grub");
     DO_TEST_DIFFERENT("disk-virtio");
+    DO_TEST_DIFFERENT("virtio-scsi");
     DO_TEST_DIFFERENT("grub-bootorder");
     DO_TEST_DIFFERENT("grub-bootorder2");
     DO_TEST_DIFFERENT("grub-defaults");
-- 
2.24.1





More information about the libvir-list mailing list