[libvirt] [PATCH 00/10 v3] Support scsi device passthrough via scsi-generic

This adds the support for qemu's scsi-generic device, which can be used to passthrough scsi host device. An example of the XML: <hostdev mode='subsystem' type='scsi' managed='no'> <source> <adapter name='scsi_host7'/> <address bus='0' target='0' unit='0'/> </source> <address type='drive' controller='0' bus='0' target='0' unit='1'/> </hostdev> Mainly changes from v2.5 (details in each patch): * 1/10 and 2/10 of v2.5 are pushed * 8/10 of v2.5 is splitted * A better way to generate the address for scsi host device instead of the rigid one. * Support all scsi controllers when building the qemu command line. * Figure out the limits of the qemu properties, and do checking * Don't forget DAC and apparmor security backends * Orgnize the patches in a better way. * Fix the make check failure Han Cheng (7): conf: Introduce XMLs for scsi hostdev qemu: New cap flags for scsi-generic utils: util functions for scsi hostdev qemu: Build qemu command line for scsi host device qemu: Introduce activeScsiHostdevs list for scsi host devices qemu: Allow the scsi-generic device in cgroup qemu: Add hotplug support for scsi host device Osier Yang (3): security: Manage the security label for scsi host device qemu: Refactor helpers for USB device attachment conf: Generate address for scsi host device automatically docs/formatdomain.html.in | 38 +- docs/schemas/domaincommon.rng | 29 ++ po/POTFILES.in | 1 + src/Makefile.am | 1 + src/conf/domain_audit.c | 10 + src/conf/domain_conf.c | 312 +++++++++++++++- src/conf/domain_conf.h | 8 + src/libvirt_private.syms | 22 ++ src/qemu/qemu_capabilities.c | 14 +- src/qemu/qemu_capabilities.h | 2 + src/qemu/qemu_cgroup.c | 72 +++- src/qemu/qemu_cgroup.h | 3 + src/qemu/qemu_command.c | 154 +++++++- src/qemu/qemu_command.h | 6 + src/qemu/qemu_conf.h | 2 + src/qemu/qemu_driver.c | 3 + src/qemu/qemu_hostdev.c | 213 +++++++++++ src/qemu/qemu_hostdev.h | 10 + src/qemu/qemu_hotplug.c | 224 +++++++++--- src/qemu/qemu_process.c | 3 + src/security/security_apparmor.c | 49 ++- src/security/security_dac.c | 77 +++- src/security/security_selinux.c | 72 +++- src/util/virscsi.c | 407 +++++++++++++++++++++ src/util/virscsi.h | 84 +++++ tests/qemuhelpdata/qemu-1.0-device | 10 + tests/qemuhelpdata/qemu-1.1.0-device | 10 + tests/qemuhelpdata/qemu-1.2.0-device | 5 + tests/qemuhelpdata/qemu-kvm-1.2.0-device | 5 + tests/qemuhelptest.c | 19 +- .../qemuxml2argv-hostdev-scsi-boot.args | 9 + .../qemuxml2argv-hostdev-scsi-boot.xml | 34 ++ .../qemuxml2argv-hostdev-scsi-readonly.args | 9 + .../qemuxml2argv-hostdev-scsi-readonly.xml | 35 ++ tests/qemuxml2argvtest.c | 9 + tests/qemuxml2xmltest.c | 2 + 36 files changed, 1842 insertions(+), 121 deletions(-) create mode 100644 src/util/virscsi.c create mode 100644 src/util/virscsi.h create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-hostdev-scsi-boot.args create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-hostdev-scsi-boot.xml create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-hostdev-scsi-readonly.args create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-hostdev-scsi-readonly.xml -- 1.8.1.4

From: Han Cheng <hanc.fnst@cn.fujitsu.com> 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@cn.fujitsu.com> Signed-off-by: Osier Yang <jyang@redhat.com> --- 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

From: Han Cheng <hanc.fnst@cn.fujitsu.com> Adding two cap flags for scsi-generic: QEMU_CAPS_SCSI_GENERIC QEMU_CAPS_SCSI_GENERIC_BOOTINDEX Signed-off-by: Han Cheng <hanc.fnst@cn.fujitsu.com> --- src/qemu/qemu_capabilities.c | 14 ++++++++++++-- src/qemu/qemu_capabilities.h | 2 ++ tests/qemuhelpdata/qemu-1.0-device | 10 ++++++++++ tests/qemuhelpdata/qemu-1.1.0-device | 10 ++++++++++ tests/qemuhelpdata/qemu-1.2.0-device | 5 +++++ tests/qemuhelpdata/qemu-kvm-1.2.0-device | 5 +++++ tests/qemuhelptest.c | 19 ++++++++++++++----- 7 files changed, 58 insertions(+), 7 deletions(-) diff --git a/src/qemu/qemu_capabilities.c b/src/qemu/qemu_capabilities.c index 1d54477..c3e134e 100644 --- a/src/qemu/qemu_capabilities.c +++ b/src/qemu/qemu_capabilities.c @@ -222,6 +222,8 @@ VIR_ENUM_IMPL(virQEMUCaps, QEMU_CAPS_LAST, "tpm-tis", "nvram", /* 140 */ + "scsi-generic", + "scsi-generic.bootindex", ); struct _virQEMUCaps { @@ -1342,14 +1344,15 @@ struct virQEMUCapsStringFlags virQEMUCapsObjectTypes[] = { { "VGA", QEMU_CAPS_DEVICE_VGA }, { "cirrus-vga", QEMU_CAPS_DEVICE_CIRRUS_VGA }, { "vmware-svga", QEMU_CAPS_DEVICE_VMWARE_SVGA }, - { "usb-serial", QEMU_CAPS_DEVICE_USB_SERIAL}, - { "usb-net", QEMU_CAPS_DEVICE_USB_NET}, + { "usb-serial", QEMU_CAPS_DEVICE_USB_SERIAL }, + { "usb-net", QEMU_CAPS_DEVICE_USB_NET }, { "virtio-rng-pci", QEMU_CAPS_DEVICE_VIRTIO_RNG }, { "virtio-rng-s390", QEMU_CAPS_DEVICE_VIRTIO_RNG }, { "virtio-rng-ccw", QEMU_CAPS_DEVICE_VIRTIO_RNG }, { "rng-random", QEMU_CAPS_OBJECT_RNG_RANDOM }, { "rng-egd", QEMU_CAPS_OBJECT_RNG_EGD }, { "spapr-nvram", QEMU_CAPS_DEVICE_NVRAM }, + { "scsi-generic", QEMU_CAPS_DEVICE_SCSI_GENERIC }, }; static struct virQEMUCapsStringFlags virQEMUCapsObjectPropsVirtioBlk[] = { @@ -1395,6 +1398,10 @@ static struct virQEMUCapsStringFlags virQEMUCapsObjectPropsUsbHost[] = { { "bootindex", QEMU_CAPS_USB_HOST_BOOTINDEX }, }; +static struct virQEMUCapsStringFlags virQEMUCapsObjectPropsScsiGeneric[] = { + { "bootindex", QEMU_CAPS_DEVICE_SCSI_GENERIC_BOOTINDEX }, +}; + struct virQEMUCapsObjectTypeProps { const char *type; struct virQEMUCapsStringFlags *props; @@ -1428,6 +1435,8 @@ static struct virQEMUCapsObjectTypeProps virQEMUCapsObjectProps[] = { ARRAY_CARDINALITY(virQEMUCapsObjectPropsUsbRedir) }, { "usb-host", virQEMUCapsObjectPropsUsbHost, ARRAY_CARDINALITY(virQEMUCapsObjectPropsUsbHost) }, + { "scsi-generic", virQEMUCapsObjectPropsScsiGeneric, + ARRAY_CARDINALITY(virQEMUCapsObjectPropsScsiGeneric) }, }; @@ -1625,6 +1634,7 @@ virQEMUCapsExtractDeviceStr(const char *qemu, "-device", "usb-redir,?", "-device", "ide-drive,?", "-device", "usb-host,?", + "-device", "scsi-generic,?", NULL); /* qemu -help goes to stdout, but qemu -device ? goes to stderr. */ virCommandSetErrorBuffer(cmd, &output); diff --git a/src/qemu/qemu_capabilities.h b/src/qemu/qemu_capabilities.h index 9c2bf57..c7346bc 100644 --- a/src/qemu/qemu_capabilities.h +++ b/src/qemu/qemu_capabilities.h @@ -180,6 +180,8 @@ enum virQEMUCapsFlags { QEMU_CAPS_DEVICE_TPM_TIS = 139, /* -device tpm_tis */ QEMU_CAPS_DEVICE_NVRAM = 140, /* -global spapr-nvram.reg=xxxx */ + QEMU_CAPS_DEVICE_SCSI_GENERIC = 141, /* -device scsi-generic */ + QEMU_CAPS_DEVICE_SCSI_GENERIC_BOOTINDEX = 142, /* -device scsi-generic.bootindex */ QEMU_CAPS_LAST, /* this must always be the last item */ }; diff --git a/tests/qemuhelpdata/qemu-1.0-device b/tests/qemuhelpdata/qemu-1.0-device index 0bdfbbd..d557f0e 100644 --- a/tests/qemuhelpdata/qemu-1.0-device +++ b/tests/qemuhelpdata/qemu-1.0-device @@ -136,3 +136,13 @@ virtio-net-pci.romfile=string virtio-net-pci.rombar=uint32 virtio-net-pci.multifunction=on/off virtio-net-pci.command_serr_enable=on/off +scsi-generic.drive=drive +scsi-generic.logical_block_size=uint16 +scsi-generic.physical_block_size=uint16 +scsi-generic.min_io_size=uint16 +scsi-generic.opt_io_size=uint32 +scsi-generic.bootindex=int32 +scsi-generic.discard_granularity=uint32 +scsi-generic.channel=uint32 +scsi-generic.scsi-id=uint32 +scsi-generic.lun=uint32 diff --git a/tests/qemuhelpdata/qemu-1.1.0-device b/tests/qemuhelpdata/qemu-1.1.0-device index abbf850..7313a34 100644 --- a/tests/qemuhelpdata/qemu-1.1.0-device +++ b/tests/qemuhelpdata/qemu-1.1.0-device @@ -158,3 +158,13 @@ scsi-disk.dpofua=on/off scsi-disk.channel=uint32 scsi-disk.scsi-id=uint32 scsi-disk.lun=uint32 +scsi-generic.drive=drive +scsi-generic.logical_block_size=blocksize +scsi-generic.physical_block_size=blocksize +scsi-generic.min_io_size=uint16 +scsi-generic.opt_io_size=uint32 +scsi-generic.bootindex=int32 +scsi-generic.discard_granularity=uint32 +scsi-generic.channel=uint32 +scsi-generic.scsi-id=uint32 +scsi-generic.lun=uint32 diff --git a/tests/qemuhelpdata/qemu-1.2.0-device b/tests/qemuhelpdata/qemu-1.2.0-device index 5613e00..40845e4 100644 --- a/tests/qemuhelpdata/qemu-1.2.0-device +++ b/tests/qemuhelpdata/qemu-1.2.0-device @@ -208,3 +208,8 @@ usb-host.bootindex=int32 usb-host.pipeline=on/off usb-host.port=string usb-host.full-path=on/off +scsi-generic.drive=drive +scsi-generic.bootindex=int32 +scsi-generic.channel=uint32 +scsi-generic.scsi-id=uint32 +scsi-generic.lun=uint32 diff --git a/tests/qemuhelpdata/qemu-kvm-1.2.0-device b/tests/qemuhelpdata/qemu-kvm-1.2.0-device index 879a049..09e3ef7 100644 --- a/tests/qemuhelpdata/qemu-kvm-1.2.0-device +++ b/tests/qemuhelpdata/qemu-kvm-1.2.0-device @@ -220,3 +220,8 @@ usb-host.bootindex=int32 usb-host.pipeline=on/off usb-host.port=string usb-host.full-path=on/off +scsi-generic.drive=drive +scsi-generic.bootindex=int32 +scsi-generic.channel=uint32 +scsi-generic.scsi-id=uint32 +scsi-generic.lun=uint32 diff --git a/tests/qemuhelptest.c b/tests/qemuhelptest.c index 7290183..25aa55b 100644 --- a/tests/qemuhelptest.c +++ b/tests/qemuhelptest.c @@ -507,7 +507,8 @@ mymain(void) QEMU_CAPS_DEVICE_CIRRUS_VGA, QEMU_CAPS_DEVICE_VMWARE_SVGA, QEMU_CAPS_DEVICE_USB_SERIAL, - QEMU_CAPS_DEVICE_USB_NET); + QEMU_CAPS_DEVICE_USB_NET, + QEMU_CAPS_DEVICE_SCSI_GENERIC); DO_TEST("qemu-kvm-0.12.1.2-rhel61", 12001, 1, 0, QEMU_CAPS_VNC_COLON, QEMU_CAPS_NO_REBOOT, @@ -725,7 +726,9 @@ mymain(void) QEMU_CAPS_DEVICE_CIRRUS_VGA, QEMU_CAPS_DEVICE_VMWARE_SVGA, QEMU_CAPS_DEVICE_USB_SERIAL, - QEMU_CAPS_DEVICE_USB_NET); + QEMU_CAPS_DEVICE_USB_NET, + QEMU_CAPS_DEVICE_SCSI_GENERIC, + QEMU_CAPS_DEVICE_SCSI_GENERIC_BOOTINDEX); DO_TEST("qemu-1.1.0", 1001000, 0, 0, QEMU_CAPS_VNC_COLON, QEMU_CAPS_NO_REBOOT, @@ -816,7 +819,9 @@ mymain(void) QEMU_CAPS_DEVICE_USB_SERIAL, QEMU_CAPS_DEVICE_USB_NET, QEMU_CAPS_DTB, - QEMU_CAPS_IPV6_MIGRATION); + QEMU_CAPS_IPV6_MIGRATION, + QEMU_CAPS_DEVICE_SCSI_GENERIC, + QEMU_CAPS_DEVICE_SCSI_GENERIC_BOOTINDEX); DO_TEST("qemu-1.2.0", 1002000, 0, 0, QEMU_CAPS_VNC_COLON, QEMU_CAPS_NO_REBOOT, @@ -919,7 +924,9 @@ mymain(void) QEMU_CAPS_DEVICE_USB_NET, QEMU_CAPS_DTB, QEMU_CAPS_SCSI_MEGASAS, - QEMU_CAPS_IPV6_MIGRATION); + QEMU_CAPS_IPV6_MIGRATION, + QEMU_CAPS_DEVICE_SCSI_GENERIC, + QEMU_CAPS_DEVICE_SCSI_GENERIC_BOOTINDEX); DO_TEST("qemu-kvm-1.2.0", 1002000, 1, 0, QEMU_CAPS_VNC_COLON, QEMU_CAPS_NO_REBOOT, @@ -1027,7 +1034,9 @@ mymain(void) QEMU_CAPS_DEVICE_USB_NET, QEMU_CAPS_DTB, QEMU_CAPS_SCSI_MEGASAS, - QEMU_CAPS_IPV6_MIGRATION); + QEMU_CAPS_IPV6_MIGRATION, + QEMU_CAPS_DEVICE_SCSI_GENERIC, + QEMU_CAPS_DEVICE_SCSI_GENERIC_BOOTINDEX); return ret == 0 ? EXIT_SUCCESS : EXIT_FAILURE; } -- 1.8.1.4

