From: Han Cheng <hanc.fnst@xxxxxxxxxxxxxx> An example of the scsi hostdev XML: <hostdev mode='subsystem' type='scsi'> <source> <adapter name='scsi_host0'/> <address bus='0' target='0' unit='0'/> </source> <address type='drive' controller='0' bus='0' target='0' unit='7'/> </hostdev> Except these generic XML, this also introduces a "readonly" tag (mapping to "readonly" property of "-drive" in the qemu command line), though it's only valid for scsi hostdev now. The device address must be specified manually. Later patch will let libvirt generate it automatically. Signed-off-by: Han Cheng <hanc.fnst@xxxxxxxxxxxxxx> Signed-off-by: Osier Yang <jyang@xxxxxxxxxx> --- v2.5 - v3: * Remove the rigid algrithom to generate the address, the address of scsi host device must be specified manually now, later patch will add the generator. * Merge the xml2xml test from 10/10 into this patch * s/1.0.5/1.0.6/ * Improve the XML parsing, fixes on virReportError statements, typos * hostdev->readonly is changed from bit field into boolean --- docs/formatdomain.html.in | 38 ++++- docs/schemas/domaincommon.rng | 29 ++++ src/conf/domain_audit.c | 10 ++ src/conf/domain_conf.c | 172 ++++++++++++++++++++- src/conf/domain_conf.h | 8 + .../qemuxml2argv-hostdev-scsi-readonly.xml | 35 +++++ tests/qemuxml2xmltest.c | 1 + 7 files changed, 284 insertions(+), 9 deletions(-) create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-hostdev-scsi-readonly.xml diff --git a/docs/formatdomain.html.in b/docs/formatdomain.html.in index 6d6cfb6..ec8cd39 100644 --- a/docs/formatdomain.html.in +++ b/docs/formatdomain.html.in @@ -2223,13 +2223,13 @@ <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> @@ -2260,12 +2260,29 @@ </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 @@ -2275,13 +2292,16 @@ 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 can be described by <code>adapter</code> and + <code>address</code>. <span class="since">Since 1.0.0</span>, the <code>source</code> element of USB devices may contain <code>startupPolicy</code> attribute which can @@ -2308,6 +2328,9 @@ <code>id</code> attribute that specifies the USB vendor and product id. The ids can be given in decimal, hexadecimal (starting with 0x) or octal (starting with 0) form.</dd> + <dt><code>readonly</code></dt> + <dd>Indicates that the device is readonly, only valid for SCSI device. + <span class="since">Since 1.0.6</span></dd> <dt><code>boot</code></dt> <dd>Specifies that the device is bootable. The <code>order</code> attribute determines the order in which devices will be tried during @@ -2316,6 +2339,7 @@ <a href="#elementsOSBIOS">BIOS bootloader</a> section. <span class="since">Since 0.8.8</span> for PCI devices, <span class="since">Since 1.0.1</span> for USB devices. + <span class="since">Since 1.0.6</span> for SCSI devices. <dt><code>rom</code></dt> <dd>The <code>rom</code> element is used to change how a PCI device's ROM is presented to the guest. The optional <code>bar</code> diff --git a/docs/schemas/domaincommon.rng b/docs/schemas/domaincommon.rng index 0c1b76a..ac33a8d 100644 --- a/docs/schemas/domaincommon.rng +++ b/docs/schemas/domaincommon.rng @@ -3070,6 +3070,7 @@ <choice> <ref name="hostdevsubsyspci"/> <ref name="hostdevsubsysusb"/> + <ref name="hostdevsubsysscsi"/> </choice> </define> @@ -3125,6 +3126,23 @@ </element> </define> + <define name="hostdevsubsysscsi"> + <attribute name="type"> + <value>scsi</value> + </attribute> + <element name="source"> + <ref name="sourceinfoadapter"/> + <element name="address"> + <ref name="scsiaddress"/> + </element> + </element> + <optional> + <element name='readonly'> + <empty/> + </element> + </optional> + </define> + <define name="hostdevcapsstorage"> <attribute name="type"> <value>storage</value> @@ -3180,6 +3198,17 @@ </attribute> </element> </define> + <define name="scsiaddress"> + <attribute name="bus"> + <ref name="driveBus"/> + </attribute> + <attribute name="target"> + <ref name="driveTarget"/> + </attribute> + <attribute name="unit"> + <ref name="driveUnit"/> + </attribute> + </define> <define name="usbportaddress"> <attribute name="bus"> <ref name="usbAddr"/> diff --git a/src/conf/domain_audit.c b/src/conf/domain_audit.c index 6d0ae48..d2fb556 100644 --- a/src/conf/domain_audit.c +++ b/src/conf/domain_audit.c @@ -398,6 +398,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 958b77b..edbed89 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -580,7 +580,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(virDomainHostdevCaps, VIR_DOMAIN_HOSTDEV_CAPS_TYPE_LAST, "storage", @@ -1623,7 +1624,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); @@ -1635,6 +1637,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; } } @@ -3382,6 +3389,91 @@ virDomainParseLegacyDeviceAddress(char *devaddr, } 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 addresses are specified")); + 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 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 are specified")); + 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 address")); + goto cleanup; + } + + got_adapter = true; + } else { + virReportError(VIR_ERR_XML_ERROR, + _("unsuported 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")); + goto cleanup; + } + + ret = 0; +cleanup: + VIR_FREE(bus); + VIR_FREE(target); + VIR_FREE(unit); + return ret; +} + +static int virDomainHostdevSubsysUsbDefParseXML(const xmlNodePtr node, virDomainHostdevDefPtr def) { @@ -3676,6 +3768,10 @@ 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"), @@ -8511,6 +8607,7 @@ virDomainHostdevDefParseXML(const xmlNodePtr node, } if (def->mode == VIR_DOMAIN_HOSTDEV_MODE_SUBSYS) { + xmlNodePtr cur; switch (def->source.subsys.type) { case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI: if (def->info->type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE && @@ -8519,6 +8616,25 @@ virDomainHostdevDefParseXML(const xmlNodePtr node, _("PCI host devices must use 'pci' address type")); 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; + } + + cur = node->children; + while (cur != NULL) { + if (cur->type == XML_ELEMENT_NODE) { + if (xmlStrEqual(cur->name, BAD_CAST "readonly")) { + def->readonly = true; + break; + } + } + cur = cur->next; + } break; } } @@ -9047,6 +9163,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, @@ -9060,6 +9187,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; } @@ -12863,6 +12992,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 <address/> info for any devices, @@ -12899,6 +13052,9 @@ virDomainDefAddImplicitControllers(virDomainDefPtr def) if (virDomainDefMaybeAddSmartcardController(def) < 0) return -1; + if (virDomainDefMaybeAddHostdevSCSIcontroller(def) < 0) + return -1; + return 0; } @@ -13753,6 +13909,15 @@ virDomainHostdevDefFormatSubsys(virBufferPtr buf, virBufferAdjustIndent(buf, 2); switch (def->source.subsys.type) { + 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; case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB: if (def->source.subsys.u.usb.vendor) { virBufferAsprintf(buf, "<vendor id='0x%.4x'/>\n", @@ -15056,6 +15221,9 @@ virDomainHostdevDefFormat(virBufferPtr buf, case VIR_DOMAIN_HOSTDEV_MODE_SUBSYS: if (virDomainHostdevDefFormatSubsys(buf, def, flags, false) < 0) return -1; + if (def->source.subsys.type == VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_SCSI && + def->readonly) + virBufferAsprintf(buf, "<readonly/>\n"); break; case VIR_DOMAIN_HOSTDEV_MODE_CAPABILITIES: if (virDomainHostdevDefFormatCaps(buf, def) < 0) diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index d9ea1c1..8c42e0a 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -385,6 +385,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 }; @@ -404,6 +405,12 @@ struct _virDomainHostdevSubsys { unsigned vendor; unsigned product; } usb; + struct { + char *adapter; + unsigned bus; + unsigned target; + unsigned unit; + } scsi; virDevicePCIAddress pci; /* host address */ } u; }; @@ -442,6 +449,7 @@ struct _virDomainHostdevDef { int startupPolicy; /* enum virDomainStartupPolicy */ bool managed; bool missing; + bool readonly; /* Only valid for scsi hostdev */ union { virDomainHostdevSubsys subsys; virDomainHostdevCaps caps; diff --git a/tests/qemuxml2argvdata/qemuxml2argv-hostdev-scsi-readonly.xml b/tests/qemuxml2argvdata/qemuxml2argv-hostdev-scsi-readonly.xml new file mode 100644 index 0000000..11d1712 --- /dev/null +++ b/tests/qemuxml2argvdata/qemuxml2argv-hostdev-scsi-readonly.xml @@ -0,0 +1,35 @@ +<domain type='qemu'> + <name>QEMUGuest2</name> + <uuid>c7a5fdbd-edaf-9466-926a-d65c16db1809</uuid> + <memory unit='KiB'>219100</memory> + <currentMemory unit='KiB'>219100</currentMemory> + <vcpu placement='static'>1</vcpu> + <os> + <type arch='i686' machine='pc'>hvm</type> + <boot dev='hd'/> + </os> + <clock offset='utc'/> + <on_poweroff>destroy</on_poweroff> + <on_reboot>restart</on_reboot> + <on_crash>destroy</on_crash> + <devices> + <emulator>/usr/bin/qemu</emulator> + <disk type='block' device='disk'> + <source dev='/dev/HostVG/QEMUGuest2'/> + <target dev='hda' bus='ide'/> + <address type='drive' controller='0' bus='0' target='0' unit='0'/> + </disk> + <controller type='scsi' index='0' model='virtio-scsi'/> + <controller type='usb' index='0'/> + <controller type='ide' index='0'/> + <hostdev mode='subsystem' type='scsi' managed='yes'> + <source> + <adapter name='scsi_host0'/> + <address bus='0' target='0' unit='0'/> + </source> + <readonly/> + <address type='drive' controller='0' bus='0' target='4' unit='8'/> + </hostdev> + <memballoon model='virtio'/> + </devices> +</domain> diff --git a/tests/qemuxml2xmltest.c b/tests/qemuxml2xmltest.c index 1d10bf2..a2727b0 100644 --- a/tests/qemuxml2xmltest.c +++ b/tests/qemuxml2xmltest.c @@ -230,6 +230,7 @@ mymain(void) DO_TEST("hostdev-usb-address"); DO_TEST("hostdev-pci-address"); + DO_TEST("hostdev-scsi-readonly"); DO_TEST("pci-rom"); DO_TEST("encrypted-disk"); -- 1.8.1.4 -- libvir-list mailing list libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list