(before anything else - you committed an unresolved merge conflict in
qemu_command.h. You'll need to remove the extra
">>>>>>>>> blah" text.)
Hopefully Eric can once again review the logic of the code that
determines what bridges need to be auto-added, and assign PCI addresses
to devices, since he was kind enough to review it last time :-)
On 04/22/2013 02: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.
Ah, so then we don't need to warn about gaps in the index sequence.
---
src/conf/domain_conf.c | 26 ++-
src/qemu/qemu_command.c | 211 +++++++++++++++++----
src/qemu/qemu_command.h | 4 +-
tests/qemuxml2argvdata/qemuxml2argv-pci-bridge.xml | 210 ++++++++++++++++++++
tests/qemuxml2xmltest.c | 1 +
5 files changed, 411 insertions(+), 41 deletions(-)
create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-pci-bridge.xml
diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c
index 5740009..800c0a7 100644
--- a/src/conf/domain_conf.c
+++ b/src/conf/domain_conf.c
@@ -2578,6 +2578,9 @@ virDomainDefPostParseInternal(virDomainDefPtr def,
virCapsPtr caps ATTRIBUTE_UNUSED)
{
int i;
+ bool b;
+ int ret = -1;
+ 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);
+ 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));
+ 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));
+ }
+ }
+ ret = 0;
+
+cleanup:
+ virBitmapFree(bitmap);
+
+ return ret;
I don't see where we do something like this for the other controller
types. We should (separate patch though, of course :-)
no_memory:
virReportOOMError();
- return -1;
+ goto cleanup;
}
diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c
index 3787ff1..ec7d0e6 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,46 @@ 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"));
+ return false;
+ } else {
+ virReportError(VIR_ERR_XML_ERROR, "%s",
+ _("Slot 0 on bus 0 is reserved for the host
bridge"));
+ return false;
+ }
You can move the duplicated "return false;" out of the if and else, to a
single occurrence.
+ }
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 +1313,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 +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++;
+ }
+ }
+
+ if (nbuses > 0 && max_idx >= nbuses)
+ nbuses = max_idx + 1;
+
+ 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) {
+ 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 +1462,8 @@ int qemuDomainAssignAddresses(virDomainDefPtr def,
}
qemuDomainPCIAddressSetPtr qemuDomainPCIAddressSetCreate(virDomainDefPtr def,
- unsigned int nbuses)
+ unsigned int nbuses,
+ bool dryRun)
{
qemuDomainPCIAddressSetPtr addrs;
int i;
@@ -1392,6 +1475,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 +1508,9 @@ int qemuDomainPCIAddressReserveAddr(qemuDomainPCIAddressSetPtr
addrs,
{
char *str;
+ if (addrs->dryRun && qemuDomainPCIAddressSetGrow(addrs, addr) < 0)
+ return -1;
+
if (!(str = qemuPCIAddressAsString(addr)))
return -1;
@@ -1450,6 +1537,9 @@ int qemuDomainPCIAddressReserveSlot(qemuDomainPCIAddressSetPtr
addrs,
{
char *str;
+ if (addrs->dryRun && qemuDomainPCIAddressSetGrow(addrs, addr) < 0)
+ return -1;
+
if (!(str = qemuPCIAddressAsString(addr)))
return -1;
@@ -1519,41 +1609,58 @@ void qemuDomainPCIAddressSetFree(qemuDomainPCIAddressSetPtr
addrs)
VIR_FREE(addrs);
}
-
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 +1673,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 +1850,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 +1901,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 +2094,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..5d9ae72 100644
--- a/src/qemu/qemu_command.h
+++ b/src/qemu/qemu_command.h
@@ -197,7 +197,9 @@ int qemuDomainAssignPCIAddresses(virDomainDefPtr def,
virQEMUCapsPtr qemuCaps,
virDomainObjPtr obj);
qemuDomainPCIAddressSetPtr qemuDomainPCIAddressSetCreate(virDomainDefPtr def,
- unsigned int nbuses);
+ unsigned int nbuses,
+ bool dryRun);
+>>>>>>> a3dcfa9... qemu: auto-add bridges and allow using them
Oops. You've got an unresolved merge error!
int qemuDomainPCIAddressReserveSlot(qemuDomainPCIAddressSetPtr
addrs,
virDevicePCIAddressPtr addr);
int qemuDomainPCIAddressReserveAddr(qemuDomainPCIAddressSetPtr addrs,
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
You should also do a test case where you haven't specified the bridges
in the "xmlIn", but only in the "xmlOut". I *think* you should be
able
to do that by just putting the file that contains the bridges in
qemuxml2xmloutdata, and calling the test with DO_TEST_DIFFERENT() in
qemuxml2xmltest, AND omitting this test from qemuargv2xmltest.
@@ -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/qemuxml2xmltest.c b/tests/qemuxml2xmltest.c
index 7434190..04b14df 100644
--- a/tests/qemuxml2xmltest.c
+++ b/tests/qemuxml2xmltest.c
@@ -276,6 +276,7 @@ mymain(void)
DO_TEST_DIFFERENT("metadata");
DO_TEST("tpm-passthrough");
+ DO_TEST("pci-bridge");
virObjectUnref(driver.caps);
virObjectUnref(driver.xmlopt);
What about qemuxml2argvtest.c? (which means you also need a .args file.)