From: Han Cheng <hanc.fnst@cn.fujitsu.com> This patch adds util functions for scsi hostdev. Signed-off-by: Han Cheng <hanc.fnst@cn.fujitsu.com> Signed-off-by: Osier Yang <jyang@redhat.com> --- v2.5 - v3: * A couple "char *" to "const char *" * s/unsigned int readonly/bool readonly/ * Use STRPREIFX instead of the wrong use of STRSKIP (which can introduce bug) for virSCSIDeviceGetAdapterId. * Improve virSCSIDeviceNew I'm not fan of introducing another list like virPCIDeviceList, which brought much trouble for us, but it doesn't hurt much to do clean up together later, since they are very similar. --- po/POTFILES.in | 1 + src/Makefile.am | 1 + src/libvirt_private.syms | 22 +++ src/util/virscsi.c | 407 +++++++++++++++++++++++++++++++++++++++++++++++ src/util/virscsi.h | 84 ++++++++++ 5 files changed, 515 insertions(+) create mode 100644 src/util/virscsi.c create mode 100644 src/util/virscsi.h diff --git a/po/POTFILES.in b/po/POTFILES.in index bf5a864..f3ea4da 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -174,6 +174,7 @@ src/util/virportallocator.c src/util/virprocess.c src/util/virrandom.c src/util/virsexpr.c +src/util/virscsi.c src/util/virsocketaddr.c src/util/virstatslinux.c src/util/virstoragefile.c diff --git a/src/Makefile.am b/src/Makefile.am index cd098db..a9769a4 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -111,6 +111,7 @@ UTIL_SOURCES = \ util/virportallocator.c util/virportallocator.h \ util/virprocess.c util/virprocess.h \ util/virrandom.h util/virrandom.c \ + util/virscsi.c util/virscsi.h \ util/virsexpr.c util/virsexpr.h \ util/virsocketaddr.h util/virsocketaddr.c \ util/virstatslinux.c util/virstatslinux.h \ diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index b3f8521..9bf92a6 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -1675,6 +1675,28 @@ virRandomGenerateWWN; virRandomInt; +# util/virscsi.h +virSCSIDeviceFileIterate; +virSCSIDeviceFree; +virSCSIDeviceGetAdapter; +virSCSIDeviceGetBus; +virSCSIDeviceGetDevStr; +virSCSIDeviceGetName; +virSCSIDeviceGetReadonly; +virSCSIDeviceGetTarget; +virSCSIDeviceGetUnit; +virSCSIDeviceGetUsedBy; +virSCSIDeviceListAdd; +virSCSIDeviceListCount; +virSCSIDeviceListDel; +virSCSIDeviceListFind; +virSCSIDeviceListGet; +virSCSIDeviceListNew; +virSCSIDeviceListSteal; +virSCSIDeviceNew; +virSCSIDeviceSetUsedBy; + + # util/virsexpr.h sexpr2string; sexpr_append; diff --git a/src/util/virscsi.c b/src/util/virscsi.c new file mode 100644 index 0000000..9a5e41c --- /dev/null +++ b/src/util/virscsi.c @@ -0,0 +1,407 @@ +/* + * virscsi.c: helper APIs for managing host SCSI devices + * + * Copyright (C) 2013 Fujitsu, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see + * <http://www.gnu.org/licenses/>. + * + * Authors: + * Han Cheng <hanc.fnst@cn.fujitsu.com> + */ + +#include <config.h> + +#include <dirent.h> +#include <fcntl.h> +#include <inttypes.h> +#include <limits.h> +#include <stdio.h> +#include <string.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> + +#include "virscsi.h" +#include "virlog.h" +#include "viralloc.h" +#include "virutil.h" +#include "virerror.h" + +#define SYSFS_SCSI_DEVICES "/sys/bus/scsi/devices" + +/* For virReportOOMError() and virReportSystemError() */ +#define VIR_FROM_THIS VIR_FROM_NONE + +struct _virSCSIDevice { + unsigned int adapter; + unsigned int bus; + unsigned int target; + unsigned int unit; + + char *name; /* adapter:bus:target:unit */ + char *id; /* model:vendor */ + char *path; + const char *used_by; /* name of the domain using this dev */ + + bool readonly; +}; + +struct _virSCSIDeviceList { + virObjectLockable parent; + unsigned int count; + virSCSIDevicePtr *devs; +}; + +static virClassPtr virSCSIDeviceListClass; + +static void virSCSIDeviceListDispose(void *obj); + +static int +virSCSIOnceInit(void) +{ + if (!(virSCSIDeviceListClass = virClassNew(virClassForObjectLockable(), + "virSCSIDeviceList", + sizeof(virSCSIDeviceList), + virSCSIDeviceListDispose))) + return -1; + + return 0; +} + +VIR_ONCE_GLOBAL_INIT(virSCSI) + +static int +virSCSIDeviceGetAdapterId(const char *adapter, + unsigned int *adapter_id) +{ + if (STRPREFIX(adapter, "scsi_host")) { + if (virStrToLong_ui(adapter + strlen("scsi_host"), + NULL, 0, adapter_id) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Cannot parse adapter '%s'"), adapter); + return -1; + } + } + + return 0; +} + +char * +virSCSIDeviceGetDevStr(const char *adapter, + unsigned int bus, + unsigned int target, + unsigned int unit) +{ + DIR *dir = NULL; + struct dirent *entry; + char *path = NULL; + char *sg = NULL; + unsigned int adapter_id; + + if (virSCSIDeviceGetAdapterId(adapter, &adapter_id) < 0) + return NULL; + + if (virAsprintf(&path, + SYSFS_SCSI_DEVICES "/%d:%d:%d:%d/scsi_generic", + adapter_id, bus, target, unit) < 0) { + virReportOOMError(); + return NULL; + } + + if (!(dir = opendir(path))) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Failed to open %s"), path); + goto cleanup; + } + + while ((entry = readdir(dir))) { + if (entry->d_name[0] == '.') + continue; + + if (virAsprintf(&sg, "%s", entry->d_name) < 0) { + virReportOOMError(); + goto cleanup; + } + } + +cleanup: + closedir(dir); + VIR_FREE(path); + return sg; +} + +virSCSIDevicePtr +virSCSIDeviceNew(const char *adapter, + unsigned int bus, + unsigned int target, + unsigned int unit, + bool readonly) +{ + virSCSIDevicePtr dev, ret = NULL; + char *sg = NULL; + char *vendor_path = NULL; + char *model_path = NULL; + char *vendor = NULL; + char *model = NULL; + + if (VIR_ALLOC(dev) < 0) { + virReportOOMError(); + return NULL; + } + + dev->bus = bus; + dev->target = target; + dev->unit = unit; + dev->readonly = readonly; + + if (!(sg = virSCSIDeviceGetDevStr(adapter, bus, target, unit))) + goto cleanup; + + if (virSCSIDeviceGetAdapterId(adapter, &dev->adapter) < 0) + goto cleanup; + + if (virAsprintf(&dev->name, "%d:%d:%d:%d", dev->adapter, + dev->bus, dev->bus, dev->unit) < 0 || + virAsprintf(&dev->path, "/dev/%s", sg) < 0) { + virReportOOMError(); + goto cleanup; + } + + if (access(dev->path, F_OK) != 0) { + virReportSystemError(errno, + _("Device %s not found: could not access %s"), + dev->name, dev->path); + goto cleanup; + } + + if (virAsprintf(&vendor_path, + SYSFS_SCSI_DEVICES "/%s/vendor", dev->name) < 0 || + virAsprintf(&model_path, + SYSFS_SCSI_DEVICES "/%s/model", dev->name) < 0) { + virReportOOMError(); + goto cleanup; + } + + if (virFileReadAll(vendor_path, 1024, &vendor) < 0) + goto cleanup; + + if (virFileReadAll(model_path, 1024, &model) < 0) + goto cleanup; + + virTrimSpaces(vendor, NULL); + virTrimSpaces(model, NULL); + + if (virAsprintf(&dev->id, "%s:%s", vendor, model) < 0) { + virReportOOMError(); + goto cleanup; + } + + ret = dev; +cleanup: + VIR_FREE(vendor); + VIR_FREE(model); + VIR_FREE(vendor_path); + VIR_FREE(model_path); + if (!ret) + virSCSIDeviceFree(dev); + return ret; +} + +void +virSCSIDeviceFree(virSCSIDevicePtr dev) +{ + if (!dev) + return; + + VIR_FREE(dev->id); + VIR_FREE(dev->name); + VIR_FREE(dev->path); + VIR_FREE(dev); +} + +void +virSCSIDeviceSetUsedBy(virSCSIDevicePtr dev, + const char *name) +{ + dev->used_by = name; +} + +const char * +virSCSIDeviceGetUsedBy(virSCSIDevicePtr dev) +{ + return dev->used_by; +} + +const char * +virSCSIDeviceGetName(virSCSIDevicePtr dev) +{ + return dev->name; +} + +unsigned int +virSCSIDeviceGetAdapter(virSCSIDevicePtr dev) +{ + return dev->adapter; +} + +unsigned int +virSCSIDeviceGetBus(virSCSIDevicePtr dev) +{ + return dev->bus; +} + +unsigned int +virSCSIDeviceGetTarget(virSCSIDevicePtr dev) +{ + return dev->target; +} + +unsigned int +virSCSIDeviceGetUnit(virSCSIDevicePtr dev) +{ + return dev->unit; +} + +bool +virSCSIDeviceGetReadonly(virSCSIDevicePtr dev) +{ + return dev->readonly; +} + +int +virSCSIDeviceFileIterate(virSCSIDevicePtr dev, + virSCSIDeviceFileActor actor, + void *opaque) +{ + return (actor)(dev, dev->path, opaque); +} + +virSCSIDeviceListPtr +virSCSIDeviceListNew(void) +{ + virSCSIDeviceListPtr list; + + if (virSCSIInitialize() < 0) + return NULL; + + if (!(list = virObjectLockableNew(virSCSIDeviceListClass))) + return NULL; + + return list; +} + +static void +virSCSIDeviceListDispose(void *obj) +{ + virSCSIDeviceListPtr list = obj; + int i; + + for (i = 0; i < list->count; i++) + virSCSIDeviceFree(list->devs[i]); + + VIR_FREE(list->devs); +} + +int +virSCSIDeviceListAdd(virSCSIDeviceListPtr list, + virSCSIDevicePtr dev) +{ + if (virSCSIDeviceListFind(list, dev)) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Device %s already exists"), + dev->name); + return -1; + } + + if (VIR_REALLOC_N(list->devs, list->count + 1) < 0) { + virReportOOMError(); + return -1; + } + + list->devs[list->count++] = dev; + + return 0; +} + +virSCSIDevicePtr +virSCSIDeviceListGet(virSCSIDeviceListPtr list, int idx) +{ + if (idx >= list->count || idx < 0) + return NULL; + + return list->devs[idx]; +} + +int +virSCSIDeviceListCount(virSCSIDeviceListPtr list) +{ + return list->count; +} + +virSCSIDevicePtr +virSCSIDeviceListSteal(virSCSIDeviceListPtr list, + virSCSIDevicePtr dev) +{ + virSCSIDevicePtr ret = NULL; + int i; + + for (i = 0; i < list->count; i++) { + if (list->devs[i]->adapter != dev->adapter || + list->devs[i]->bus != dev->bus || + list->devs[i]->target != dev->target || + list->devs[i]->unit != dev->unit) + continue; + + ret = list->devs[i]; + + if (i != list->count--) + memmove(&list->devs[i], + &list->devs[i+1], + sizeof(*list->devs) * (list->count - i)); + + if (VIR_REALLOC_N(list->devs, list->count) < 0) { + ; /* not fatal */ + } + + break; + } + + return ret; +} + +void +virSCSIDeviceListDel(virSCSIDeviceListPtr list, + virSCSIDevicePtr dev) +{ + virSCSIDevicePtr ret = virSCSIDeviceListSteal(list, dev); + virSCSIDeviceFree(ret); +} + +virSCSIDevicePtr +virSCSIDeviceListFind(virSCSIDeviceListPtr list, + virSCSIDevicePtr dev) +{ + int i; + + for (i = 0; i < list->count; i++) { + if (list->devs[i]->adapter == dev->adapter && + list->devs[i]->bus == dev->bus && + list->devs[i]->target == dev->target && + list->devs[i]->unit == dev->unit) + return list->devs[i]; + } + + return NULL; +} diff --git a/src/util/virscsi.h b/src/util/virscsi.h new file mode 100644 index 0000000..9d63acc --- /dev/null +++ b/src/util/virscsi.h @@ -0,0 +1,84 @@ +/* + * virscsi.h: helper APIs for managing host SCSI devices + * + * Copyright (C) 2013 Fujitsu, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see + * <http://www.gnu.org/licenses/>. + * + * Authors: + * Han Cheng <hanc.fnst@cn.fujitsu.com> + */ + +#ifndef __VIR_SCSI_H__ +# define __VIR_SCSI_H__ + +# include "internal.h" +# include "virobject.h" + +typedef struct _virSCSIDevice virSCSIDevice; +typedef virSCSIDevice *virSCSIDevicePtr; + +typedef struct _virSCSIDeviceList virSCSIDeviceList; +typedef virSCSIDeviceList *virSCSIDeviceListPtr; + +char *virSCSIDeviceGetDevStr(const char *adapter, + unsigned int bus, + unsigned int target, + unsigned int unit); + +virSCSIDevicePtr virSCSIDeviceNew(const char *adapter, + unsigned int bus, + unsigned int target, + unsigned int unit, + bool readonly); + +void virSCSIDeviceFree(virSCSIDevicePtr dev); +void virSCSIDeviceSetUsedBy(virSCSIDevicePtr dev, const char *name); +const char *virSCSIDeviceGetUsedBy(virSCSIDevicePtr dev); +const char *virSCSIDeviceGetName(virSCSIDevicePtr dev); +unsigned int virSCSIDeviceGetAdapter(virSCSIDevicePtr dev); +unsigned int virSCSIDeviceGetBus(virSCSIDevicePtr dev); +unsigned int virSCSIDeviceGetTarget(virSCSIDevicePtr dev); +unsigned int virSCSIDeviceGetUnit(virSCSIDevicePtr dev); +bool virSCSIDeviceGetReadonly(virSCSIDevicePtr dev); + +/* + * Callback that will be invoked once for each file + * associated with / used for SCSI host device access. + * + * Should return 0 if successfully processed, or + * -1 to indicate error and abort iteration + */ +typedef int (*virSCSIDeviceFileActor)(virSCSIDevicePtr dev, + const char *path, void *opaque); + +int virSCSIDeviceFileIterate(virSCSIDevicePtr dev, + virSCSIDeviceFileActor actor, + void *opaque); + +virSCSIDeviceListPtr virSCSIDeviceListNew(void); +int virSCSIDeviceListAdd(virSCSIDeviceListPtr list, + virSCSIDevicePtr dev); +virSCSIDevicePtr virSCSIDeviceListGet(virSCSIDeviceListPtr list, + int idx); +int virSCSIDeviceListCount(virSCSIDeviceListPtr list); +virSCSIDevicePtr virSCSIDeviceListSteal(virSCSIDeviceListPtr list, + virSCSIDevicePtr dev); +void virSCSIDeviceListDel(virSCSIDeviceListPtr list, + virSCSIDevicePtr dev); +virSCSIDevicePtr virSCSIDeviceListFind(virSCSIDeviceListPtr list, + virSCSIDevicePtr dev); + +#endif /* __VIR_SCSI_H__ */ -- 1.8.1.4

