<h4><a name="elementsHostDev">Host device assignment</a></h4>
- <h5><a href="elementsHostDevSubsys">USB / PCI devices</a></h5>
+ <h5><a href="elementsHostDevSubsys">USB / PCI / SCSI devices</a></h5>
<p>
- 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 <code>hostdev</code> element.
- <span class="since">since after 0.4.4 for USB and 0.6.0 for PCI
- (KVM only)</span>:
+ <span class="since">since after 0.4.4 for USB, 0.6.0 for PCI(KVM only)
+ and 1.0.6 for SCSI(KVM only)</span>:
</p>
<pre>
<rom bar='on' file='/etc/fake/boot.bin'/>
</hostdev>
</devices>
+ ...</pre>
+
+ <p>or:</p>
+
+<pre>
+ ...
+ <devices>
+ <hostdev mode='subsystem' type='scsi'>
+ <source>
+ <adapter name='scsi_host0'/>
+ <address type='scsi' bus='0' target='0' unit='0'/>
+ </source>
+ <readonly/>
+ <address type='drive' controller='0' bus='0' target='0' unit='0'/>
+ </hostdev>
+ </devices>
...</pre>
<dl>
<dt><code>hostdev</code></dt>
<dd>The <code>hostdev</code> element is the main container for describing
host devices. For usb device passthrough <code>mode</code> is always
- "subsystem" and <code>type</code> is "usb" for a USB device and "pci"
- for a PCI device. When <code>managed</code> is "yes" for a PCI
+ "subsystem" and <code>type</code> is "usb" for a USB device, "pci"
+ for a PCI device and "scsi" for a SCSI device. When
+ <code>managed</code> 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 <code>managed</code> is omitted or "no", and for USB
hot-plugging the device,
and <code>virNodeDeviceReAttach</code> (or <code>virsh
nodedev-reattach</code>) after hot-unplug or stopping the
- guest.</dd>
+ guest. For SCSI device, user is responsible to make sure the device
+ is not used by host.</dd>
<dt><code>source</code></dt>
<dd>The source element describes the device as seen from the host.
The USB device can either be addressed by vendor / product id using the
<code>vendor</code> and <code>product</code> elements or by the device's
address on the hosts using the <code>address</code> element. PCI devices
on the other hand can only be described by their <code>address</code>.
+ SCSI devices are described by both the <code>adapter</code> and
+ <code>address</code> elements.
<span class="since">Since 1.0.0</span>, the <code>source</code> element
of USB devices may contain <code>startupPolicy</code> attribute which can
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,
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);
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;
}
}
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,
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"),
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;
}
}
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,
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;
}
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 <address/> info for any devices,
if (virDomainDefMaybeAddSmartcardController(def) < 0)
return -1;
+ if (virDomainDefMaybeAddHostdevSCSIcontroller(def) < 0)
+ return -1;
+
return 0;
}
virBufferAddLit(buf, "</origstates>\n");
}
break;
+ case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_SCSI:
+ virBufferAsprintf(buf, "<adapter name='%s'/>\n",
+ def->source.subsys.u.scsi.adapter);
+ virBufferAsprintf(buf, "<address %sbus='%d' target='%d' unit='%d'/>\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"),