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.
---
v4:
Moved the check for duplicate controller indexes to a separate patch
Simplified nbuses and max_idx computation
Only does two-pass allocation of PCI addresses if the machine has a PCI bus
Does not contain traces of rebasing or spurious whitespace changes
Tests auto-adding PCI bridges in xml->argv and xml->xml tests.
src/qemu/qemu_command.c | 210 +++++++++++++++++----
src/qemu/qemu_command.h | 3 +-
.../qemuxml2argv-pci-autoadd-addr.args | 12 ++
.../qemuxml2argv-pci-autoadd-addr.xml | 44 +++++
.../qemuxml2argv-pci-autoadd-idx.args | 13 ++
.../qemuxml2argv-pci-autoadd-idx.xml | 45 +++++
tests/qemuxml2argvdata/qemuxml2argv-pci-bridge.xml | 210 +++++++++++++++++++++
tests/qemuxml2argvtest.c | 3 +
.../qemuxml2xmlout-pci-autoadd-addr.xml | 44 +++++
.../qemuxml2xmlout-pci-autoadd-idx.xml | 45 +++++
tests/qemuxml2xmltest.c | 3 +
11 files changed, 593 insertions(+), 39 deletions(-)
create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-pci-autoadd-addr.args
create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-pci-autoadd-addr.xml
create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-pci-autoadd-idx.args
create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-pci-autoadd-idx.xml
create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-pci-bridge.xml
create mode 100644 tests/qemuxml2xmloutdata/qemuxml2xmlout-pci-autoadd-addr.xml
create mode 100644 tests/qemuxml2xmloutdata/qemuxml2xmlout-pci-autoadd-idx.xml
diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c
index 3787ff1..30c967c 100644
--- a/src/qemu/qemu_command.c
+++ b/src/qemu/qemu_command.c
@@ -1197,6 +1197,8 @@ struct _qemuDomainPCIAddressSet {
qemuDomainPCIAddressBus *used;
virDevicePCIAddress lastaddr;
size_t nbuses; /* allocation of 'used' */
+ bool dryRun; /* on a dry run, new buses are auto-added
+ and addresses aren't saved in device infos */
};
@@ -1216,9 +1218,10 @@ static bool qemuPCIAddressValidate(qemuDomainPCIAddressSetPtr addrs
ATTRIBUTE_UN
_("Only PCI domain 0 is available"));
return false;
}
- if (addr->bus != 0) {
- virReportError(VIR_ERR_XML_ERROR, "%s",
- _("Only PCI bus 0 is available"));
+ if (addr->bus >= addrs->nbuses) {
+ virReportError(VIR_ERR_XML_ERROR,
+ _("Only PCI buses up to %zu are available"),
+ addrs->nbuses - 1);
return false;
}
if (addr->function >= QEMU_PCI_ADDRESS_FUNCTION_LAST) {
@@ -1233,9 +1236,45 @@ static bool qemuPCIAddressValidate(qemuDomainPCIAddressSetPtr addrs
ATTRIBUTE_UN
QEMU_PCI_ADDRESS_SLOT_LAST);
return false;
}
+ if (addr->slot == 0) {
+ if (addr->bus) {
+ virReportError(VIR_ERR_XML_ERROR, "%s",
+ _("Slot 0 is unusable on PCI bridges"));
+ } else {
+ virReportError(VIR_ERR_XML_ERROR, "%s",
+ _("Slot 0 on bus 0 is reserved for the host
bridge"));
+ }
+ return false;
+ }
return true;
}
+/* 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)
+{
+ int add, i;
+
+ add = addr->bus - addrs->nbuses + 1;
+ i = addrs->nbuses;
+ if (add <= 0)
+ return 0;
+ if (VIR_EXPAND_N(addrs->used, addrs->nbuses, add) < 0) {
+ virReportOOMError();
+ return -1;
+ }
+ /* reserve slot 0 on the new buses */
+ for (; i < addrs->nbuses; i++)
+ addrs->used[i][0] = 0xFF;
+ return add;
+}
+
static char *qemuPCIAddressAsString(virDevicePCIAddressPtr addr)
{
@@ -1273,6 +1312,9 @@ static int qemuCollectPCIAddress(virDomainDefPtr def
ATTRIBUTE_UNUSED,
return 0;
}
+ if (addrs->dryRun && qemuDomainPCIAddressSetGrow(addrs, addr) < 0)
+ return -1;
+
if (!qemuPCIAddressValidate(addrs, addr))
return -1;
@@ -1326,15 +1368,53 @@ qemuDomainAssignPCIAddresses(virDomainDefPtr def,
qemuDomainObjPrivatePtr priv = NULL;
if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE)) {
+ int max_idx = -1;
int nbuses = 0;
int i;
+ int rv;
for (i = 0; i < def->ncontrollers; i++) {
- if (def->controllers[i]->type == VIR_DOMAIN_CONTROLLER_TYPE_PCI)
- nbuses++;
+ if (def->controllers[i]->type == VIR_DOMAIN_CONTROLLER_TYPE_PCI) {
+ if (def->controllers[i]->idx > max_idx)
+ max_idx = def->controllers[i]->idx;
+ }
+ }
+
+ nbuses = max_idx + 1;
+
+ if (nbuses > 0 &&
+ 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 (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)
@@ -1380,7 +1460,8 @@ int qemuDomainAssignAddresses(virDomainDefPtr def,
}
qemuDomainPCIAddressSetPtr qemuDomainPCIAddressSetCreate(virDomainDefPtr def,
- unsigned int nbuses)
+ unsigned int nbuses,
+ bool dryRun)
{
qemuDomainPCIAddressSetPtr addrs;
int i;
@@ -1392,6 +1473,7 @@ qemuDomainPCIAddressSetPtr
qemuDomainPCIAddressSetCreate(virDomainDefPtr def,
goto no_memory;
addrs->nbuses = nbuses;
+ addrs->dryRun = dryRun;
/* reserve slot 0 in every bus - it's used by the host bridge on bus 0
* and unusable on PCI bridges */
@@ -1424,6 +1506,9 @@ int qemuDomainPCIAddressReserveAddr(qemuDomainPCIAddressSetPtr
addrs,
{
char *str;
+ if (addrs->dryRun && qemuDomainPCIAddressSetGrow(addrs, addr) < 0)
+ return -1;
+
if (!(str = qemuPCIAddressAsString(addr)))
return -1;
@@ -1450,6 +1535,9 @@ int qemuDomainPCIAddressReserveSlot(qemuDomainPCIAddressSetPtr
addrs,
{
char *str;
+ if (addrs->dryRun && qemuDomainPCIAddressSetGrow(addrs, addr) < 0)
+ return -1;
+
if (!(str = qemuPCIAddressAsString(addr)))
return -1;
@@ -1524,36 +1612,54 @@ static int
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;
+ }
- 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);
+ }
+
+ }
}
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,
@@ -1566,8 +1672,10 @@ int qemuDomainPCIAddressSetNextAddr(qemuDomainPCIAddressSetPtr
addrs,
if (qemuDomainPCIAddressReserveSlot(addrs, &addr) < 0)
return -1;
- dev->type = VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI;
- dev->addr.pci = addr;
+ if (!addrs->dryRun) {
+ dev->type = VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI;
+ dev->addr.pci = addr;
+ }
addrs->lastaddr = addr;
return 0;
@@ -1741,6 +1849,18 @@ qemuAssignDevicePCISlots(virDomainDefPtr def,
}
}
+ /* PCI controllers */
+ for (i = 0; i < def->ncontrollers; i++) {
+ if (def->controllers[i]->type == VIR_DOMAIN_CONTROLLER_TYPE_PCI) {
+ if (def->controllers[i]->model ==
VIR_DOMAIN_CONTROLLER_MODEL_PCI_ROOT)
+ continue;
+ if (def->controllers[i]->info.type !=
VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE)
+ continue;
+ if (qemuDomainPCIAddressSetNextAddr(addrs,
&def->controllers[i]->info) < 0)
+ goto error;
+ }
+ }
+
for (i = 0; i < def->nfss ; i++) {
if (def->fss[i]->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE)
continue;
@@ -1780,9 +1900,10 @@ qemuAssignDevicePCISlots(virDomainDefPtr def,
/* Device controllers (SCSI, USB, but not IDE, FDC or CCID) */
for (i = 0; i < def->ncontrollers ; i++) {
- /* PCI root has no address */
+ /* PCI controllers have been dealt with earlier */
if (def->controllers[i]->type == VIR_DOMAIN_CONTROLLER_TYPE_PCI)
continue;
+
/* FDC lives behind the ISA bridge; CCID is a usb device */
if (def->controllers[i]->type == VIR_DOMAIN_CONTROLLER_TYPE_FDC ||
def->controllers[i]->type == VIR_DOMAIN_CONTROLLER_TYPE_CCID)
@@ -1972,16 +2093,29 @@ qemuBuildDeviceAddressStr(virBufferPtr buf,
}
}
- /* XXX
- * When QEMU grows support for > 1 PCI bus, then pci.0 changes
- * to pci.1, pci.2, etc
- * When QEMU grows support for > 1 PCI domain, then pci.0 change
- * to pciNN.0 where NN is the domain number
+ /*
+ * PCI bridge support is required for multiple buses
+ * 'pci.%u' is the ID of the bridge as specified in
+ * qemuBuildControllerDevStr
+ *
+ * PCI_MULTIBUS capability indicates that the implicit
+ * PCI bus is named 'pci.0' instead of 'pci'.
*/
- if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_PCI_MULTIBUS))
- virBufferAsprintf(buf, ",bus=pci.0");
- else
- virBufferAsprintf(buf, ",bus=pci");
+ if (info->addr.pci.bus != 0) {
+ if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_PCI_BRIDGE)) {
+ virBufferAsprintf(buf, ",bus=pci.%u", info->addr.pci.bus);
+ } else {
+ virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
+ _("Multiple PCI buses are not supported "
+ "with this QEMU binary"));
+ return -1;
+ }
+ } else {
+ if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_PCI_MULTIBUS))
+ virBufferAsprintf(buf, ",bus=pci.0");
+ else
+ virBufferAsprintf(buf, ",bus=pci");
+ }
if (info->addr.pci.multi == VIR_DEVICE_ADDRESS_PCI_MULTI_ON)
virBufferAddLit(buf, ",multifunction=on");
else if (info->addr.pci.multi == VIR_DEVICE_ADDRESS_PCI_MULTI_OFF)
diff --git a/src/qemu/qemu_command.h b/src/qemu/qemu_command.h
index b05510b..5f04001 100644
--- a/src/qemu/qemu_command.h
+++ b/src/qemu/qemu_command.h
@@ -197,7 +197,8 @@ int qemuDomainAssignPCIAddresses(virDomainDefPtr def,
virQEMUCapsPtr qemuCaps,
virDomainObjPtr obj);
qemuDomainPCIAddressSetPtr qemuDomainPCIAddressSetCreate(virDomainDefPtr def,
- unsigned int nbuses);
+ unsigned int nbuses,
+ bool dryRun);
int qemuDomainPCIAddressReserveSlot(qemuDomainPCIAddressSetPtr addrs,
virDevicePCIAddressPtr addr);
int qemuDomainPCIAddressReserveAddr(qemuDomainPCIAddressSetPtr addrs,
diff --git a/tests/qemuxml2argvdata/qemuxml2argv-pci-autoadd-addr.args
b/tests/qemuxml2argvdata/qemuxml2argv-pci-autoadd-addr.args
new file mode 100644
index 0000000..b346c72
--- /dev/null
+++ b/tests/qemuxml2argvdata/qemuxml2argv-pci-autoadd-addr.args
@@ -0,0 +1,12 @@
+LC_ALL=C PATH=/bin HOME=/home/test USER=test LOGNAME=test \
+/usr/libexec/qemu-kvm -S -M pc-1.2 -m 2048 -smp 2 -nographic -nodefaults \
+-monitor unix:/tmp/test-monitor,server,nowait -boot c \
+-device pci-bridge,chassis_nr=1,id=pci.1,bus=pci.0,addr=0x3 \
+-device pci-bridge,chassis_nr=2,id=pci.2,bus=pci.0,addr=0x4 \
+-device pci-bridge,chassis_nr=3,id=pci.3,bus=pci.0,addr=0x5 \
+-device pci-bridge,chassis_nr=4,id=pci.4,bus=pci.0,addr=0x6 \
+-device pci-bridge,chassis_nr=5,id=pci.5,bus=pci.0,addr=0x7 \
+-device pci-bridge,chassis_nr=6,id=pci.6,bus=pci.0,addr=0x8 \
+-device pci-bridge,chassis_nr=7,id=pci.7,bus=pci.0,addr=0x9 \
+-usb -cdrom /var/iso/f18kde.iso \
+-device virtio-balloon-pci,id=balloon0,bus=pci.7,addr=0x6
diff --git a/tests/qemuxml2argvdata/qemuxml2argv-pci-autoadd-addr.xml
b/tests/qemuxml2argvdata/qemuxml2argv-pci-autoadd-addr.xml
new file mode 100644
index 0000000..92db5e6
--- /dev/null
+++ b/tests/qemuxml2argvdata/qemuxml2argv-pci-autoadd-addr.xml
@@ -0,0 +1,44 @@
+<domain type='qemu'>
+ <name>fdr-br</name>
+ <uuid>3ec6cbe1-b5a2-4515-b800-31a61855df41</uuid>
+ <memory unit='KiB'>2097152</memory>
+ <currentMemory unit='KiB'>2097152</currentMemory>
+ <vcpu placement='static' cpuset='0-1'>2</vcpu>
+ <os>
+ <type arch='x86_64' machine='pc-1.2'>hvm</type>
+ <boot dev='hd'/>
+ </os>
+ <features>
+ <acpi/>
+ <apic/>
+ <pae/>
+ </features>
+ <clock offset='utc'/>
+ <on_poweroff>destroy</on_poweroff>
+ <on_reboot>restart</on_reboot>
+ <on_crash>restart</on_crash>
+ <devices>
+ <emulator>/usr/libexec/qemu-kvm</emulator>
+ <disk type='file' device='cdrom'>
+ <driver name='qemu' type='raw'/>
+ <source file='/var/iso/f18kde.iso'/>
+ <target dev='hdc' bus='ide'/>
+ <readonly/>
+ <address type='drive' controller='0' bus='1'
target='0' unit='0'/>
+ </disk>
+ <controller type='usb' index='0'>
+ <address type='pci' domain='0x0000' bus='0x00'
slot='0x01' function='0x2'/>
+ </controller>
+ <controller type='ide' index='0'>
+ <address type='pci' domain='0x0000' bus='0x00'
slot='0x01' function='0x1'/>
+ </controller>
+ <input type='mouse' bus='ps2'/>
+ <video>
+ <model type='cirrus' vram='9216' heads='1'/>
+ <address type='pci' domain='0x0000' bus='0x00'
slot='0x02' function='0x0'/>
+ </video>
+ <memballoon model='virtio'>
+ <address type='pci' domain='0x0000' bus='0x07'
slot='0x06' function='0x0'/>
+ </memballoon>
+ </devices>
+</domain>
diff --git a/tests/qemuxml2argvdata/qemuxml2argv-pci-autoadd-idx.args
b/tests/qemuxml2argvdata/qemuxml2argv-pci-autoadd-idx.args
new file mode 100644
index 0000000..bd2706e
--- /dev/null
+++ b/tests/qemuxml2argvdata/qemuxml2argv-pci-autoadd-idx.args
@@ -0,0 +1,13 @@
+LC_ALL=C PATH=/bin HOME=/home/test USER=test LOGNAME=test \
+/usr/libexec/qemu-kvm -S -M pc-1.2 -m 2048 -smp 2 -nographic -nodefaults \
+-monitor unix:/tmp/test-monitor,server,nowait -boot c \
+-device pci-bridge,chassis_nr=8,id=pci.8,bus=pci.0,addr=0x3 \
+-device pci-bridge,chassis_nr=1,id=pci.1,bus=pci.0,addr=0x4 \
+-device pci-bridge,chassis_nr=2,id=pci.2,bus=pci.0,addr=0x5 \
+-device pci-bridge,chassis_nr=3,id=pci.3,bus=pci.0,addr=0x6 \
+-device pci-bridge,chassis_nr=4,id=pci.4,bus=pci.0,addr=0x7 \
+-device pci-bridge,chassis_nr=5,id=pci.5,bus=pci.0,addr=0x8 \
+-device pci-bridge,chassis_nr=6,id=pci.6,bus=pci.0,addr=0x9 \
+-device pci-bridge,chassis_nr=7,id=pci.7,bus=pci.0,addr=0xa \
+-usb -cdrom /var/iso/f18kde.iso \
+-device virtio-balloon-pci,id=balloon0,bus=pci.4,addr=0x6
diff --git a/tests/qemuxml2argvdata/qemuxml2argv-pci-autoadd-idx.xml
b/tests/qemuxml2argvdata/qemuxml2argv-pci-autoadd-idx.xml
new file mode 100644
index 0000000..f6a3ddf
--- /dev/null
+++ b/tests/qemuxml2argvdata/qemuxml2argv-pci-autoadd-idx.xml
@@ -0,0 +1,45 @@
+<domain type='qemu'>
+ <name>fdr-br</name>
+ <uuid>3ec6cbe1-b5a2-4515-b800-31a61855df41</uuid>
+ <memory unit='KiB'>2097152</memory>
+ <currentMemory unit='KiB'>2097152</currentMemory>
+ <vcpu placement='static' cpuset='0-1'>2</vcpu>
+ <os>
+ <type arch='x86_64' machine='pc-1.2'>hvm</type>
+ <boot dev='hd'/>
+ </os>
+ <features>
+ <acpi/>
+ <apic/>
+ <pae/>
+ </features>
+ <clock offset='utc'/>
+ <on_poweroff>destroy</on_poweroff>
+ <on_reboot>restart</on_reboot>
+ <on_crash>restart</on_crash>
+ <devices>
+ <emulator>/usr/libexec/qemu-kvm</emulator>
+ <disk type='file' device='cdrom'>
+ <driver name='qemu' type='raw'/>
+ <source file='/var/iso/f18kde.iso'/>
+ <target dev='hdc' bus='ide'/>
+ <readonly/>
+ <address type='drive' controller='0' bus='1'
target='0' unit='0'/>
+ </disk>
+ <controller type='usb' index='0'>
+ <address type='pci' domain='0x0000' bus='0x00'
slot='0x01' function='0x2'/>
+ </controller>
+ <controller type='ide' index='0'>
+ <address type='pci' domain='0x0000' bus='0x00'
slot='0x01' function='0x1'/>
+ </controller>
+ <controller type='pci' index='8' model='pci-bridge'/>
+ <input type='mouse' bus='ps2'/>
+ <video>
+ <model type='cirrus' vram='9216' heads='1'/>
+ <address type='pci' domain='0x0000' bus='0x00'
slot='0x02' function='0x0'/>
+ </video>
+ <memballoon model='virtio'>
+ <address type='pci' domain='0x0000' bus='0x04'
slot='0x06' function='0x0'/>
+ </memballoon>
+ </devices>
+</domain>
diff --git a/tests/qemuxml2argvdata/qemuxml2argv-pci-bridge.xml
b/tests/qemuxml2argvdata/qemuxml2argv-pci-bridge.xml
new file mode 100644
index 0000000..eb20328
--- /dev/null
+++ b/tests/qemuxml2argvdata/qemuxml2argv-pci-bridge.xml
@@ -0,0 +1,210 @@
+<domain type='qemu'>
+ <name>fdr-br</name>
+ <uuid>3ec6cbe1-b5a2-4515-b800-31a61855df41</uuid>
+ <memory unit='KiB'>2097152</memory>
+ <currentMemory unit='KiB'>2097152</currentMemory>
+ <vcpu placement='static' cpuset='0-1'>2</vcpu>
+ <os>
+ <type arch='x86_64' machine='pc-1.2'>hvm</type>
+ <boot dev='hd'/>
+ </os>
+ <features>
+ <acpi/>
+ <apic/>
+ <pae/>
+ </features>
+ <clock offset='utc'/>
+ <on_poweroff>destroy</on_poweroff>
+ <on_reboot>restart</on_reboot>
+ <on_crash>restart</on_crash>
+ <devices>
+ <emulator>/usr/libexec/qemu-kvm</emulator>
+ <disk type='file' device='cdrom'>
+ <driver name='qemu' type='raw'/>
+ <source file='/var/iso/f18kde.iso'/>
+ <target dev='hdc' bus='ide'/>
+ <readonly/>
+ <address type='drive' controller='0' bus='1'
target='0' unit='0'/>
+ </disk>
+ <controller type='usb' index='0'>
+ <address type='pci' domain='0x0000' bus='0x00'
slot='0x01' function='0x2'/>
+ </controller>
+ <controller type='ide' index='0'>
+ <address type='pci' domain='0x0000' bus='0x00'
slot='0x01' function='0x1'/>
+ </controller>
+ <controller type='pci' index='0' model='pci-root'/>
+ <controller type='pci' index='1' model='pci-bridge'/>
+ <controller type='pci' index='2' model='pci-bridge'/>
+ <interface type='network'>
+ <mac address='52:54:00:f1:95:51'/>
+ <source network='default'/>
+ <model type='rtl8139'/>
+ </interface>
+ <interface type='network'>
+ <mac address='52:54:00:5c:c6:1a'/>
+ <source network='default'/>
+ <model type='e1000'/>
+ </interface>
+ <interface type='network'>
+ <mac address='52:54:00:39:97:ac'/>
+ <source network='default'/>
+ <model type='e1000'/>
+ </interface>
+ <interface type='network'>
+ <mac address='52:54:00:45:28:cb'/>
+ <source network='default'/>
+ <model type='e1000'/>
+ </interface>
+ <interface type='network'>
+ <mac address='52:54:00:ee:b9:a8'/>
+ <source network='default'/>
+ <model type='e1000'/>
+ </interface>
+ <interface type='network'>
+ <mac address='52:54:00:a9:f7:17'/>
+ <source network='default'/>
+ <model type='e1000'/>
+ </interface>
+ <interface type='network'>
+ <mac address='52:54:00:df:2b:f3'/>
+ <source network='default'/>
+ <model type='e1000'/>
+ </interface>
+ <interface type='network'>
+ <mac address='52:54:00:78:94:b4'/>
+ <source network='default'/>
+ <model type='e1000'/>
+ </interface>
+ <interface type='network'>
+ <mac address='52:54:00:6b:9b:06'/>
+ <source network='default'/>
+ <model type='e1000'/>
+ </interface>
+ <interface type='network'>
+ <mac address='52:54:00:17:df:bc'/>
+ <source network='default'/>
+ <model type='e1000'/>
+ </interface>
+ <interface type='network'>
+ <mac address='52:54:00:3b:d0:51'/>
+ <source network='default'/>
+ <model type='e1000'/>
+ </interface>
+ <interface type='network'>
+ <mac address='52:54:00:8d:2d:17'/>
+ <source network='default'/>
+ <model type='e1000'/>
+ </interface>
+ <interface type='network'>
+ <mac address='52:54:00:a7:66:af'/>
+ <source network='default'/>
+ <model type='e1000'/>
+ </interface>
+ <interface type='network'>
+ <mac address='52:54:00:54:ab:d7'/>
+ <source network='default'/>
+ <model type='e1000'/>
+ </interface>
+ <interface type='network'>
+ <mac address='52:54:00:1f:99:90'/>
+ <source network='default'/>
+ <model type='e1000'/>
+ </interface>
+ <interface type='network'>
+ <mac address='52:54:00:c8:43:87'/>
+ <source network='default'/>
+ <model type='e1000'/>
+ </interface>
+ <interface type='network'>
+ <mac address='52:54:00:df:22:b2'/>
+ <source network='default'/>
+ <model type='e1000'/>
+ </interface>
+ <interface type='network'>
+ <mac address='52:54:00:d2:9a:47'/>
+ <source network='default'/>
+ <model type='e1000'/>
+ </interface>
+ <interface type='network'>
+ <mac address='52:54:00:86:05:e2'/>
+ <source network='default'/>
+ <model type='e1000'/>
+ </interface>
+ <interface type='network'>
+ <mac address='52:54:00:8c:1c:c2'/>
+ <source network='default'/>
+ <model type='e1000'/>
+ </interface>
+ <interface type='network'>
+ <mac address='52:54:00:48:58:92'/>
+ <source network='default'/>
+ <model type='e1000'/>
+ </interface>
+ <interface type='network'>
+ <mac address='52:54:00:99:e5:bf'/>
+ <source network='default'/>
+ <model type='e1000'/>
+ </interface>
+ <interface type='network'>
+ <mac address='52:54:00:b1:8c:25'/>
+ <source network='default'/>
+ <model type='e1000'/>
+ </interface>
+ <interface type='network'>
+ <mac address='52:54:00:60:e0:d0'/>
+ <source network='default'/>
+ <model type='e1000'/>
+ </interface>
+ <interface type='network'>
+ <mac address='52:54:00:37:00:6a'/>
+ <source network='default'/>
+ <model type='e1000'/>
+ </interface>
+ <interface type='network'>
+ <mac address='52:54:00:c7:c8:ad'/>
+ <source network='default'/>
+ <model type='e1000'/>
+ </interface>
+ <interface type='network'>
+ <mac address='52:54:00:4e:a7:cf'/>
+ <source network='default'/>
+ <model type='e1000'/>
+ </interface>
+ <interface type='network'>
+ <mac address='52:54:00:00:79:69'/>
+ <source network='default'/>
+ <model type='e1000'/>
+ </interface>
+ <interface type='network'>
+ <mac address='52:54:00:47:00:6f'/>
+ <source network='default'/>
+ <model type='e1000'/>
+ </interface>
+ <interface type='network'>
+ <mac address='52:54:00:2a:8c:8b'/>
+ <source network='default'/>
+ <model type='e1000'/>
+ </interface>
+ <interface type='network'>
+ <mac address='52:54:00:ec:d5:e3'/>
+ <source network='default'/>
+ <model type='e1000'/>
+ </interface>
+ <interface type='network'>
+ <mac address='52:54:00:7e:6e:c8'/>
+ <source network='default'/>
+ <model type='e1000'/>
+ </interface>
+ <input type='mouse' bus='ps2'/>
+ <graphics type='vnc' port='-1' autoport='yes'
listen='127.0.0.1' keymap='en-us'>
+ <listen type='address' address='127.0.0.1'/>
+ </graphics>
+ <video>
+ <model type='cirrus' vram='9216' heads='1'/>
+ <address type='pci' domain='0x0000' bus='0x00'
slot='0x02' function='0x0'/>
+ </video>
+ <memballoon model='virtio'>
+ <address type='pci' domain='0x0000' bus='0x00'
slot='0x06' function='0x0'/>
+ </memballoon>
+ </devices>
+</domain>
diff --git a/tests/qemuxml2argvtest.c b/tests/qemuxml2argvtest.c
index ae73509..955400a 100644
--- a/tests/qemuxml2argvtest.c
+++ b/tests/qemuxml2argvtest.c
@@ -949,6 +949,9 @@ mymain(void)
DO_TEST("tpm-passthrough", QEMU_CAPS_DEVICE,
QEMU_CAPS_DEVICE_TPM_PASSTHROUGH, QEMU_CAPS_DEVICE_TPM_TIS);
+ DO_TEST("pci-autoadd-addr", QEMU_CAPS_DEVICE,
QEMU_CAPS_DEVICE_PCI_BRIDGE);
+ DO_TEST("pci-autoadd-idx", QEMU_CAPS_DEVICE, QEMU_CAPS_DEVICE_PCI_BRIDGE);
+
virObjectUnref(driver.config);
virObjectUnref(driver.caps);
virObjectUnref(driver.xmlopt);
diff --git a/tests/qemuxml2xmloutdata/qemuxml2xmlout-pci-autoadd-addr.xml
b/tests/qemuxml2xmloutdata/qemuxml2xmlout-pci-autoadd-addr.xml
new file mode 100644
index 0000000..13f0f5d
--- /dev/null
+++ b/tests/qemuxml2xmloutdata/qemuxml2xmlout-pci-autoadd-addr.xml
@@ -0,0 +1,44 @@
+<domain type='qemu'>
+ <name>fdr-br</name>
+ <uuid>3ec6cbe1-b5a2-4515-b800-31a61855df41</uuid>
+ <memory unit='KiB'>2097152</memory>
+ <currentMemory unit='KiB'>2097152</currentMemory>
+ <vcpu placement='static' cpuset='0-1'>2</vcpu>
+ <os>
+ <type arch='x86_64' machine='pc-1.2'>hvm</type>
+ <boot dev='hd'/>
+ </os>
+ <features>
+ <acpi/>
+ <apic/>
+ <pae/>
+ </features>
+ <clock offset='utc'/>
+ <on_poweroff>destroy</on_poweroff>
+ <on_reboot>restart</on_reboot>
+ <on_crash>restart</on_crash>
+ <devices>
+ <emulator>/usr/libexec/qemu-kvm</emulator>
+ <disk type='file' device='cdrom'>
+ <driver name='qemu' type='raw'/>
+ <source file='/var/iso/f18kde.iso'/>
+ <target dev='hdc' bus='ide'/>
+ <readonly/>
+ <address type='drive' controller='0' bus='1'
target='0' unit='0'/>
+ </disk>
+ <controller type='usb' index='0'>
+ <address type='pci' domain='0x0000' bus='0x00'
slot='0x01' function='0x2'/>
+ </controller>
+ <controller type='ide' index='0'>
+ <address type='pci' domain='0x0000' bus='0x00'
slot='0x01' function='0x1'/>
+ </controller>
+ <controller type='pci' index='0' model='pci-root'/>
+ <video>
+ <model type='cirrus' vram='9216' heads='1'/>
+ <address type='pci' domain='0x0000' bus='0x00'
slot='0x02' function='0x0'/>
+ </video>
+ <memballoon model='virtio'>
+ <address type='pci' domain='0x0000' bus='0x07'
slot='0x06' function='0x0'/>
+ </memballoon>
+ </devices>
+</domain>
diff --git a/tests/qemuxml2xmloutdata/qemuxml2xmlout-pci-autoadd-idx.xml
b/tests/qemuxml2xmloutdata/qemuxml2xmlout-pci-autoadd-idx.xml
new file mode 100644
index 0000000..8748437
--- /dev/null
+++ b/tests/qemuxml2xmloutdata/qemuxml2xmlout-pci-autoadd-idx.xml
@@ -0,0 +1,45 @@
+<domain type='qemu'>
+ <name>fdr-br</name>
+ <uuid>3ec6cbe1-b5a2-4515-b800-31a61855df41</uuid>
+ <memory unit='KiB'>2097152</memory>
+ <currentMemory unit='KiB'>2097152</currentMemory>
+ <vcpu placement='static' cpuset='0-1'>2</vcpu>
+ <os>
+ <type arch='x86_64' machine='pc-1.2'>hvm</type>
+ <boot dev='hd'/>
+ </os>
+ <features>
+ <acpi/>
+ <apic/>
+ <pae/>
+ </features>
+ <clock offset='utc'/>
+ <on_poweroff>destroy</on_poweroff>
+ <on_reboot>restart</on_reboot>
+ <on_crash>restart</on_crash>
+ <devices>
+ <emulator>/usr/libexec/qemu-kvm</emulator>
+ <disk type='file' device='cdrom'>
+ <driver name='qemu' type='raw'/>
+ <source file='/var/iso/f18kde.iso'/>
+ <target dev='hdc' bus='ide'/>
+ <readonly/>
+ <address type='drive' controller='0' bus='1'
target='0' unit='0'/>
+ </disk>
+ <controller type='usb' index='0'>
+ <address type='pci' domain='0x0000' bus='0x00'
slot='0x01' function='0x2'/>
+ </controller>
+ <controller type='ide' index='0'>
+ <address type='pci' domain='0x0000' bus='0x00'
slot='0x01' function='0x1'/>
+ </controller>
+ <controller type='pci' index='8' model='pci-bridge'/>
+ <controller type='pci' index='0' model='pci-root'/>
+ <video>
+ <model type='cirrus' vram='9216' heads='1'/>
+ <address type='pci' domain='0x0000' bus='0x00'
slot='0x02' function='0x0'/>
+ </video>
+ <memballoon model='virtio'>
+ <address type='pci' domain='0x0000' bus='0x04'
slot='0x06' function='0x0'/>
+ </memballoon>
+ </devices>
+</domain>
diff --git a/tests/qemuxml2xmltest.c b/tests/qemuxml2xmltest.c
index 7434190..5ae7c3b 100644
--- a/tests/qemuxml2xmltest.c
+++ b/tests/qemuxml2xmltest.c
@@ -276,6 +276,9 @@ mymain(void)
DO_TEST_DIFFERENT("metadata");
DO_TEST("tpm-passthrough");
+ DO_TEST("pci-bridge");
+ DO_TEST_DIFFERENT("pci-autoadd-addr");
+ DO_TEST_DIFFERENT("pci-autoadd-idx");
virObjectUnref(driver.caps);
virObjectUnref(driver.xmlopt);
--
1.8.1.5