From: Han Cheng <hanc.fnst@cn.fujitsu.com> Except the scsi host device's controller is "lsilogic", mapping between the libvirt attributes and scsi-generic properties is: libvirt qemu ----------------------------------------- controller bus ($libvirt_controller.0) bus channel target scsi-id unit lun For scsi host device with "lsilogic" controller, the mapping is: ('target (libvirt)' must be 0, as it's not used; 'unit (libvirt) must <= 7). libvirt qemu ---------------------------------------------------------- controller && bus bus ($libvirt_controller.$libvirt_bus) unit scsi-id Regardless of whether the controller is "lsilogic" or not, the "bus (libvirt)" must be 0. (It's not good to hardcode/hard-check limits of these attributes, and even worse, these limits are not documented, one has to find out by either testing or reading the qemu code, I'm looking forward to qemu expose limits like these one day). For example, exposing "max_target", "max_lun" for megasas: static const struct SCSIBusInfo megasas_scsi_info = { .tcq = true, .max_target = MFI_MAX_LD, .max_lun = 255, .transfer_data = megasas_xfer_complete, .get_sg_list = megasas_get_sg_list, .complete = megasas_command_complete, .cancel = megasas_command_cancel, }; Example of the qemu command line (lsilogic controller): -drive file=/dev/sg2,if=none,id=drive-hostdev-scsi_host7-0-0-0 \ -device scsi-generic,bus=scsi0.0,scsi-id=8,\ drive=drive-hostdev-scsi_host7-0-0-0,id=hostdev-scsi_host7-0-0-0 Example of the qemu command line (virtio-scsi controller): -drive file=/dev/sg2,if=none,id=drive-hostdev-scsi_host7-0-0-0 \ -device scsi-generic,bus=scsi0.0,channel=0,scsi-id=128,lun=128,\ drive=drive-hostdev-scsi_host7-0-0-0,id=hostdev-scsi_host7-0-0-0 Signed-off-by: Han Cheng <hanc.fnst@cn.fujitsu.com> Signed-off-by: Osier Yang <jyang@redhat.com> --- v2.5 - v3: * Add support for all other controllers, but not only virtio-scsi * Add checking for "bus == 0" * Add checking for "target == 0" && "unit <= 7" for scsi host device which is on "lsilogic" controller. * Integrate xml2argv test from 10/10 of v2.5 into this patch --- src/qemu/qemu_command.c | 154 ++++++++++++++++++++- src/qemu/qemu_command.h | 6 + .../qemuxml2argv-hostdev-scsi-boot.args | 9 ++ .../qemuxml2argv-hostdev-scsi-boot.xml | 34 +++++ .../qemuxml2argv-hostdev-scsi-readonly.args | 9 ++ .../qemuxml2argv-hostdev-scsi-readonly.xml | 4 +- tests/qemuxml2argvtest.c | 9 ++ tests/qemuxml2xmltest.c | 1 + 8 files changed, 221 insertions(+), 5 deletions(-) create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-hostdev-scsi-boot.args create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-hostdev-scsi-boot.xml create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-hostdev-scsi-readonly.args diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c index 6cca229..1398664 100644 --- a/src/qemu/qemu_command.c +++ b/src/qemu/qemu_command.c @@ -47,6 +47,7 @@ #include "device_conf.h" #include "virstoragefile.h" #include "virtpm.h" +#include "virscsi.h" #include <sys/stat.h> #include <fcntl.h> @@ -664,7 +665,16 @@ qemuAssignDeviceHostdevAlias(virDomainDefPtr def, virDomainHostdevDefPtr hostdev } } - if (virAsprintf(&hostdev->info->alias, "hostdev%d", idx) < 0) { + if (hostdev->source.subsys.type == VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_SCSI) { + if (virAsprintf(&hostdev->info->alias, "hostdev-%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) { + virReportOOMError(); + return -1; + } + } else if (virAsprintf(&hostdev->info->alias, "hostdev%d", idx) < 0) { virReportOOMError(); return -1; } @@ -4408,6 +4418,110 @@ qemuBuildUSBHostdevUsbDevStr(virDomainHostdevDefPtr dev) return ret; } +char * +qemuBuildSCSIHostdevDrvStr(virDomainHostdevDefPtr dev, + virQEMUCapsPtr qemuCaps) +{ + virBuffer buf = VIR_BUFFER_INITIALIZER; + char *sg = NULL; + + if (!(sg = virSCSIDeviceGetDevStr(dev->source.subsys.u.scsi.adapter, + dev->source.subsys.u.scsi.bus, + dev->source.subsys.u.scsi.target, + dev->source.subsys.u.scsi.unit))) { + goto error; + } + + virBufferAsprintf(&buf, "file=/dev/%s,if=none", sg); + virBufferAsprintf(&buf, ",id=%s-%s", + virDomainDeviceAddressTypeToString(dev->info->type), + dev->info->alias); + + if (dev->readonly && + virQEMUCapsGet(qemuCaps, QEMU_CAPS_DRIVE_READONLY)) + virBufferAsprintf(&buf, ",readonly=on"); + + if (virBufferError(&buf)) { + virReportOOMError(); + goto error; + } + + return virBufferContentAndReset(&buf); +error: + VIR_FREE(sg); + virBufferFreeAndReset(&buf); + return NULL; +} + + +char * +qemuBuildSCSIHostdevDevStr(virDomainDefPtr def, + virDomainHostdevDefPtr dev, + virQEMUCapsPtr qemuCaps) +{ + virBuffer buf = VIR_BUFFER_INITIALIZER; + int model = -1; + + model = virDomainDeviceFindControllerModel(def, dev->info, + VIR_DOMAIN_CONTROLLER_TYPE_SCSI); + + if (qemuSetScsiControllerModel(def, qemuCaps, &model) < 0) + goto error; + + if (dev->info->addr.drive.bus != 0) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("bus must be 0 for scsi host device")); + goto error; + } + + if (model == VIR_DOMAIN_CONTROLLER_MODEL_SCSI_LSILOGIC) { + if (dev->info->addr.drive.target != 0) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("target must be 0 for scsi host device " + "if its controller model is 'lsilogic'")); + goto error; + } + + if (dev->info->addr.drive.unit > 7) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("unit must be not more than 7 for scsi host " + "device if its controller model is 'lsilogic'")); + goto error; + } + } + + virBufferAsprintf(&buf, "scsi-generic"); + + if (model == VIR_DOMAIN_CONTROLLER_MODEL_SCSI_LSILOGIC) { + virBufferAsprintf(&buf, ",bus=scsi%d.%d,scsi-id=%d", + dev->info->addr.drive.controller, + dev->info->addr.drive.bus, + dev->info->addr.drive.unit); + } else { + virBufferAsprintf(&buf, ",bus=scsi%d.0,channel=%d,scsi-id=%d,lun=%d", + dev->info->addr.drive.controller, + dev->info->addr.drive.bus, + dev->info->addr.drive.target, + dev->info->addr.drive.unit); + } + + virBufferAsprintf(&buf, ",drive=%s-%s,id=%s", + virDomainDeviceAddressTypeToString(dev->info->type), + dev->info->alias, dev->info->alias); + + if (dev->info->bootIndex) + virBufferAsprintf(&buf, ",bootindex=%d", dev->info->bootIndex); + + if (virBufferError(&buf)) { + virReportOOMError(); + goto error; + } + + return virBufferContentAndReset(&buf); +error: + virBufferFreeAndReset(&buf); + return NULL; +} /* This function outputs a -chardev command line option which describes only the @@ -7661,10 +7775,11 @@ qemuBuildCommandLine(virConnectPtr conn, if (hostdev->info->bootIndex) { if (hostdev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS || (hostdev->source.subsys.type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI && - hostdev->source.subsys.type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB)) { + hostdev->source.subsys.type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB && + hostdev->source.subsys.type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_SCSI)) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("booting from assigned devices is only" - " supported for PCI and USB devices")); + " supported for PCI, USB and SCSI devices")); goto error; } else { if (hostdev->source.subsys.type == VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI && @@ -7681,6 +7796,39 @@ qemuBuildCommandLine(virConnectPtr conn, " supported with this version of qemu")); goto error; } + if (hostdev->source.subsys.type == VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_SCSI && + !virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_SCSI_GENERIC_BOOTINDEX)) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("booting from assigned SCSI devices is not" + " supported with this version of qemu")); + goto error; + } + } + } + + /* SCSI */ + if (hostdev->mode == VIR_DOMAIN_HOSTDEV_MODE_SUBSYS && + hostdev->source.subsys.type == VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_SCSI) { + if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_DRIVE) && + virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE) && + virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_SCSI_GENERIC)) { + char *drvstr; + + virCommandAddArg(cmd, "-drive"); + if (!(drvstr = qemuBuildSCSIHostdevDrvStr(hostdev, qemuCaps))) + goto error; + virCommandAddArg(cmd, drvstr); + VIR_FREE(drvstr); + + virCommandAddArg(cmd, "-device"); + if (!(devstr = qemuBuildSCSIHostdevDevStr(def, hostdev, qemuCaps))) + goto error; + virCommandAddArg(cmd, devstr); + VIR_FREE(devstr); + } else { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("SCSI passthrough is not supported by this version of qemu")); + goto error; } } diff --git a/src/qemu/qemu_command.h b/src/qemu/qemu_command.h index 1789c20..fcf9892 100644 --- a/src/qemu/qemu_command.h +++ b/src/qemu/qemu_command.h @@ -139,6 +139,12 @@ char * qemuBuildUSBHostdevUsbDevStr(virDomainHostdevDefPtr dev); char * qemuBuildUSBHostdevDevStr(virDomainHostdevDefPtr dev, virQEMUCapsPtr qemuCaps); +char * qemuBuildSCSIHostdevDrvStr(virDomainHostdevDefPtr dev, + virQEMUCapsPtr qemuCaps); +char * qemuBuildSCSIHostdevDevStr(virDomainDefPtr def, + virDomainHostdevDefPtr dev, + virQEMUCapsPtr qemuCaps); + char * qemuBuildHubDevStr(virDomainHubDefPtr dev, virQEMUCapsPtr qemuCaps); char * qemuBuildRedirdevDevStr(virDomainDefPtr def, virDomainRedirdevDefPtr dev, diff --git a/tests/qemuxml2argvdata/qemuxml2argv-hostdev-scsi-boot.args b/tests/qemuxml2argvdata/qemuxml2argv-hostdev-scsi-boot.args new file mode 100644 index 0000000..cd22672 --- /dev/null +++ b/tests/qemuxml2argvdata/qemuxml2argv-hostdev-scsi-boot.args @@ -0,0 +1,9 @@ +LC_ALL=C PATH=/bin HOME=/home/test USER=test LOGNAME=test /usr/bin/qemu -S -M \ +pc -m 214 -smp 1 -nographic -nodefconfig -nodefaults -monitor \ +unix:/tmp/test-monitor,server,nowait -no-acpi \ +-device virtio-scsi-pci,id=scsi0,bus=pci.0,addr=0x3 -usb \ +-drive file=/dev/HostVG/QEMUGuest2,if=none,id=drive-ide0-0-0 \ +-device ide-drive,bus=ide.0,unit=0,drive=drive-ide0-0-0,id=ide0-0-0 \ +-drive file=/dev/sg0,if=none,id=drive-hostdev-scsi_host0-0-0-0 \ +-device scsi-generic,bus=scsi0.0,channel=0,scsi-id=4,lun=8,drive=drive-hostdev-scsi_host0-0-0-0,id=hostdev-scsi_host0-0-0-0,bootindex=1 \ +-device virtio-balloon-pci,id=balloon0,bus=pci.0,addr=0x4 diff --git a/tests/qemuxml2argvdata/qemuxml2argv-hostdev-scsi-boot.xml b/tests/qemuxml2argvdata/qemuxml2argv-hostdev-scsi-boot.xml new file mode 100644 index 0000000..e3de719 --- /dev/null +++ b/tests/qemuxml2argvdata/qemuxml2argv-hostdev-scsi-boot.xml @@ -0,0 +1,34 @@ +<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> + </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> + <boot order='1'/> + <address type='drive' controller='0' bus='0' target='4' unit='8'/> + </hostdev> + <memballoon model='virtio'/> + </devices> +</domain> diff --git a/tests/qemuxml2argvdata/qemuxml2argv-hostdev-scsi-readonly.args b/tests/qemuxml2argvdata/qemuxml2argv-hostdev-scsi-readonly.args new file mode 100644 index 0000000..42fd60b --- /dev/null +++ b/tests/qemuxml2argvdata/qemuxml2argv-hostdev-scsi-readonly.args @@ -0,0 +1,9 @@ +LC_ALL=C PATH=/bin HOME=/home/test USER=test LOGNAME=test /usr/bin/qemu -S -M pc \ +-m 214 -smp 1 -nographic -nodefconfig -nodefaults -monitor \ +unix:/tmp/test-monitor,server,nowait -no-acpi -boot c \ +-device lsi,id=scsi0,bus=pci.0,addr=0x3 -usb \ +-drive file=/dev/HostVG/QEMUGuest2,if=none,id=drive-ide0-0-0 \ +-device ide-drive,bus=ide.0,unit=0,drive=drive-ide0-0-0,id=ide0-0-0 \ +-drive file=/dev/sg0,if=none,id=drive-hostdev-scsi_host0-0-0-0,readonly=on \ +-device scsi-generic,bus=scsi0.0,scsi-id=7,drive=drive-hostdev-scsi_host0-0-0-0,id=hostdev-scsi_host0-0-0-0 \ +-device virtio-balloon-pci,id=balloon0,bus=pci.0,addr=0x4 diff --git a/tests/qemuxml2argvdata/qemuxml2argv-hostdev-scsi-readonly.xml b/tests/qemuxml2argvdata/qemuxml2argv-hostdev-scsi-readonly.xml index 11d1712..83936b1 100644 --- a/tests/qemuxml2argvdata/qemuxml2argv-hostdev-scsi-readonly.xml +++ b/tests/qemuxml2argvdata/qemuxml2argv-hostdev-scsi-readonly.xml @@ -19,7 +19,7 @@ <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='scsi' index='0'/> <controller type='usb' index='0'/> <controller type='ide' index='0'/> <hostdev mode='subsystem' type='scsi' managed='yes'> @@ -28,7 +28,7 @@ <address bus='0' target='0' unit='0'/> </source> <readonly/> - <address type='drive' controller='0' bus='0' target='4' unit='8'/> + <address type='drive' controller='0' bus='0' target='0' unit='7'/> </hostdev> <memballoon model='virtio'/> </devices> diff --git a/tests/qemuxml2argvtest.c b/tests/qemuxml2argvtest.c index f40d002..93a5a9a 100644 --- a/tests/qemuxml2argvtest.c +++ b/tests/qemuxml2argvtest.c @@ -951,6 +951,15 @@ mymain(void) DO_TEST("tpm-passthrough", QEMU_CAPS_DEVICE, QEMU_CAPS_DEVICE_TPM_PASSTHROUGH, QEMU_CAPS_DEVICE_TPM_TIS); + DO_TEST("hostdev-scsi-boot", + QEMU_CAPS_DRIVE, QEMU_CAPS_DEVICE, QEMU_CAPS_NODEFCONFIG, + QEMU_CAPS_VIRTIO_SCSI, QEMU_CAPS_DEVICE_SCSI_GENERIC, + QEMU_CAPS_BOOTINDEX, QEMU_CAPS_DEVICE_SCSI_GENERIC_BOOTINDEX); + DO_TEST("hostdev-scsi-readonly", + QEMU_CAPS_DRIVE, QEMU_CAPS_DRIVE_READONLY, + QEMU_CAPS_DEVICE, QEMU_CAPS_NODEFCONFIG, + QEMU_CAPS_SCSI_LSI, QEMU_CAPS_DEVICE_SCSI_GENERIC); + virObjectUnref(driver.config); virObjectUnref(driver.caps); virObjectUnref(driver.xmlopt); diff --git a/tests/qemuxml2xmltest.c b/tests/qemuxml2xmltest.c index a2727b0..bcd26df 100644 --- a/tests/qemuxml2xmltest.c +++ b/tests/qemuxml2xmltest.c @@ -231,6 +231,7 @@ mymain(void) DO_TEST("hostdev-usb-address"); DO_TEST("hostdev-pci-address"); DO_TEST("hostdev-scsi-readonly"); + DO_TEST("hostdev-scsi-boot"); DO_TEST("pci-rom"); DO_TEST("encrypted-disk"); -- 1.8.1.4

