On 04/22/2013 12:43 PM, Ján Tomko wrote:
Add a "dry run" address allocation to figure out how many
bridges
will be needed for all the devices without explicit addresses.
Auto-add just enough bridges to put all the devices on, or up to the
bridge with the largest specified index.
---
In addition to Laine's comments,
+ virBitmapPtr bitmap = NULL;
/* verify init path for container based domains */
if (STREQ(def->os.type, "exe") && !def->os.init) {
@@ -2653,11 +2656,30 @@ virDomainDefPostParseInternal(virDomainDefPtr def,
}
}
- return 0;
+ /* Verify that PCI controller indexes are unique */
+ bitmap = virBitmapNew(def->ncontrollers);
This limits the bitmap to the number of controllers that I passed in,
but your commit message makes it sound like I can pass in a controller
for index 1 and index 2 while letting the code auto-insert the
controller for index 0. If that's true, then...
+ for (i = 0; i < def->ncontrollers; i++) {
+ if (def->controllers[i]->type == VIR_DOMAIN_CONTROLLER_TYPE_PCI) {
+ ignore_value(virBitmapGetBit(bitmap, def->controllers[i]->idx,
&b));
...attempting to get bit 2 (based on the explicit 2 in
def->controllers[i]->idx) will fail as out-of-range,...
+ if (b) {
+ virReportError(VIR_ERR_XML_ERROR,
+ _("Multiple PCI controllers with index %d"),
+ def->controllers[i]->idx);
+ goto cleanup;
+ }
+ ignore_value(virBitmapSetBit(bitmap, def->controllers[i]->idx));
+ }
+ }
...and the attempt to set will also fail. Which means that a similar
example of passing in two controllers that both try to use index 2, and
let 0 and 1 be auto-populated, won't detect the collision. Do we know
what the maximum index will be? Is it time to add a growable bitmap?
Should we separate this duplicate detection code into a separate patch?
+/* Ensure addr fits in the address set, by expanding it if needed.
+ * Return value:
+ * -1 = OOM
+ * 0 = no action performed
+ * >0 = number of buses added
+ */
+static int
+qemuDomainPCIAddressSetGrow(qemuDomainPCIAddressSetPtr addrs,
+ virDevicePCIAddressPtr addr)
Indentation is off.
@@ -1326,15 +1369,54 @@ qemuDomainAssignPCIAddresses(virDomainDefPtr
def,
qemuDomainObjPrivatePtr priv = NULL;
if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE)) {
+ int max_idx = 0;
int nbuses = 0;
int i;
+ int rv;
for (i = 0; i < def->ncontrollers; i++) {
- if (def->controllers[i]->type == VIR_DOMAIN_CONTROLLER_TYPE_PCI)
+ if (def->controllers[i]->type == VIR_DOMAIN_CONTROLLER_TYPE_PCI) {
+ if (def->controllers[i]->idx > max_idx)
+ max_idx = def->controllers[i]->idx;
nbuses++;
+ }
+ }
This looks like a more accurate determination of the max bus number;
should you move the duplicate detection here instead?
+
+ if (nbuses > 0 && max_idx >= nbuses)
+ nbuses = max_idx + 1;
You change nbuses, but only if it is already > 1, but then...
+
+ if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_PCI_BRIDGE)) {
+ virDomainDeviceInfo info;
+ /* 1st pass to figure out how many PCI bridges we need */
+ if (!(addrs = qemuDomainPCIAddressSetCreate(def, nbuses, true)))
+ goto cleanup;
+ if (qemuAssignDevicePCISlots(def, qemuCaps, addrs) < 0)
+ goto cleanup;
+ /* Reserve 1 extra slot for a (potential) bridge */
+ if (qemuDomainPCIAddressSetNextAddr(addrs, &info) < 0)
+ goto cleanup;
+
+ for (i = 1; i < addrs->nbuses; i++) {
+ if ((rv = virDomainDefMaybeAddController(
+ def, VIR_DOMAIN_CONTROLLER_TYPE_PCI,
+ i, VIR_DOMAIN_CONTROLLER_MODEL_PCI_BRIDGE)) < 0)
+ goto cleanup;
+ /* If we added a new bridge, we will need one more address */
+ if (rv > 0 && qemuDomainPCIAddressSetNextAddr(addrs,
&info) < 0)
+ goto cleanup;
+ }
+ nbuses = addrs->nbuses;
+ qemuDomainPCIAddressSetFree(addrs);
+ addrs = NULL;
+
+ } else if (nbuses > 1) {
...read nbuses if the capability is not present. Would it be any
simpler to just change this to 'else if (max_idx > 0)'?
+ virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
"%s",
+ _("PCI bridges are not supported "
+ "by this QEMU binary"));
+ goto cleanup;
}
- if (!(addrs = qemuDomainPCIAddressSetCreate(def, nbuses)))
+ if (!(addrs = qemuDomainPCIAddressSetCreate(def, nbuses, false)))
goto cleanup;
if (qemuAssignDevicePCISlots(def, qemuCaps, addrs) < 0)
@@ -1519,41 +1609,58 @@ void
qemuDomainPCIAddressSetFree(qemuDomainPCIAddressSetPtr addrs)
VIR_FREE(addrs);
}
-
static int
I think we've been settling on two blank lines between functions lately,
although I'm not bothered if you leave this change in.
qemuDomainPCIAddressGetNextSlot(qemuDomainPCIAddressSetPtr addrs,
virDevicePCIAddressPtr next_addr)
{
- virDevicePCIAddress tmp_addr = addrs->lastaddr;
- int i;
- char *addr;
+ virDevicePCIAddress a = addrs->lastaddr;
- tmp_addr.slot++;
- for (i = 0; i < QEMU_PCI_ADDRESS_SLOT_LAST; i++, tmp_addr.slot++) {
- if (QEMU_PCI_ADDRESS_SLOT_LAST <= tmp_addr.slot) {
- tmp_addr.slot = 0;
- }
+ if (addrs->nbuses == 0) {
+ virReportError(VIR_ERR_XML_ERROR, "%s", _("No PCI buses
available"));
+ return -1;
+ }
addrs->nbuses should always be >= 1, now that we allocate it, right? Is
it possible to hit this error?
- if (!(addr = qemuPCIAddressAsString(&tmp_addr)))
- return -1;
+ /* Start the search at the last used bus and slot */
+ for (a.slot++; a.bus < addrs->nbuses; a.bus++) {
+ for ( ; a.slot < QEMU_PCI_ADDRESS_SLOT_LAST; a.slot++) {
+ if (!qemuDomainPCIAddressSlotInUse(addrs, &a))
+ goto success;
- if (qemuDomainPCIAddressSlotInUse(addrs, &tmp_addr)) {
- VIR_DEBUG("PCI addr %s already in use", addr);
- VIR_FREE(addr);
- continue;
+ VIR_DEBUG("PCI slot %.4x:%.2x:%.2x already in use",
+ a.domain, a.bus, a.slot);
}
+ a.slot = 1;
+ }
- VIR_DEBUG("Found free PCI addr %s", addr);
- VIR_FREE(addr);
+ /* There were no free slots after the last used one */
+ if (addrs->dryRun) {
+ /* a is already set to the first new bus and slot 1 */
+ if (qemuDomainPCIAddressSetGrow(addrs, &a) < 0)
+ return -1;
+ goto success;
+ } else {
+ /* Check the buses from 0 up to the last used one */
+ for (a.bus = 0; a.bus <= addrs->lastaddr.bus; a.bus++) {
+ for (a.slot = 1; a.slot < QEMU_PCI_ADDRESS_SLOT_LAST; a.slot++) {
+ if (!qemuDomainPCIAddressSlotInUse(addrs, &a))
+ goto success;
- addrs->lastaddr = tmp_addr;
- *next_addr = tmp_addr;
- return 0;
+ VIR_DEBUG("PCI slot %.4x:%.2x:%.2x already in use",
+ a.domain, a.bus, a.slot);
+ }
+
+ }
}
This wraparound logic was definitely easier to read than the v2 attempt.
virReportError(VIR_ERR_INTERNAL_ERROR,
"%s", _("No more available PCI addresses"));
return -1;
+
+success:
+ VIR_DEBUG("Found free PCI slot %.4x:%.2x:%.2x",
+ a.domain, a.bus, a.slot);
+ *next_addr = a;
+ return 0;
}
int qemuDomainPCIAddressSetNextAddr(qemuDomainPCIAddressSetPtr addrs,
--
Eric Blake eblake redhat com +1-919-301-3266
Libvirt virtualization library
http://libvirt.org