- USB and PCI devices attached to the host can be passed through
+ USB, PCI and SCSI devices attached to the host can be passed through
to the guest using the hostdev element.
- since after 0.4.4 for USB and 0.6.0 for PCI
- (KVM only):
+ since after 0.4.4 for USB, 0.6.0 for PCI(KVM only)
+ and 1.0.6 for SCSI(KVM only):
The hostdev element is the main container for describing
host devices. For usb device passthrough mode is always
- "subsystem" and type is "usb" for a USB device and "pci"
- for a PCI device. When managed is "yes" for a PCI
+ "subsystem" and type is "usb" for a USB device, "pci"
+ for a PCI device and "scsi" for a SCSI device. When
+ managed is "yes" for a PCI
device, it is detached from the host before being passed on to
the guest, and reattached to the host after the guest exits.
If managed is omitted or "no", and for USB
@@ -2309,13 +2326,16 @@
hot-plugging the device,
and virNodeDeviceReAttach (or virsh
nodedev-reattach) after hot-unplug or stopping the
- guest.
+ guest. For SCSI device, user is responsible to make sure the device
+ is not used by host.
source
The source element describes the device as seen from the host.
The USB device can either be addressed by vendor / product id using the
vendor and product elements or by the device's
address on the hosts using the address element. PCI devices
on the other hand can only be described by their address.
+ SCSI devices are described by both the adapter and
+ address elements.
Since 1.0.0, the source element
of USB devices may contain startupPolicy attribute which can
diff --git a/docs/schemas/domaincommon.rng b/docs/schemas/domaincommon.rng
index 10596dc0c0..6a4b77aa13 100644
--- a/docs/schemas/domaincommon.rng
+++ b/docs/schemas/domaincommon.rng
@@ -3094,6 +3094,7 @@
+
@@ -3162,6 +3163,20 @@
+
+
+ scsi
+
+
+
+
+
+
+
+
+
+
+
storage
@@ -3217,6 +3232,17 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/conf/domain_audit.c b/src/conf/domain_audit.c
index 40910d679f..a6cefb2e85 100644
--- a/src/conf/domain_audit.c
+++ b/src/conf/domain_audit.c
@@ -399,6 +399,16 @@ virDomainAuditHostdev(virDomainObjPtr vm, virDomainHostdevDefPtr hostdev,
goto cleanup;
}
break;
+ case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_SCSI:
+ if (virAsprintf(&address, "%s:%d:%d:%d",
+ hostdev->source.subsys.u.scsi.adapter,
+ hostdev->source.subsys.u.scsi.bus,
+ hostdev->source.subsys.u.scsi.target,
+ hostdev->source.subsys.u.scsi.unit) < 0) {
+ VIR_WARN("OOM while encoding audit message");
+ goto cleanup;
+ }
+ break;
default:
VIR_WARN("Unexpected hostdev type while encoding audit message: %d",
hostdev->source.subsys.type);
diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c
index e0bd8aa47f..d20faa5eef 100644
--- a/src/conf/domain_conf.c
+++ b/src/conf/domain_conf.c
@@ -585,7 +585,8 @@ VIR_ENUM_IMPL(virDomainHostdevMode, VIR_DOMAIN_HOSTDEV_MODE_LAST,
VIR_ENUM_IMPL(virDomainHostdevSubsys, VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_LAST,
"usb",
- "pci")
+ "pci",
+ "scsi")
VIR_ENUM_IMPL(virDomainHostdevSubsysPciBackend,
VIR_DOMAIN_HOSTDEV_PCI_BACKEND_TYPE_LAST,
@@ -1634,7 +1635,8 @@ void virDomainHostdevDefClear(virDomainHostdevDefPtr def)
if (def->parent.type == VIR_DOMAIN_DEVICE_NONE)
virDomainDeviceInfoFree(def->info);
- if (def->mode == VIR_DOMAIN_HOSTDEV_MODE_CAPABILITIES) {
+ switch (def->mode) {
+ case VIR_DOMAIN_HOSTDEV_MODE_CAPABILITIES:
switch (def->source.caps.type) {
case VIR_DOMAIN_HOSTDEV_CAPS_TYPE_STORAGE:
VIR_FREE(def->source.caps.u.storage.block);
@@ -1646,6 +1648,11 @@ void virDomainHostdevDefClear(virDomainHostdevDefPtr def)
VIR_FREE(def->source.caps.u.net.iface);
break;
}
+ break;
+ case VIR_DOMAIN_HOSTDEV_MODE_SUBSYS:
+ if (def->source.subsys.type == VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_SCSI)
+ VIR_FREE(def->source.subsys.u.scsi.adapter);
+ break;
}
}
@@ -3680,6 +3687,94 @@ out:
return ret;
}
+static int
+virDomainHostdevSubsysScsiDefParseXML(const xmlNodePtr node,
+ virDomainHostdevDefPtr def)
+{
+ int ret = -1;
+ bool got_address = false, got_adapter = false;
+ xmlNodePtr cur;
+ char *bus = NULL, *target = NULL, *unit = NULL;
+
+ cur = node->children;
+ while (cur != NULL) {
+ if (cur->type == XML_ELEMENT_NODE) {
+ if (xmlStrEqual(cur->name, BAD_CAST "address")) {
+ if (got_address) {
+ virReportError(VIR_ERR_XML_ERROR, "%s",
+ _("more than one source addresses is "
+ "specified for scsi hostdev"));
+ goto cleanup;
+ }
+
+ if (!(bus = virXMLPropString(cur, "bus")) ||
+ !(target = virXMLPropString(cur, "target")) ||
+ !(unit = virXMLPropString(cur, "unit"))) {
+ virReportError(VIR_ERR_XML_ERROR, "%s",
+ _("'bus', 'target', and 'unit' must be specified "
+ "for scsi hostdev source address"));
+ goto cleanup;
+ }
+
+ if (virStrToLong_ui(bus, NULL, 0, &def->source.subsys.u.scsi.bus) < 0) {
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ _("cannot parse bus '%s'"), bus);
+ goto cleanup;
+ }
+
+ if (virStrToLong_ui(target, NULL, 0, &def->source.subsys.u.scsi.target) < 0) {
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ _("cannot parse target '%s'"), target);
+ goto cleanup;
+ }
+
+ if (virStrToLong_ui(unit, NULL, 0, &def->source.subsys.u.scsi.unit) < 0) {
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ _("cannot parse unit '%s'"), unit);
+ goto cleanup;
+ }
+
+ got_address = true;
+ } else if (xmlStrEqual(cur->name, BAD_CAST "adapter")) {
+ if (got_adapter) {
+ virReportError(VIR_ERR_XML_ERROR, "%s",
+ _("more than one adapters is specified "
+ "for scsi hostdev source"));
+ goto cleanup;
+ }
+ if (!(def->source.subsys.u.scsi.adapter =
+ virXMLPropString(cur, "name"))) {
+ virReportError(VIR_ERR_XML_ERROR, "%s",
+ _("'adapter' must be specified for scsi hostdev source"));
+ goto cleanup;
+ }
+
+ got_adapter = true;
+ } else {
+ virReportError(VIR_ERR_XML_ERROR,
+ _("unsupported element '%s' of scsi hostdev source"),
+ cur->name);
+ goto cleanup;
+ }
+ }
+ cur = cur->next;
+ }
+
+ if (!got_address || !got_adapter) {
+ virReportError(VIR_ERR_XML_ERROR, "%s",
+ _("'adapter' and 'address' must be specified for scsi "
+ "hostdev source"));
+ goto cleanup;
+ }
+
+ ret = 0;
+cleanup:
+ VIR_FREE(bus);
+ VIR_FREE(target);
+ VIR_FREE(unit);
+ return ret;
+}
+
static int
virDomainHostdevDefParseXMLSubsys(xmlNodePtr node,
xmlXPathContextPtr ctxt,
@@ -3761,6 +3856,12 @@ virDomainHostdevDefParseXMLSubsys(xmlNodePtr node,
if (virDomainHostdevSubsysUsbDefParseXML(sourcenode, def) < 0)
goto error;
break;
+
+ case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_SCSI:
+ if (virDomainHostdevSubsysScsiDefParseXML(sourcenode, def) < 0)
+ goto error;
+ break;
+
default:
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
_("address type='%s' not supported in hostdev interfaces"),
@@ -8623,6 +8724,13 @@ virDomainHostdevDefParseXML(const xmlNodePtr node,
goto error;
}
break;
+ case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_SCSI:
+ if (def->info->type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE) {
+ virReportError(VIR_ERR_XML_ERROR, "%s",
+ _("SCSI host devices must have address specified"));
+ goto error;
+ }
+ break;
}
}
@@ -9150,6 +9258,17 @@ virDomainHostdevMatchSubsysPCI(virDomainHostdevDefPtr a,
return 0;
}
+static int
+virDomainHostdevMatchSubsysSCSI(virDomainHostdevDefPtr a,
+ virDomainHostdevDefPtr b)
+{
+ if (STREQ(a->source.subsys.u.scsi.adapter, b->source.subsys.u.scsi.adapter) &&
+ a->source.subsys.u.scsi.bus == b->source.subsys.u.scsi.bus &&
+ a->source.subsys.u.scsi.target == b->source.subsys.u.scsi.target &&
+ a->source.subsys.u.scsi.unit == b->source.subsys.u.scsi.unit)
+ return 1;
+ return 0;
+}
static int
virDomainHostdevMatchSubsys(virDomainHostdevDefPtr a,
@@ -9163,6 +9282,8 @@ virDomainHostdevMatchSubsys(virDomainHostdevDefPtr a,
return virDomainHostdevMatchSubsysPCI(a, b);
case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB:
return virDomainHostdevMatchSubsysUSB(a, b);
+ case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_SCSI:
+ return virDomainHostdevMatchSubsysSCSI(a, b);
}
return 0;
}
@@ -12987,6 +13108,30 @@ virDomainDefMaybeAddSmartcardController(virDomainDefPtr def)
return 0;
}
+static int
+virDomainDefMaybeAddHostdevSCSIcontroller(virDomainDefPtr def)
+{
+ /* Look for any hostdev scsi dev */
+ int i;
+ int maxController = -1;
+ virDomainHostdevDefPtr hostdev;
+
+ for (i = 0; i < def->nhostdevs; i++) {
+ hostdev = def->hostdevs[i];
+ if (hostdev->mode == VIR_DOMAIN_HOSTDEV_MODE_SUBSYS &&
+ hostdev->source.subsys.type == VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_SCSI &&
+ (int)hostdev->info->addr.drive.controller > maxController) {
+ maxController = hostdev->info->addr.drive.controller;
+ }
+ }
+
+ for (i = 0; i <= maxController; i++) {
+ if (virDomainDefMaybeAddController(def, VIR_DOMAIN_CONTROLLER_TYPE_SCSI, i, -1) < 0)
+ return -1;
+ }
+
+ return 0;
+}
/*
* Based on the declared info for any devices,
@@ -13023,6 +13168,9 @@ virDomainDefAddImplicitControllers(virDomainDefPtr def)
if (virDomainDefMaybeAddSmartcardController(def) < 0)
return -1;
+ if (virDomainDefMaybeAddHostdevSCSIcontroller(def) < 0)
+ return -1;
+
return 0;
}
@@ -13928,6 +14076,15 @@ virDomainHostdevDefFormatSubsys(virBufferPtr buf,
virBufferAddLit(buf, "\n");
}
break;
+ case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_SCSI:
+ virBufferAsprintf(buf, "\n",
+ def->source.subsys.u.scsi.adapter);
+ virBufferAsprintf(buf, "\n",
+ includeTypeInAddr ? "type='scsi' " : "",
+ def->source.subsys.u.scsi.bus,
+ def->source.subsys.u.scsi.target,
+ def->source.subsys.u.scsi.unit);
+ break;
default:
virReportError(VIR_ERR_INTERNAL_ERROR,
_("unexpected hostdev type %d"),
diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h
index 21f7ce28da..1efae699c3 100644
--- a/src/conf/domain_conf.h
+++ b/src/conf/domain_conf.h
@@ -384,6 +384,7 @@ enum virDomainHostdevMode {
enum virDomainHostdevSubsysType {
VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB,
VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI,
+ VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_SCSI,
VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_LAST
};
@@ -417,6 +418,12 @@ struct _virDomainHostdevSubsys {
virDevicePCIAddress addr; /* host address */
int backend; /* enum virDomainHostdevSubsysPciBackendType */
} pci;
+ struct {
+ char *adapter;
+ unsigned bus;
+ unsigned target;
+ unsigned unit;
+ } scsi;
} u;
};
diff --git a/tests/qemuxml2argvdata/qemuxml2argv-hostdev-scsi.xml b/tests/qemuxml2argvdata/qemuxml2argv-hostdev-scsi.xml
new file mode 100644
index 0000000000..5a263e7be0
--- /dev/null
+++ b/tests/qemuxml2argvdata/qemuxml2argv-hostdev-scsi.xml
@@ -0,0 +1,35 @@
+
+ QEMUGuest2
+ c7a5fdbd-edaf-9466-926a-d65c16db1809
+ 219100
+ 219100
+ 1
+
+ hvm
+
+
+
+ destroy
+ restart
+ destroy
+
+ /usr/bin/qemu
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/tests/qemuxml2xmltest.c b/tests/qemuxml2xmltest.c
index 64c1c83023..1ca1f7e0da 100644
--- a/tests/qemuxml2xmltest.c
+++ b/tests/qemuxml2xmltest.c
@@ -285,6 +285,8 @@ mymain(void)
DO_TEST_DIFFERENT("pci-autoadd-addr");
DO_TEST_DIFFERENT("pci-autoadd-idx");
+ DO_TEST("hostdev-scsi");
+
virObjectUnref(driver.caps);
virObjectUnref(driver.xmlopt);