Il 26/04/2013 22:15, Osier Yang ha scritto:
--- v2.5 - v3: * Add support for all other controllers, but not only virtio-scsi * Add checking for "bus == 0"
Don't check for bus == 0, ibmvscsi for example supports buses up to 3. Paolo
* Add checking for "target == 0" && "unit <= 7" for scsi host device which is on "lsilogic" controller. * Integrate xml2argv test from 10/10 of v2.5 into this patch ---

From: Han Cheng <hanc.fnst@cn.fujitsu.com> Although virtio-scsi supports SCSI PR, the device on host may do not support it. To avoid losing data, we only allow one scsi hostdev to be passthroughed to one live guest, Just like what we do for PCI and USB devices. Signed-off-by: Han Cheng <hanc.fnst@cn.fujitsu.com> --- v2.5 - v3: * Small changes, mainly on errors & coding style --- src/qemu/qemu_conf.h | 2 + src/qemu/qemu_driver.c | 3 + src/qemu/qemu_hostdev.c | 213 ++++++++++++++++++++++++++++++++++++++++++++++++ src/qemu/qemu_hostdev.h | 10 +++ src/qemu/qemu_process.c | 3 + 5 files changed, 231 insertions(+) diff --git a/src/qemu/qemu_conf.h b/src/qemu/qemu_conf.h index 77d3d2f..30a2a22 100644 --- a/src/qemu/qemu_conf.h +++ b/src/qemu/qemu_conf.h @@ -36,6 +36,7 @@ # include "security/security_manager.h" # include "virpci.h" # include "virusb.h" +# include "virscsi.h" # include "cpu_conf.h" # include "driver.h" # include "virportallocator.h" @@ -203,6 +204,7 @@ struct _virQEMUDriver { virPCIDeviceListPtr activePciHostdevs; virPCIDeviceListPtr inactivePciHostdevs; virUSBDeviceListPtr activeUsbHostdevs; + virSCSIDeviceListPtr activeScsiHostdevs; /* Immutable pointer. Unsafe APIs. XXX */ virHashTablePtr sharedDisks; diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 13d6d60..24eca08 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -675,6 +675,9 @@ qemuStateInitialize(bool privileged, if ((qemu_driver->inactivePciHostdevs = virPCIDeviceListNew()) == NULL) goto error; + if (!(qemu_driver->activeScsiHostdevs = virSCSIDeviceListNew())) + goto error; + if (!(qemu_driver->sharedDisks = virHashCreate(30, qemuSharedDiskEntryFree))) goto error; diff --git a/src/qemu/qemu_hostdev.c b/src/qemu/qemu_hostdev.c index 0db9ddd..445db67 100644 --- a/src/qemu/qemu_hostdev.c +++ b/src/qemu/qemu_hostdev.c @@ -29,6 +29,7 @@ #include "viralloc.h" #include "virpci.h" #include "virusb.h" +#include "virscsi.h" #include "virnetdev.h" #define VIR_FROM_THIS VIR_FROM_QEMU @@ -214,6 +215,47 @@ cleanup: return ret; } +int +qemuUpdateActiveScsiHostdevs(virQEMUDriverPtr driver, + virDomainDefPtr def) +{ + virDomainHostdevDefPtr hostdev = NULL; + int i; + int ret = -1; + + if (!def->nhostdevs) + return 0; + + virObjectLock(driver->activeScsiHostdevs); + for (i = 0; i < def->nhostdevs; i++) { + virSCSIDevicePtr scsi = NULL; + hostdev = def->hostdevs[i]; + + if (hostdev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS || + hostdev->source.subsys.type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_SCSI) + continue; + + if (!(scsi = virSCSIDeviceNew(hostdev->source.subsys.u.scsi.adapter, + hostdev->source.subsys.u.scsi.bus, + hostdev->source.subsys.u.scsi.target, + hostdev->source.subsys.u.scsi.unit, + hostdev->readonly))) + goto cleanup; + + virSCSIDeviceSetUsedBy(scsi, def->name); + + if (virSCSIDeviceListAdd(driver->activeScsiHostdevs, scsi) < 0) { + virSCSIDeviceFree(scsi); + goto cleanup; + } + } + ret = 0; + +cleanup: + virObjectUnlock(driver->activeScsiHostdevs); + return ret; +} + static int qemuDomainHostdevPciSysfsPath(virDomainHostdevDefPtr hostdev, char **sysfs_path) { @@ -811,6 +853,107 @@ cleanup: return ret; } +int +qemuPrepareHostdevSCSIDevices(virQEMUDriverPtr driver, + const char *name, + virDomainHostdevDefPtr *hostdevs, + int nhostdevs) +{ + int i, j, count; + virSCSIDeviceListPtr list; + virSCSIDevicePtr tmp; + + /* To prevent situation where SCSI device is assigned to two domains + * we need to keep a list of currently assigned SCSI devices. + * This is done in several loops which cannot be joined into one big + * loop. See qemuPrepareHostdevPCIDevices() + */ + if (!(list = virSCSIDeviceListNew())) + goto cleanup; + + /* Loop 1: build temporary list */ + for (i = 0 ; i < nhostdevs ; i++) { + virDomainHostdevDefPtr hostdev = hostdevs[i]; + virSCSIDevicePtr scsi; + + if (hostdev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS || + hostdev->source.subsys.type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_SCSI) + continue; + + if (hostdev->managed) { + virReportError(VIR_ERR_XML_ERROR, "%s", + _("SCSI host device doesn't support managed mode")); + goto cleanup; + } + + if (!(scsi = virSCSIDeviceNew(hostdev->source.subsys.u.scsi.adapter, + hostdev->source.subsys.u.scsi.bus, + hostdev->source.subsys.u.scsi.target, + hostdev->source.subsys.u.scsi.unit, + hostdev->readonly))) + goto cleanup; + + if (scsi && virSCSIDeviceListAdd(list, scsi) < 0) { + virSCSIDeviceFree(scsi); + goto cleanup; + } + } + + /* Loop 2: Mark devices in temporary list as used by @name + * and add them to driver list. However, if something goes + * wrong, perform rollback. + */ + virObjectLock(driver->activeScsiHostdevs); + count = virSCSIDeviceListCount(list); + + for (i = 0; i < count; i++) { + virSCSIDevicePtr scsi = virSCSIDeviceListGet(list, i); + if ((tmp = virSCSIDeviceListFind(driver->activeScsiHostdevs, scsi))) { + const char *other_name = virSCSIDeviceGetUsedBy(tmp); + + if (other_name) + virReportError(VIR_ERR_OPERATION_INVALID, + _("SCSI device %s is in use by domain %s"), + virSCSIDeviceGetName(tmp), other_name); + else + virReportError(VIR_ERR_OPERATION_INVALID, + _("SCSI device %s is already in use"), + virSCSIDeviceGetName(tmp)); + goto error; + } + + virSCSIDeviceSetUsedBy(scsi, name); + VIR_DEBUG("Adding %s to activeScsiHostdevs", virSCSIDeviceGetName(scsi)); + + if (virSCSIDeviceListAdd(driver->activeScsiHostdevs, scsi) < 0) + goto error; + } + + virObjectUnlock(driver->activeScsiHostdevs); + + /* Loop 3: Temporary list was successfully merged with + * driver list, so steal all items to avoid freeing them + * when freeing temporary list. + */ + while (virSCSIDeviceListCount(list) > 0) { + tmp = virSCSIDeviceListGet(list, 0); + virSCSIDeviceListSteal(list, tmp); + } + + virObjectUnref(list); + return 0; + +error: + for (j = 0; j < i; j++) { + tmp = virSCSIDeviceListGet(list, i); + virSCSIDeviceListSteal(driver->activeScsiHostdevs, tmp); + } + virObjectUnlock(driver->activeScsiHostdevs); +cleanup: + virObjectUnref(list); + return -1; +} + int qemuPrepareHostDevices(virQEMUDriverPtr driver, virDomainDefPtr def, bool coldBoot) @@ -825,6 +968,10 @@ int qemuPrepareHostDevices(virQEMUDriverPtr driver, if (qemuPrepareHostUSBDevices(driver, def, coldBoot) < 0) return -1; + if (qemuPrepareHostdevSCSIDevices(driver, def->name, + def->hostdevs, def->nhostdevs) < 0) + return -1; + return 0; } @@ -1009,6 +1156,69 @@ qemuDomainReAttachHostUsbDevices(virQEMUDriverPtr driver, virObjectUnlock(driver->activeUsbHostdevs); } +void +qemuDomainReAttachHostScsiDevices(virQEMUDriverPtr driver, + const char *name, + virDomainHostdevDefPtr *hostdevs, + int nhostdevs) +{ + int i; + + virObjectLock(driver->activeScsiHostdevs); + for (i = 0; i < nhostdevs; i++) { + virDomainHostdevDefPtr hostdev = hostdevs[i]; + virSCSIDevicePtr scsi, tmp; + const char *used_by = NULL; + + if (hostdev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS || + hostdev->source.subsys.type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_SCSI) + continue; + + if (!(scsi = virSCSIDeviceNew(hostdev->source.subsys.u.scsi.adapter, + hostdev->source.subsys.u.scsi.bus, + hostdev->source.subsys.u.scsi.target, + hostdev->source.subsys.u.scsi.unit, + hostdev->readonly))) { + VIR_WARN("Unable to reattach SCSI device %s:%d:%d:%d on domain %s", + hostdev->source.subsys.u.scsi.adapter, + hostdev->source.subsys.u.scsi.bus, + hostdev->source.subsys.u.scsi.target, + hostdev->source.subsys.u.scsi.unit, + name); + continue; + } + + /* Only delete the devices which are marked as being used by @name, + * because qemuProcessStart could fail on the half way. */ + + tmp = virSCSIDeviceListFind(driver->activeScsiHostdevs, scsi); + virSCSIDeviceFree(scsi); + + if (!tmp) { + VIR_WARN("Unable to find device %s:%d:%d:%d " + "in list of active SCSI devices", + hostdev->source.subsys.u.scsi.adapter, + hostdev->source.subsys.u.scsi.bus, + hostdev->source.subsys.u.scsi.target, + hostdev->source.subsys.u.scsi.unit); + continue; + } + + used_by = virSCSIDeviceGetUsedBy(tmp); + if (STREQ_NULLABLE(used_by, name)) { + VIR_DEBUG("Removing %s:%d:%d:%d dom=%s from activeScsiHostdevs", + hostdev->source.subsys.u.scsi.adapter, + hostdev->source.subsys.u.scsi.bus, + hostdev->source.subsys.u.scsi.target, + hostdev->source.subsys.u.scsi.unit, + name); + + virSCSIDeviceListDel(driver->activeScsiHostdevs, tmp); + } + } + virObjectUnlock(driver->activeScsiHostdevs); +} + void qemuDomainReAttachHostDevices(virQEMUDriverPtr driver, virDomainDefPtr def) { @@ -1020,4 +1230,7 @@ void qemuDomainReAttachHostDevices(virQEMUDriverPtr driver, qemuDomainReAttachHostUsbDevices(driver, def->name, def->hostdevs, def->nhostdevs); + + qemuDomainReAttachHostScsiDevices(driver, def->name, def->hostdevs, + def->nhostdevs); } diff --git a/src/qemu/qemu_hostdev.h b/src/qemu/qemu_hostdev.h index a1b8b9e..327d4d5 100644 --- a/src/qemu/qemu_hostdev.h +++ b/src/qemu/qemu_hostdev.h @@ -31,6 +31,8 @@ int qemuUpdateActivePciHostdevs(virQEMUDriverPtr driver, virDomainDefPtr def); int qemuUpdateActiveUsbHostdevs(virQEMUDriverPtr driver, virDomainDefPtr def); +int qemuUpdateActiveScsiHostdevs(virQEMUDriverPtr driver, + virDomainDefPtr def); int qemuPrepareHostdevPCIDevices(virQEMUDriverPtr driver, const char *name, const unsigned char *uuid, @@ -42,9 +44,17 @@ int qemuFindHostdevUSBDevice(virDomainHostdevDefPtr hostdev, int qemuPrepareHostdevUSBDevices(virQEMUDriverPtr driver, const char *name, virUSBDeviceListPtr list); +int qemuPrepareHostdevSCSIDevices(virQEMUDriverPtr driver, + const char *name, + virDomainHostdevDefPtr *hostdevs, + int nhostdevs); int qemuPrepareHostDevices(virQEMUDriverPtr driver, virDomainDefPtr def, bool coldBoot); +void qemuDomainReAttachHostScsiDevices(virQEMUDriverPtr driver, + const char *name, + virDomainHostdevDefPtr *hostdevs, + int nhostdevs); void qemuReattachPciDevice(virPCIDevicePtr dev, virQEMUDriverPtr driver); void qemuDomainReAttachHostdevDevices(virQEMUDriverPtr driver, const char *name, diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c index 925939d..f87ffbe 100644 --- a/src/qemu/qemu_process.c +++ b/src/qemu/qemu_process.c @@ -3008,6 +3008,9 @@ qemuProcessReconnect(void *opaque) if (qemuUpdateActiveUsbHostdevs(driver, obj->def) < 0) goto error; + if (qemuUpdateActiveScsiHostdevs(driver, obj->def) < 0) + goto error; + if (qemuInitCgroup(driver, obj, false) < 0) goto error; -- 1.8.1.4

