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 | 144 +++++++++++++++++++++++++++++++++++++++++++++++--
1 file changed, 139 insertions(+), 5 deletions(-)
diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c
index 31a8c46..cff2b46 100644
--- a/src/conf/domain_conf.c
+++ b/src/conf/domain_conf.c
@@ -8658,8 +8658,140 @@ 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)
@@ -8719,7 +8851,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;
@@ -9128,8 +9261,8 @@ 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,
- flags)))
+ if (!(dev->data.hostdev = virDomainHostdevDefParseXML(xmlopt, def, node,
+ ctxt, NULL, flags)))
goto error;
} else if (xmlStrEqual(node->name, BAD_CAST "controller")) {
dev->type = VIR_DOMAIN_DEVICE_CONTROLLER;
@@ -11477,7 +11610,8 @@ 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