Il 26/04/2013 22:15, Osier Yang ha scritto:
From: Han Cheng <hanc.fnst@cn.fujitsu.com>
Although virtio-scsi supports SCSI PR, the device on host may do not support it. To avoid losing data, we only allow one scsi hostdev to be passthroughed to one live guest, Just like what we do for PCI and USB devices.
Signed-off-by: Han Cheng <hanc.fnst@cn.fujitsu.com>
--- v2.5 - v3: * Small changes, mainly on errors & coding style
I think this restriction should be limited to devices that do not have a <shareable/> element, similar to disks. I created BZ957292 for this. Paolo

On 27/04/13 06:04, Paolo Bonzini wrote:
Il 26/04/2013 22:15, Osier Yang ha scritto:
From: Han Cheng <hanc.fnst@cn.fujitsu.com>
Although virtio-scsi supports SCSI PR, the device on host may do not support it. To avoid losing data, we only allow one scsi hostdev to be passthroughed to one live guest, Just like what we do for PCI and USB devices.
Signed-off-by: Han Cheng <hanc.fnst@cn.fujitsu.com>
--- v2.5 - v3: * Small changes, mainly on errors & coding style I think this restriction should be limited to devices that do not have a <shareable/> element, similar to disks. I created BZ957292 for this. Right, I should use PATCH 0/n in the cover letter to indicate there are still patches left.. They are in hand, but not good tested. Will send them later & took the bug.
By the way, is there other -drive property which is nice to have for scsi-generic? Except the readonly.. (discard, serial, werror/rerror, and cache, I don't see other useful properties for scsi-generic)? Osier

Il 27/04/2013 03:49, Osier Yang ha scritto:
On 27/04/13 06:04, Paolo Bonzini wrote:
Il 26/04/2013 22:15, Osier Yang ha scritto:
From: Han Cheng <hanc.fnst@cn.fujitsu.com>
Although virtio-scsi supports SCSI PR, the device on host may do not support it. To avoid losing data, we only allow one scsi hostdev to be passthroughed to one live guest, Just like what we do for PCI and USB devices.
Signed-off-by: Han Cheng <hanc.fnst@cn.fujitsu.com>
--- v2.5 - v3: * Small changes, mainly on errors & coding style I think this restriction should be limited to devices that do not have a <shareable/> element, similar to disks. I created BZ957292 for this. Right, I should use PATCH 0/n in the cover letter to indicate there are still patches left.. They are in hand, but not good tested. Will send them later & took the bug.
By the way, is there other -drive property which is nice to have for scsi-generic? Except the readonly.. (discard, serial, werror/rerror, and cache, I don't see other useful properties for scsi-generic)?
discard/serial/cache are not supported. rerror and werror are but they will be. Paolo

From: Han Cheng <hanc.fnst@cn.fujitsu.com> This adds the scsi-generic device into the device controller's whitelist, so that it's allowed to used by the qemu process. Signed-off-by: Han Cheng <hanc.fnst@cn.fujitsu.com> --- v2.5 - v3: * Only rebasing, and small changes on coding style --- src/qemu/qemu_cgroup.c | 72 +++++++++++++++++++++++++++++++++++++++++++------- src/qemu/qemu_cgroup.h | 3 +++ 2 files changed, 65 insertions(+), 10 deletions(-) diff --git a/src/qemu/qemu_cgroup.c b/src/qemu/qemu_cgroup.c index 891984a..f631db1 100644 --- a/src/qemu/qemu_cgroup.c +++ b/src/qemu/qemu_cgroup.c @@ -214,6 +214,34 @@ int qemuSetupHostUsbDeviceCgroup(virUSBDevicePtr dev ATTRIBUTE_UNUSED, } +int +qemuSetupHostScsiDeviceCgroup(virSCSIDevicePtr dev ATTRIBUTE_UNUSED, + const char *path, + void *opaque) +{ + virDomainObjPtr vm = opaque; + qemuDomainObjPrivatePtr priv = vm->privateData; + int rc; + + VIR_DEBUG("Process path '%s' for SCSI device", path); + + rc = virCgroupAllowDevicePath(priv->cgroup, path, + virSCSIDeviceGetReadonly(dev) ? + VIR_CGROUP_DEVICE_READ : + VIR_CGROUP_DEVICE_RW); + + virDomainAuditCgroupPath(vm, priv->cgroup, "allow", path, + virSCSIDeviceGetReadonly(dev) ? "r" : "rw", rc); + if (rc < 0) { + virReportSystemError(-rc, + _("Unable to allow device %s"), + path); + return -1; + } + + return 0; +} + int qemuInitCgroup(virQEMUDriverPtr driver, virDomainObjPtr vm, bool startup) @@ -421,26 +449,50 @@ int qemuSetupCgroup(virQEMUDriverPtr driver, for (i = 0; i < vm->def->nhostdevs; i++) { virDomainHostdevDefPtr hostdev = vm->def->hostdevs[i]; - virUSBDevicePtr usb; + virUSBDevicePtr usb = NULL; + virSCSIDevicePtr scsi = NULL; if (hostdev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS) continue; - if (hostdev->source.subsys.type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB) + if (hostdev->source.subsys.type != + VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB && + hostdev->source.subsys.type != + VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_SCSI) continue; if (hostdev->missing) continue; - if ((usb = virUSBDeviceNew(hostdev->source.subsys.u.usb.bus, - hostdev->source.subsys.u.usb.device, - NULL)) == NULL) - goto cleanup; + if (hostdev->source.subsys.type == + VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB) { + if ((usb = virUSBDeviceNew(hostdev->source.subsys.u.usb.bus, + hostdev->source.subsys.u.usb.device, + NULL)) == NULL) + goto cleanup; - if (virUSBDeviceFileIterate(usb, qemuSetupHostUsbDeviceCgroup, - vm) < 0) { + if (virUSBDeviceFileIterate(usb, + qemuSetupHostUsbDeviceCgroup, + vm) < 0) { + virUSBDeviceFree(usb); + goto cleanup; + } virUSBDeviceFree(usb); - goto cleanup; + } else if (hostdev->source.subsys.type == + VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_SCSI) { + if ((scsi = virSCSIDeviceNew(hostdev->source.subsys.u.scsi.adapter, + hostdev->source.subsys.u.scsi.bus, + hostdev->source.subsys.u.scsi.target, + hostdev->source.subsys.u.scsi.unit, + hostdev->readonly)) == NULL) + goto cleanup; + + if (virSCSIDeviceFileIterate(scsi, + qemuSetupHostScsiDeviceCgroup, + vm) < 0) { + virSCSIDeviceFree(scsi); + goto cleanup; + } + virSCSIDeviceFree(scsi); } - virUSBDeviceFree(usb); } } diff --git a/src/qemu/qemu_cgroup.h b/src/qemu/qemu_cgroup.h index e63f443..14f0553 100644 --- a/src/qemu/qemu_cgroup.h +++ b/src/qemu/qemu_cgroup.h @@ -36,6 +36,9 @@ int qemuTeardownDiskCgroup(virDomainObjPtr vm, int qemuSetupHostUsbDeviceCgroup(virUSBDevicePtr dev, const char *path, void *opaque); +int qemuSetupHostScsiDeviceCgroup(virSCSIDevicePtr dev, + const char *path, + void *opaque); int qemuInitCgroup(virQEMUDriverPtr driver, virDomainObjPtr vm, bool startup); -- 1.8.1.4

To not introduce more redundant code, helpers are added for both "selinux", "dac", and "apparmor" backends. Signed-off-by: Han Cheng <hanc.fnst@cn.fujitsu.com> Signed-off-by: Osier Yang <jyang@redhat> v2.5 - v3: * Splitted from 8/10 of v2.5 * Don't forget the other backends (DAC, and apparmor) --- src/security/security_apparmor.c | 49 ++++++++++++++++--------- src/security/security_dac.c | 77 ++++++++++++++++++++++++++++++++-------- src/security/security_selinux.c | 72 ++++++++++++++++++++++++++++++------- 3 files changed, 155 insertions(+), 43 deletions(-) diff --git a/src/security/security_apparmor.c b/src/security/security_apparmor.c index 9dd8d74..feca838 100644 --- a/src/security/security_apparmor.c +++ b/src/security/security_apparmor.c @@ -306,8 +306,7 @@ reload_profile(virSecurityManagerPtr mgr, } static int -AppArmorSetSecurityUSBLabel(virUSBDevicePtr dev ATTRIBUTE_UNUSED, - const char *file, void *opaque) +AppArmorSetSecurityHostdevLabelHelper(const char *file, void *opaque) { struct SDPDOP *ptr = opaque; virDomainDefPtr def = ptr->def; @@ -328,25 +327,24 @@ AppArmorSetSecurityUSBLabel(virUSBDevicePtr dev ATTRIBUTE_UNUSED, } static int +AppArmorSetSecurityUSBLabel(virUSBDevicePtr dev ATTRIBUTE_UNUSED, + const char *file, void *opaque) +{ + return AppArmorSetSecurityHostdevLabelHelper(file, opaque); +} + +static int AppArmorSetSecurityPCILabel(virPCIDevicePtr dev ATTRIBUTE_UNUSED, const char *file, void *opaque) { - struct SDPDOP *ptr = opaque; - virDomainDefPtr def = ptr->def; + return AppArmorSetSecurityHostdevLabelHelper(file, opaque); +} - if (reload_profile(ptr->mgr, def, file, true) < 0) { - const virSecurityLabelDefPtr secdef = virDomainDefGetSecurityLabelDef( - def, SECURITY_APPARMOR_NAME); - if (!secdef) { - virReportOOMError(); - return -1; - } - virReportError(VIR_ERR_INTERNAL_ERROR, - _("cannot update AppArmor profile \'%s\'"), - secdef->imagelabel); - return -1; - } - return 0; +static int +AppArmorSetSecuritySCSILabel(virSCSIDevicePtr dev ATTRIBUTE_UNUSED, + const char *file, void *opaque) +{ + return AppArmorSetSecurityHostdevLabelHelper(file, opaque); } /* Called on libvirtd startup to see if AppArmor is available */ @@ -836,6 +834,23 @@ AppArmorSetSecurityHostdevLabel(virSecurityManagerPtr mgr, break; } + case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_SCSI: { + virSCSIDevicePtr scsi = + virSCSIDeviceNew(dev->source.subsys.u.scsi.adapter, + dev->source.subsys.u.scsi.bus, + dev->source.subsys.u.scsi.target, + dev->source.subsys.u.scsi.unit, + dev->readonly); + + if (!scsi) + goto done; + + ret = virSCSIDeviceFileIterate(scsi, AppArmorSetSecuritySCSILabel, ptr); + virSCSIDeviceFree(scsi); + + break; + } + default: ret = 0; break; diff --git a/src/security/security_dac.c b/src/security/security_dac.c index 38f7ba0..19fe447 100644 --- a/src/security/security_dac.c +++ b/src/security/security_dac.c @@ -30,6 +30,7 @@ #include "virlog.h" #include "virpci.h" #include "virusb.h" +#include "virscsi.h" #include "virstoragefile.h" #define VIR_FROM_THIS VIR_FROM_SECURITY @@ -432,11 +433,9 @@ virSecurityDACRestoreSecurityImageLabel(virSecurityManagerPtr mgr, return virSecurityDACRestoreSecurityImageLabelInt(mgr, def, disk, 0); } - static int -virSecurityDACSetSecurityPCILabel(virPCIDevicePtr dev ATTRIBUTE_UNUSED, - const char *file, - void *opaque) +virSecurityDACSetSecurityHostdevLabelHelper(const char *file, + void *opaque) { void **params = opaque; virSecurityManagerPtr mgr = params[0]; @@ -451,23 +450,29 @@ virSecurityDACSetSecurityPCILabel(virPCIDevicePtr dev ATTRIBUTE_UNUSED, return virSecurityDACSetOwnership(file, user, group); } +static int +virSecurityDACSetSecurityPCILabel(virPCIDevicePtr dev ATTRIBUTE_UNUSED, + const char *file, + void *opaque) +{ + return virSecurityDACSetSecurityHostdevLabelHelper(file, opaque); +} + static int virSecurityDACSetSecurityUSBLabel(virUSBDevicePtr dev ATTRIBUTE_UNUSED, const char *file, void *opaque) { - void **params = opaque; - virSecurityManagerPtr mgr = params[0]; - virDomainDefPtr def = params[1]; - virSecurityDACDataPtr priv = virSecurityManagerGetPrivateData(mgr); - uid_t user; - gid_t group; - - if (virSecurityDACGetIds(def, priv, &user, &group)) - return -1; + return virSecurityDACSetSecurityHostdevLabelHelper(file, opaque); +} - return virSecurityDACSetOwnership(file, user, group); +static int +virSecurityDACSetSecuritySCSILabel(virSCSIDevicePtr dev ATTRIBUTE_UNUSED, + const char *file, + void *opaque) +{ + return virSecurityDACSetSecurityHostdevLabelHelper(file, opaque); } @@ -523,6 +528,24 @@ virSecurityDACSetSecurityHostdevLabel(virSecurityManagerPtr mgr, break; } + case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_SCSI: { + virSCSIDevicePtr scsi = + virSCSIDeviceNew(dev->source.subsys.u.scsi.adapter, + dev->source.subsys.u.scsi.bus, + dev->source.subsys.u.scsi.target, + dev->source.subsys.u.scsi.unit, + dev->readonly); + + if (!scsi) + goto done; + + ret = virSCSIDeviceFileIterate(scsi, virSecurityDACSetSecuritySCSILabel, + params); + virSCSIDeviceFree(scsi); + + break; + } + default: ret = 0; break; @@ -552,6 +575,15 @@ virSecurityDACRestoreSecurityUSBLabel(virUSBDevicePtr dev ATTRIBUTE_UNUSED, static int +virSecurityDACRestoreSecuritySCSILabel(virSCSIDevicePtr dev ATTRIBUTE_UNUSED, + const char *file, + void *opaque ATTRIBUTE_UNUSED) +{ + return virSecurityDACRestoreSecurityFileLabel(file); +} + + +static int virSecurityDACRestoreSecurityHostdevLabel(virSecurityManagerPtr mgr, virDomainDefPtr def ATTRIBUTE_UNUSED, virDomainHostdevDefPtr dev, @@ -602,6 +634,23 @@ virSecurityDACRestoreSecurityHostdevLabel(virSecurityManagerPtr mgr, break; } + case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_SCSI: { + virSCSIDevicePtr scsi = + virSCSIDeviceNew(dev->source.subsys.u.scsi.adapter, + dev->source.subsys.u.scsi.bus, + dev->source.subsys.u.scsi.target, + dev->source.subsys.u.scsi.unit, + dev->readonly); + + if (!scsi) + goto done; + + ret = virSCSIDeviceFileIterate(scsi, virSecurityDACRestoreSecuritySCSILabel, mgr); + virSCSIDeviceFree(scsi); + + break; + } + default: ret = 0; break; diff --git a/src/security/security_selinux.c b/src/security/security_selinux.c index 61ff1de..2156fff 100644 --- a/src/security/security_selinux.c +++ b/src/security/security_selinux.c @@ -39,6 +39,7 @@ #include "virlog.h" #include "virpci.h" #include "virusb.h" +#include "virscsi.h" #include "virstoragefile.h" #include "virfile.h" #include "virhash.h" @@ -1277,10 +1278,8 @@ virSecuritySELinuxSetSecurityImageLabel(virSecurityManagerPtr mgr, &cbdata); } - static int -virSecuritySELinuxSetSecurityPCILabel(virPCIDevicePtr dev ATTRIBUTE_UNUSED, - const char *file, void *opaque) +virSecuritySELinuxSetSecurityHostdevLabelHelper(const char *file, void *opaque) { virSecurityLabelDefPtr secdef; virDomainDefPtr def = opaque; @@ -1292,19 +1291,25 @@ virSecuritySELinuxSetSecurityPCILabel(virPCIDevicePtr dev ATTRIBUTE_UNUSED, } static int -virSecuritySELinuxSetSecurityUSBLabel(virUSBDevicePtr dev ATTRIBUTE_UNUSED, +virSecuritySELinuxSetSecurityPCILabel(virPCIDevicePtr dev ATTRIBUTE_UNUSED, const char *file, void *opaque) { - virSecurityLabelDefPtr secdef; - virDomainDefPtr def = opaque; - - secdef = virDomainDefGetSecurityLabelDef(def, SECURITY_SELINUX_NAME); - if (secdef == NULL) - return -1; + return virSecuritySELinuxSetSecurityHostdevLabelHelper(file, opaque); +} - return virSecuritySELinuxSetFilecon(file, secdef->imagelabel); +static int +virSecuritySELinuxSetSecurityUSBLabel(virUSBDevicePtr dev ATTRIBUTE_UNUSED, + const char *file, void *opaque) +{ + return virSecuritySELinuxSetSecurityHostdevLabelHelper(file, opaque); } +static int +virSecuritySELinuxSetSecuritySCSILabel(virSCSIDevicePtr dev ATTRIBUTE_UNUSED, + const char *file, void *opaque) +{ + return virSecuritySELinuxSetSecurityHostdevLabelHelper(file, opaque); +} static int virSecuritySELinuxSetSecurityHostdevSubsysLabel(virDomainDefPtr def, @@ -1348,6 +1353,23 @@ virSecuritySELinuxSetSecurityHostdevSubsysLabel(virDomainDefPtr def, break; } + case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_SCSI: { + virSCSIDevicePtr scsi = + virSCSIDeviceNew(dev->source.subsys.u.scsi.adapter, + dev->source.subsys.u.scsi.bus, + dev->source.subsys.u.scsi.target, + dev->source.subsys.u.scsi.unit, + dev->readonly); + + if (!scsi) + goto done; + + ret = virSCSIDeviceFileIterate(scsi, virSecuritySELinuxSetSecuritySCSILabel, def); + virSCSIDeviceFree(scsi); + + break; + } + default: ret = 0; break; @@ -1445,7 +1467,6 @@ virSecuritySELinuxSetSecurityHostdevLabel(virSecurityManagerPtr mgr ATTRIBUTE_UN } } - static int virSecuritySELinuxRestoreSecurityPCILabel(virPCIDevicePtr dev ATTRIBUTE_UNUSED, const char *file, @@ -1468,6 +1489,16 @@ virSecuritySELinuxRestoreSecurityUSBLabel(virUSBDevicePtr dev ATTRIBUTE_UNUSED, static int +virSecuritySELinuxRestoreSecuritySCSILabel(virSCSIDevicePtr dev ATTRIBUTE_UNUSED, + const char *file, + void *opaque) +{ + virSecurityManagerPtr mgr = opaque; + + return virSecuritySELinuxRestoreSecurityFileLabel(mgr, file); +} + +static int virSecuritySELinuxRestoreSecurityHostdevSubsysLabel(virSecurityManagerPtr mgr, virDomainHostdevDefPtr dev, const char *vroot) @@ -1510,6 +1541,23 @@ virSecuritySELinuxRestoreSecurityHostdevSubsysLabel(virSecurityManagerPtr mgr, break; } + case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_SCSI: { + virSCSIDevicePtr scsi = + virSCSIDeviceNew(dev->source.subsys.u.scsi.adapter, + dev->source.subsys.u.scsi.bus, + dev->source.subsys.u.scsi.target, + dev->source.subsys.u.scsi.unit, + dev->readonly); + + if (!scsi) + goto done; + + ret = virSCSIDeviceFileIterate(scsi, virSecuritySELinuxRestoreSecuritySCSILabel, mgr); + virSCSIDeviceFree(scsi); + + break; + } + default: ret = 0; break; -- 1.8.1.4

It's better to put the usb related codes into qemuDomainAttachHostUsbDevice instead of qemuDomainAttachHostDevice. And in the old qemuDomainAttachHostDevice, just stealing the "usb" from driver->activeUsbHostdevs leaks the memory. Though the "usb" can be reused for virUSBDeviceFileIterate to set up the cgroup settings, because it will be used as readonly, let's not increase the confusion, use a "tmp_usb" instead. --- src/qemu/qemu_hotplug.c | 94 +++++++++++++++++++++---------------------------- 1 file changed, 41 insertions(+), 53 deletions(-) diff --git a/src/qemu/qemu_hotplug.c b/src/qemu/qemu_hotplug.c index 238d0d7..eb3ac52 100644 --- a/src/qemu/qemu_hotplug.c +++ b/src/qemu/qemu_hotplug.c @@ -1120,36 +1120,52 @@ int qemuDomainAttachHostUsbDevice(virQEMUDriverPtr driver, virDomainObjPtr vm, virDomainHostdevDefPtr hostdev) { - int ret; qemuDomainObjPrivatePtr priv = vm->privateData; + virUSBDeviceList *list = NULL; + virUSBDevicePtr usb = NULL; char *devstr = NULL; + int ret = -1; + bool added = false; + + if (qemuFindHostdevUSBDevice(hostdev, true, &usb) < 0) + return -1; + + if (!(list = virUSBDeviceListNew())) + goto cleanup; + + if (virUSBDeviceListAdd(list, usb) < 0) + goto cleanup; + + if (qemuPrepareHostdevUSBDevices(driver, vm->def->name, list) < 0) + goto cleanup; + + added = true; + virUSBDeviceListSteal(list, usb); if (virQEMUCapsGet(priv->qemuCaps, QEMU_CAPS_DEVICE)) { if (qemuAssignDeviceHostdevAlias(vm->def, hostdev, -1) < 0) - goto error; + goto cleanup; if (!(devstr = qemuBuildUSBHostdevDevStr(hostdev, priv->qemuCaps))) - goto error; + goto cleanup; } - if (VIR_REALLOC_N(vm->def->hostdevs, vm->def->nhostdevs+1) < 0) { - virReportOOMError(); - goto error; - } + if (VIR_REALLOC_N(vm->def->hostdevs, vm->def->nhostdevs+1) < 0) + goto cleanup; if (virCgroupHasController(priv->cgroup, VIR_CGROUP_CONTROLLER_DEVICES)) { - virUSBDevicePtr usb; + virUSBDevicePtr tmp_usb; if ((usb = virUSBDeviceNew(hostdev->source.subsys.u.usb.bus, - hostdev->source.subsys.u.usb.device, - NULL)) == NULL) - goto error; + hostdev->source.subsys.u.usb.device, + NULL)) == NULL) + goto cleanup; if (virUSBDeviceFileIterate(usb, qemuSetupHostUsbDeviceCgroup, vm) < 0) { - virUSBDeviceFree(usb); - goto error; + virUSBDeviceFree(tmp_usb); + goto cleanup; } - virUSBDeviceFree(usb); + virUSBDeviceFree(tmp_usb); } qemuDomainObjEnterMonitor(driver, vm); @@ -1160,28 +1176,27 @@ int qemuDomainAttachHostUsbDevice(virQEMUDriverPtr driver, hostdev->source.subsys.u.usb.bus, hostdev->source.subsys.u.usb.device); qemuDomainObjExitMonitor(driver, vm); + virDomainAuditHostdev(vm, hostdev, "attach", ret == 0); + if (ret < 0) - goto error; + goto cleanup; vm->def->hostdevs[vm->def->nhostdevs++] = hostdev; - - VIR_FREE(devstr); - - return 0; - -error: + ret = 0; +cleanup: + if (added) + virUSBDeviceListSteal(driver->activeUsbHostdevs, usb); + virUSBDeviceFree(usb); + virObjectUnref(list); VIR_FREE(devstr); - return -1; + return ret; } int qemuDomainAttachHostDevice(virQEMUDriverPtr driver, virDomainObjPtr vm, virDomainHostdevDefPtr hostdev) { - virUSBDeviceList *list; - virUSBDevicePtr usb = NULL; - if (hostdev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("hostdev mode '%s' not supported"), @@ -1189,30 +1204,9 @@ int qemuDomainAttachHostDevice(virQEMUDriverPtr driver, return -1; } - if (!(list = virUSBDeviceListNew())) - goto cleanup; - - if (hostdev->source.subsys.type == VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB) { - if (qemuFindHostdevUSBDevice(hostdev, true, &usb) < 0) - goto cleanup; - - if (virUSBDeviceListAdd(list, usb) < 0) { - virUSBDeviceFree(usb); - usb = NULL; - goto cleanup; - } - - if (qemuPrepareHostdevUSBDevices(driver, vm->def->name, list) < 0) { - usb = NULL; - goto cleanup; - } - - virUSBDeviceListSteal(list, usb); - } - if (virSecurityManagerSetHostdevLabel(driver->securityManager, vm->def, hostdev, NULL) < 0) - goto cleanup; + return -1; switch (hostdev->source.subsys.type) { case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI: @@ -1234,18 +1228,12 @@ int qemuDomainAttachHostDevice(virQEMUDriverPtr driver, goto error; } - virObjectUnref(list); return 0; error: if (virSecurityManagerRestoreHostdevLabel(driver->securityManager, vm->def, hostdev, NULL) < 0) VIR_WARN("Unable to restore host device labelling on hotplug fail"); - -cleanup: - virObjectUnref(list); - if (usb) - virUSBDeviceListSteal(driver->activeUsbHostdevs, usb); return -1; } -- 1.8.1.4

From: Han Cheng <hanc.fnst@cn.fujitsu.com> This adds both attachment and detachment support for scsi host device. Signed-off-by: Han Cheng <hanc.fnst@cn.fujitsu.com> Signed-off-by: Osier Yang <jyang@redhat> --- v2.5 - v3: * Rebase and small changes. --- src/qemu/qemu_hotplug.c | 146 +++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 145 insertions(+), 1 deletion(-) diff --git a/src/qemu/qemu_hotplug.c b/src/qemu/qemu_hotplug.c index eb3ac52..4e17e19 100644 --- a/src/qemu/qemu_hotplug.c +++ b/src/qemu/qemu_hotplug.c @@ -1153,7 +1153,7 @@ int qemuDomainAttachHostUsbDevice(virQEMUDriverPtr driver, goto cleanup; if (virCgroupHasController(priv->cgroup, VIR_CGROUP_CONTROLLER_DEVICES)) { - virUSBDevicePtr tmp_usb; + virUSBDevicePtr tmp_usb = NULL; if ((usb = virUSBDeviceNew(hostdev->source.subsys.u.usb.bus, hostdev->source.subsys.u.usb.device, @@ -1193,6 +1193,93 @@ cleanup: return ret; } +static int +qemuDomainAttachHostScsiDevice(virQEMUDriverPtr driver, + virDomainObjPtr vm, + virDomainHostdevDefPtr hostdev) +{ + int ret = -1; + qemuDomainObjPrivatePtr priv = vm->privateData; + char *devstr = NULL; + char *drvstr = NULL; + + if (!virQEMUCapsGet(priv->qemuCaps, QEMU_CAPS_DRIVE) || + !virQEMUCapsGet(priv->qemuCaps, QEMU_CAPS_DEVICE) || + !virQEMUCapsGet(priv->qemuCaps, QEMU_CAPS_DEVICE_SCSI_GENERIC)) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("SCSI passthrough is not supported by this version of qemu")); + return -1; + } + + if (qemuPrepareHostdevSCSIDevices(driver, vm->def->name, + &hostdev, 1)) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Unable to prepare scsi hostdev: %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); + return -1; + } + + if (qemuAssignDeviceHostdevAlias(vm->def, hostdev, 0) < 0) + goto cleanup; + + if (!(drvstr = qemuBuildSCSIHostdevDrvStr(hostdev, priv->qemuCaps))) + goto cleanup; + + if (!(devstr = qemuBuildSCSIHostdevDevStr(vm->def, hostdev, priv->qemuCaps))) + goto cleanup; + + if (VIR_REALLOC_N(vm->def->hostdevs, vm->def->nhostdevs + 1) < 0) { + virReportOOMError(); + goto cleanup; + } + + if (virCgroupHasController(priv->cgroup, VIR_CGROUP_CONTROLLER_DEVICES)) { + virSCSIDevicePtr scsi; + + if (!(scsi = virSCSIDeviceNew(hostdev->source.subsys.u.scsi.adapter, + hostdev->source.subsys.u.scsi.bus, + hostdev->source.subsys.u.scsi.target, + hostdev->source.subsys.u.scsi.unit, + hostdev->readonly))) + goto cleanup; + + if (virSCSIDeviceFileIterate(scsi, qemuSetupHostScsiDeviceCgroup, + vm) < 0) { + virSCSIDeviceFree(scsi); + goto cleanup; + } + + virSCSIDeviceFree(scsi); + } + + qemuDomainObjEnterMonitor(driver, vm); + if ((ret = qemuMonitorAddDrive(priv->mon, drvstr)) == 0) { + if ((ret = qemuMonitorAddDevice(priv->mon, devstr)) < 0) { + VIR_WARN("qemuMonitorAddDevice failed on %s (%s)", + drvstr, devstr); + qemuMonitorDriveDel(priv->mon, drvstr); + } + } + qemuDomainObjExitMonitor(driver, vm); + + virDomainAuditHostdev(vm, hostdev, "attach", ret == 0); + if (ret < 0) + goto cleanup; + + vm->def->hostdevs[vm->def->nhostdevs++] = hostdev; + + ret = 0; +cleanup: + if (ret < 0) + qemuDomainReAttachHostScsiDevices(driver, vm->def->name, &hostdev, 1); + VIR_FREE(drvstr); + VIR_FREE(devstr); + return ret; +} + int qemuDomainAttachHostDevice(virQEMUDriverPtr driver, virDomainObjPtr vm, virDomainHostdevDefPtr hostdev) @@ -1221,6 +1308,12 @@ int qemuDomainAttachHostDevice(virQEMUDriverPtr driver, goto error; break; + case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_SCSI: + if (qemuDomainAttachHostScsiDevice(driver, vm, + hostdev) < 0) + goto error; + break; + default: virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("hostdev subsys type '%s' not supported"), @@ -2433,6 +2526,48 @@ qemuDomainDetachHostUsbDevice(virQEMUDriverPtr driver, return ret; } +static int +qemuDomainDetachHostScsiDevice(virQEMUDriverPtr driver, + virDomainObjPtr vm, + virDomainHostdevDefPtr detach) +{ + qemuDomainObjPrivatePtr priv = vm->privateData; + char *drvstr = NULL; + char *devstr = NULL; + int ret = -1; + + if (!detach->info->alias) { + virReportError(VIR_ERR_OPERATION_FAILED, + "%s", _("device cannot be detached without a device alias")); + return -1; + } + + if (!(drvstr = qemuBuildSCSIHostdevDrvStr(detach, priv->qemuCaps))) + goto cleanup; + if (!(devstr = qemuBuildSCSIHostdevDevStr(vm->def, detach, priv->qemuCaps))) + goto cleanup; + + qemuDomainObjEnterMonitor(driver, vm); + if ((ret = qemuMonitorDelDevice(priv->mon, detach->info->alias)) == 0) { + if ((ret = qemuMonitorDriveDel(priv->mon, drvstr)) < 0) { + VIR_WARN("qemuMonitorDriveDel failed on %s (%s)", + detach->info->alias, drvstr); + qemuMonitorAddDevice(priv->mon, devstr); + } + } + qemuDomainObjExitMonitor(driver, vm); + + virDomainAuditHostdev(vm, detach, "detach", ret == 0); + + if (ret == 0) + qemuDomainReAttachHostScsiDevices(driver, vm->def->name, &detach, 1); + +cleanup: + VIR_FREE(drvstr); + VIR_FREE(devstr); + return ret; +} + static int qemuDomainDetachThisHostDevice(virQEMUDriverPtr driver, virDomainObjPtr vm, @@ -2464,6 +2599,9 @@ int qemuDomainDetachThisHostDevice(virQEMUDriverPtr driver, case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB: ret = qemuDomainDetachHostUsbDevice(driver, vm, detach); break; + case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_SCSI: + ret = qemuDomainDetachHostScsiDevice(driver, vm, detach); + break; default: virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("hostdev subsys type '%s' not supported"), @@ -2520,6 +2658,12 @@ int qemuDomainDetachHostDevice(virQEMUDriverPtr driver, subsys->u.usb.vendor, subsys->u.usb.product); } break; + case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_SCSI: + virReportError(VIR_ERR_OPERATION_FAILED, + _("host scsi device %s:%d:%d.%d not found"), + subsys->u.scsi.adapter, subsys->u.scsi.bus, + subsys->u.scsi.target, subsys->u.scsi.unit); + break; default: virReportError(VIR_ERR_INTERNAL_ERROR, _("unexpected hostdev type %d"), subsys->type); -- 1.8.1.4

With unknown good reasons, the attribute "bus" of scsi device address is always set to 0, same for attribute "target". (See virDomainDiskDefAssignAddress). Though we might need to change the algrithom to honor "bus" and "target" too, it's another story. The address generator for scsi host device in this patch just follows the unknown good reasons, only considering the "controller" and "unit". It walks through all scsi controllers and their units, to see if the address $controller:0:0:$unit can be used, if found one, it sits on it, otherwise, it creates a new controller (actually the controller is created implicitly by someone else), and sits on $new_controller:0:0:0 instead. --- Since it needs to add the controllers for "drive" type address implicitly, I add the generator in domain_conf.c instead of the specific the driver, e.g. qemu. --- src/conf/domain_conf.c | 142 +++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 138 insertions(+), 4 deletions(-) diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index edbed89..8ab306f 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -8556,8 +8556,141 @@ error: return NULL; } +/* Check if a drive type address $controller:0:0:$unit is already + * taken by a disk or not. + */ +static bool +virDomainDriveAddressIsUsedByDisk(virDomainDefPtr def, + enum virDomainDiskBus type, + unsigned int controller, + unsigned int unit) +{ + virDomainDiskDefPtr disk; + int i; + + for (i = 0; i < def->ndisks; i++) { + disk = def->disks[i]; + + if (disk->bus != type || + disk->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_DRIVE) + continue; + + if (disk->info.addr.drive.controller == controller && + disk->info.addr.drive.unit == unit && + disk->info.addr.drive.bus == 0 && + disk->info.addr.drive.target == 0) + return true; + } + + return false; +} + +/* Check if a drive type address $controller:0:0:$unit is already + * taken by a host device or not. + */ +static bool +virDomainDriveAddressIsUsedByHostdev(virDomainDefPtr def, + enum virDomainHostdevSubsysType type, + unsigned int controller, + unsigned int unit) +{ + virDomainHostdevDefPtr hostdev; + int i; + + for (i = 0; i < def->nhostdevs; i++) { + hostdev = def->hostdevs[i]; + + if (hostdev->source.subsys.type != type) + continue; + + if (hostdev->info->addr.drive.controller == controller && + hostdev->info->addr.drive.unit == unit && + hostdev->info->addr.drive.bus == 0 && + hostdev->info->addr.drive.target == 0) + return true; + } + + return false; +} + +/* Find out the next usable "unit" of a specific controller */ +static int +virDomainControllerSCSINextUnit(virDomainDefPtr def, + unsigned int max_unit, + unsigned int controller) +{ + int i; + + for (i = 0; i < max_unit; i++) { + /* The controller itself is on unit 7 */ + if (max_unit == 16 && i == 7) + continue; + + if (!virDomainDriveAddressIsUsedByDisk(def, + VIR_DOMAIN_DISK_BUS_SCSI, controller, i) && + !virDomainDriveAddressIsUsedByHostdev(def, + VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_SCSI, controller, i)) + return i; + } + + return -1; +} + +static int +virDomainHostdevAssignAddress(virDomainXMLOptionPtr xmlopt, + virDomainDefPtr def, + virDomainHostdevDefPtr hostdev) +{ + unsigned int max_unit; + int next_unit; + unsigned nscsi_controllers; + bool found; + int i; + + if (hostdev->source.subsys.type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_SCSI) + return -1; + + /* See comments in virDomainDiskDefAssignAddress */ + if (xmlopt->config.hasWideScsiBus) + max_unit = 16; + else + max_unit = 7; + + for (i = 0; i < def->ncontrollers; i++) { + if (def->controllers[i]->type != VIR_DOMAIN_CONTROLLER_TYPE_SCSI) + continue; + + nscsi_controllers++; + next_unit = virDomainControllerSCSINextUnit(def, max_unit, + def->controllers[i]->idx); + if (next_unit >= 0) { + found = true; + break; + } + } + + hostdev->info->type = VIR_DOMAIN_DEVICE_ADDRESS_TYPE_DRIVE; + + if (found) { + hostdev->info->addr.drive.controller = def->controllers[i]->idx; + hostdev->info->addr.drive.bus = 0; + hostdev->info->addr.drive.target = 0; + hostdev->info->addr.drive.unit = next_unit; + } else { + hostdev->info->addr.drive.controller = nscsi_controllers + 1; + hostdev->info->addr.drive.bus = 0; + hostdev->info->addr.drive.target = 0; + hostdev->info->addr.drive.unit = 0; + } + + return 0; +} + + static virDomainHostdevDefPtr -virDomainHostdevDefParseXML(const xmlNodePtr node, +virDomainHostdevDefParseXML(virDomainXMLOptionPtr xmlopt, + virDomainDefPtr vmdef, + const xmlNodePtr node, xmlXPathContextPtr ctxt, virHashTablePtr bootHash, unsigned int flags) @@ -8619,7 +8752,8 @@ virDomainHostdevDefParseXML(const xmlNodePtr node, break; case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_SCSI: - if (def->info->type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE) { + if (def->info->type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE && + virDomainHostdevAssignAddress(xmlopt, vmdef, def) < 0) { virReportError(VIR_ERR_XML_ERROR, "%s", _("SCSI host devices must have address specified")); goto error; @@ -9036,7 +9170,7 @@ virDomainDeviceDefParse(const char *xmlStr, goto error; } else if (xmlStrEqual(node->name, BAD_CAST "hostdev")) { dev->type = VIR_DOMAIN_DEVICE_HOSTDEV; - if (!(dev->data.hostdev = virDomainHostdevDefParseXML(node, ctxt, NULL, + if (!(dev->data.hostdev = virDomainHostdevDefParseXML(xmlopt, def, node, ctxt, NULL, flags))) goto error; } else if (xmlStrEqual(node->name, BAD_CAST "controller")) { @@ -11374,7 +11508,7 @@ virDomainDefParseXML(xmlDocPtr xml, for (i = 0 ; i < n ; i++) { virDomainHostdevDefPtr hostdev; - hostdev = virDomainHostdevDefParseXML(nodes[i], ctxt, bootHash, flags); + hostdev = virDomainHostdevDefParseXML(xmlopt, def, nodes[i], ctxt, bootHash, flags); if (!hostdev) goto error; -- 1.8.1.4

Il 26/04/2013 22:15, Osier Yang ha scritto:
With unknown good reasons, the attribute "bus" of scsi device address is always set to 0, same for attribute "target". (See virDomainDiskDefAssignAddress).
The target is not always equal to zero...
Though we might need to change the algrithom to honor "bus" and "target" too, it's another story. The address generator for scsi host device in this patch just follows the unknown good reasons, only considering the "controller" and "unit". It walks through all scsi controllers and their units, to see if the address $controller:0:0:$unit can be used,
... and $controller:0:0:$target:0 is preferrable to $controller:0:0:$unit. But having a different algorithm for disks vs. hostdevs would be worse, so this looks ok. Paolo
if found one, it sits on it, otherwise, it creates a new controller (actually the controller is created implicitly by someone else), and sits on $new_controller:0:0:0 instead.
--- Since it needs to add the controllers for "drive" type address implicitly, I add the generator in domain_conf.c instead of the specific the driver, e.g. qemu. --- src/conf/domain_conf.c | 142 +++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 138 insertions(+), 4 deletions(-)
diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index edbed89..8ab306f 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -8556,8 +8556,141 @@ error: return NULL; }
+/* Check if a drive type address $controller:0:0:$unit is already + * taken by a disk or not. + */ +static bool +virDomainDriveAddressIsUsedByDisk(virDomainDefPtr def, + enum virDomainDiskBus type, + unsigned int controller, + unsigned int unit) +{ + virDomainDiskDefPtr disk; + int i; + + for (i = 0; i < def->ndisks; i++) { + disk = def->disks[i]; + + if (disk->bus != type || + disk->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_DRIVE) + continue; + + if (disk->info.addr.drive.controller == controller && + disk->info.addr.drive.unit == unit && + disk->info.addr.drive.bus == 0 && + disk->info.addr.drive.target == 0) + return true; + } + + return false; +} + +/* Check if a drive type address $controller:0:0:$unit is already + * taken by a host device or not. + */ +static bool +virDomainDriveAddressIsUsedByHostdev(virDomainDefPtr def, + enum virDomainHostdevSubsysType type, + unsigned int controller, + unsigned int unit) +{ + virDomainHostdevDefPtr hostdev; + int i; + + for (i = 0; i < def->nhostdevs; i++) { + hostdev = def->hostdevs[i]; + + if (hostdev->source.subsys.type != type) + continue; + + if (hostdev->info->addr.drive.controller == controller && + hostdev->info->addr.drive.unit == unit && + hostdev->info->addr.drive.bus == 0 && + hostdev->info->addr.drive.target == 0) + return true; + } + + return false; +} + +/* Find out the next usable "unit" of a specific controller */ +static int +virDomainControllerSCSINextUnit(virDomainDefPtr def, + unsigned int max_unit, + unsigned int controller) +{ + int i; + + for (i = 0; i < max_unit; i++) { + /* The controller itself is on unit 7 */ + if (max_unit == 16 && i == 7) + continue; + + if (!virDomainDriveAddressIsUsedByDisk(def, + VIR_DOMAIN_DISK_BUS_SCSI, controller, i) && + !virDomainDriveAddressIsUsedByHostdev(def, + VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_SCSI, controller, i)) + return i; + } + + return -1; +} + +static int +virDomainHostdevAssignAddress(virDomainXMLOptionPtr xmlopt, + virDomainDefPtr def, + virDomainHostdevDefPtr hostdev) +{ + unsigned int max_unit; + int next_unit; + unsigned nscsi_controllers; + bool found; + int i; + + if (hostdev->source.subsys.type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_SCSI) + return -1; + + /* See comments in virDomainDiskDefAssignAddress */ + if (xmlopt->config.hasWideScsiBus) + max_unit = 16; + else + max_unit = 7; + + for (i = 0; i < def->ncontrollers; i++) { + if (def->controllers[i]->type != VIR_DOMAIN_CONTROLLER_TYPE_SCSI) + continue; + + nscsi_controllers++; + next_unit = virDomainControllerSCSINextUnit(def, max_unit, + def->controllers[i]->idx); + if (next_unit >= 0) { + found = true; + break; + } + } + + hostdev->info->type = VIR_DOMAIN_DEVICE_ADDRESS_TYPE_DRIVE; + + if (found) { + hostdev->info->addr.drive.controller = def->controllers[i]->idx; + hostdev->info->addr.drive.bus = 0; + hostdev->info->addr.drive.target = 0; + hostdev->info->addr.drive.unit = next_unit; + } else { + hostdev->info->addr.drive.controller = nscsi_controllers + 1; + hostdev->info->addr.drive.bus = 0; + hostdev->info->addr.drive.target = 0; + hostdev->info->addr.drive.unit = 0; + } + + return 0; +} + + static virDomainHostdevDefPtr -virDomainHostdevDefParseXML(const xmlNodePtr node, +virDomainHostdevDefParseXML(virDomainXMLOptionPtr xmlopt, + virDomainDefPtr vmdef, + const xmlNodePtr node, xmlXPathContextPtr ctxt, virHashTablePtr bootHash, unsigned int flags) @@ -8619,7 +8752,8 @@ virDomainHostdevDefParseXML(const xmlNodePtr node,
break; case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_SCSI: - if (def->info->type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE) { + if (def->info->type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE && + virDomainHostdevAssignAddress(xmlopt, vmdef, def) < 0) { virReportError(VIR_ERR_XML_ERROR, "%s", _("SCSI host devices must have address specified")); goto error; @@ -9036,7 +9170,7 @@ virDomainDeviceDefParse(const char *xmlStr, goto error; } else if (xmlStrEqual(node->name, BAD_CAST "hostdev")) { dev->type = VIR_DOMAIN_DEVICE_HOSTDEV; - if (!(dev->data.hostdev = virDomainHostdevDefParseXML(node, ctxt, NULL, + if (!(dev->data.hostdev = virDomainHostdevDefParseXML(xmlopt, def, node, ctxt, NULL, flags))) goto error; } else if (xmlStrEqual(node->name, BAD_CAST "controller")) { @@ -11374,7 +11508,7 @@ virDomainDefParseXML(xmlDocPtr xml, for (i = 0 ; i < n ; i++) { virDomainHostdevDefPtr hostdev;
- hostdev = virDomainHostdevDefParseXML(nodes[i], ctxt, bootHash, flags); + hostdev = virDomainHostdevDefParseXML(xmlopt, def, nodes[i], ctxt, bootHash, flags); if (!hostdev) goto error;
participants (2)
-
Osier Yang
-
Paolo Bonzini