]> xenbits.xensource.com Git - people/dariof/libvirt.git/commitdiff
qemu: fix handling of default/implicit devices for q35
authorLaine Stump <laine@laine.org>
Fri, 2 Aug 2013 08:55:55 +0000 (04:55 -0400)
committerLaine Stump <laine@laine.org>
Mon, 5 Aug 2013 19:47:49 +0000 (15:47 -0400)
This patch adds in special handling for a few devices that need to be
treated differently for q35 domains:

usb - there is no implicit/default usb controller for the q35
machinetype. This is done because normally the default usb controller
is added to a domain by just adding "-usb" to the qemu commandline,
and it's assumed that this will add a single piix3 usb1 controller at
slot 1 function 2. That's not what happens when the machinetype is
q35, though. Instead, adding -usb to the commandline adds 3 usb
(version 2) controllers to the domain at slot 0x1D.{1,2,7}. Rather
than having

  <controller type='usb' index='0'/>

translate into 3 separate devices on the PCI bus, it's cleaner to not
automatically add a default usb device; one can always be added
explicitly if desired. Or we may decide that on q35 machines, 3 usb
controllers will be automatically added when none is given. But for
this initial commit, at least we aren't locking ourselves into
something we later won't want.

video - qemu always initializes the primary video device immediately
after any integrated devices for the machinetype. Unless instructed
otherwise (by using "-device vga..." instead of "-vga" which libvirt
uses in many cases to work around deficiencies and bugs in various
qemu versions) qemu will always pick the first unused slot. In the
case of the "pc" machinetype and its derivatives, this is always slot
2, but on q35 machinetypes, the first free slot is slot 1 (since the
q35's integrated peripheral devices are placed in other slots,
e.g. slot 0x1f). In order to make the PCI address of the video device
predictable, that slot (1 or 2, depending on machinetype) is reserved
even when no video device has been specified.

sata - a q35 machine always has a sata controller implicitly added at
slot 0x1F, function 2. There is no way to avoid this controller, so we
always add it. Note that the xml2xml tests for the pcie-root and q35
cases were changed to use DO_TEST_DIFFERENT() so that we can check for
the sata controller being automatically added. This is especially
important because we can't check for it in the xml2argv output (it has
no effect on that output since it's an implicit device).

ide - q35 has no ide controllers.

isa and smbus controllers - these two are always present in a q35 (at
slot 0x1F functions 0 and 3) but we have no way of modelling them in
our config. We do need to reserve those functions so that the user
doesn't attempt to put anything else there though. (note that the "pc"
machine type also has an ISA controller, which we also ignore).

src/qemu/qemu_command.c
src/qemu/qemu_domain.c
tests/qemuxml2argvdata/qemuxml2argv-pcie-root.args
tests/qemuxml2argvdata/qemuxml2argv-pcie-root.xml
tests/qemuxml2argvdata/qemuxml2argv-q35.args
tests/qemuxml2argvtest.c
tests/qemuxml2xmloutdata/qemuxml2xmlout-pcie-root.xml
tests/qemuxml2xmloutdata/qemuxml2xmlout-q35.xml [new file with mode: 0644]
tests/qemuxml2xmltest.c

index 50f37ae5ccf2137de94d2218c471f9dc2c835c29..3e07360d7a41af4d79a4bf8eb605f47728cd929b 100644 (file)
@@ -1686,6 +1686,18 @@ qemuCollectPCIAddress(virDomainDefPtr def ATTRIBUTE_UNUSED,
             break;
         }
     }
+    /* SATA controllers aren't hot-plugged, and can be put in either a
+     * PCI or PCIe slot
+     */
+    if (device->type == VIR_DOMAIN_DEVICE_CONTROLLER &&
+        device->data.controller->type == VIR_DOMAIN_CONTROLLER_TYPE_SATA)
+        flags = QEMU_PCI_CONNECT_TYPE_PCI | QEMU_PCI_CONNECT_TYPE_PCIE;
+
+    /* video cards aren't hot-plugged, and can be put in either a PCI
+     * or PCIe slot
+     */
+    if (device->type == VIR_DOMAIN_DEVICE_VIDEO)
+        flags = QEMU_PCI_CONNECT_TYPE_PCI | QEMU_PCI_CONNECT_TYPE_PCIE;
 
     /* Ignore implicit controllers on slot 0:0:1.0:
      * implicit IDE controller on 0:0:1.1 (no qemu command line)
@@ -2258,6 +2270,12 @@ qemuValidateDevicePCISlotsPIIX3(virDomainDefPtr def,
     }
 
     if (def->nvideos > 0) {
+        /* Because the PIIX3 integrated IDE/USB controllers are
+         * already at slot 1, when qemu looks for the first free slot
+         * to place the VGA controller (which is always the first
+         * device added after integrated devices), it *always* ends up
+         * at slot 2.
+         */
         virDomainVideoDefPtr primaryVideo = def->videos[0];
         if (primaryVideo->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI) {
             primaryVideo->info.type = VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI;
@@ -2318,6 +2336,136 @@ error:
 }
 
 
+static bool
+qemuDomainMachineIsQ35(virDomainDefPtr def)
+{
+    return (STRPREFIX(def->os.machine, "pc-q35") ||
+            STREQ(def->os.machine, "q35"));
+}
+
+
+static int
+qemuDomainValidateDevicePCISlotsQ35(virDomainDefPtr def,
+                                    virQEMUCapsPtr qemuCaps,
+                                    qemuDomainPCIAddressSetPtr addrs)
+{
+    size_t i;
+    virDevicePCIAddress tmp_addr;
+    bool qemuDeviceVideoUsable = virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_VIDEO_PRIMARY);
+    virDevicePCIAddressPtr addrptr;
+    qemuDomainPCIConnectFlags flags = QEMU_PCI_CONNECT_TYPE_PCIE;
+
+    /* Verify that the first SATA controller is at 00:1F.2 */
+    /* the q35 machine type *always* has a SATA controller at this address */
+    for (i = 0; i < def->ncontrollers; i++) {
+        if (def->controllers[i]->type == VIR_DOMAIN_CONTROLLER_TYPE_SATA &&
+            def->controllers[i]->idx == 0) {
+            if (def->controllers[i]->info.type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI) {
+                if (def->controllers[i]->info.addr.pci.domain != 0 ||
+                    def->controllers[i]->info.addr.pci.bus != 0 ||
+                    def->controllers[i]->info.addr.pci.slot != 0x1F ||
+                    def->controllers[i]->info.addr.pci.function != 2) {
+                    virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                                   _("Primary SATA controller must have PCI address 0:0:1f.2"));
+                    goto error;
+                }
+            } else {
+                def->controllers[i]->info.type = VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI;
+                def->controllers[i]->info.addr.pci.domain = 0;
+                def->controllers[i]->info.addr.pci.bus = 0;
+                def->controllers[i]->info.addr.pci.slot = 0x1F;
+                def->controllers[i]->info.addr.pci.function = 2;
+            }
+        }
+    }
+
+    /* Reserve slot 0x1F function 0 (ISA bridge, not in config model)
+     * and function 3 (SMBus, also not (yet) in config model). As with
+     * the SATA controller, these devices are always present in a q35
+     * machine; there is no way to not have them.
+     */
+    if (addrs->nbuses) {
+        memset(&tmp_addr, 0, sizeof(tmp_addr));
+        tmp_addr.slot = 0x1F;
+        tmp_addr.function = 0;
+        tmp_addr.multi = 1;
+        if (qemuDomainPCIAddressReserveAddr(addrs, &tmp_addr, flags,
+                                            false, false) < 0)
+           goto error;
+        tmp_addr.function = 3;
+        tmp_addr.multi = 0;
+        if (qemuDomainPCIAddressReserveAddr(addrs, &tmp_addr, flags,
+                                            false, false) < 0)
+           goto error;
+    }
+
+    if (def->nvideos > 0) {
+        /* NB: unlike the pc machinetypes, on q35 machinetypes the
+         * integrated devices are at slot 0x1f, so when qemu looks for
+         * the first free lot for the first VGA, it will always be at
+         * slot 1 (which was used up by the integrated PIIX3 devices
+         * on pc machinetypes).
+         */
+        virDomainVideoDefPtr primaryVideo = def->videos[0];
+        if (primaryVideo->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI) {
+            primaryVideo->info.type = VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI;
+            primaryVideo->info.addr.pci.domain = 0;
+            primaryVideo->info.addr.pci.bus = 0;
+            primaryVideo->info.addr.pci.slot = 1;
+            primaryVideo->info.addr.pci.function = 0;
+            addrptr = &primaryVideo->info.addr.pci;
+
+            if (!qemuDomainPCIAddressValidate(addrs, addrptr, flags))
+                goto error;
+
+            if (qemuDomainPCIAddressSlotInUse(addrs, addrptr)) {
+                if (qemuDeviceVideoUsable) {
+                    virResetLastError();
+                    if (qemuDomainPCIAddressReserveNextSlot(addrs,
+                                                            &primaryVideo->info,
+                                                            flags) < 0)
+                        goto error;
+                } else {
+                    virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                                   _("PCI address 0:0:1.0 is in use, "
+                                     "QEMU needs it for primary video"));
+                    goto error;
+                }
+            } else if (qemuDomainPCIAddressReserveSlot(addrs, addrptr, flags) < 0) {
+                goto error;
+            }
+        } else if (!qemuDeviceVideoUsable) {
+            if (primaryVideo->info.addr.pci.domain != 0 ||
+                primaryVideo->info.addr.pci.bus != 0 ||
+                primaryVideo->info.addr.pci.slot != 1 ||
+                primaryVideo->info.addr.pci.function != 0) {
+                virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                               _("Primary video card must have PCI address 0:0:1.0"));
+                goto error;
+            }
+            /* If TYPE==PCI, then qemuCollectPCIAddress() function
+             * has already reserved the address, so we must skip */
+        }
+    } else if (addrs->nbuses && !qemuDeviceVideoUsable) {
+        memset(&tmp_addr, 0, sizeof(tmp_addr));
+        tmp_addr.slot = 1;
+
+        if (qemuDomainPCIAddressSlotInUse(addrs, &tmp_addr)) {
+            VIR_DEBUG("PCI address 0:0:1.0 in use, future addition of a video"
+                      " device will not be possible without manual"
+                      " intervention");
+            virResetLastError();
+        } else if (qemuDomainPCIAddressReserveSlot(addrs, &tmp_addr, flags) < 0) {
+            goto error;
+        }
+    }
+    return 0;
+
+error:
+    return -1;
+}
+
+
 /*
  * This assigns static PCI slots to all configured devices.
  * The ordering here is chosen to match the ordering used
@@ -2368,6 +2516,11 @@ qemuAssignDevicePCISlots(virDomainDefPtr def,
         goto error;
     }
 
+    if (qemuDomainMachineIsQ35(def) &&
+        qemuDomainValidateDevicePCISlotsQ35(def, qemuCaps, addrs) < 0) {
+        goto error;
+    }
+
     /* PCI controllers */
     for (i = 0; i < def->ncontrollers; i++) {
         if (def->controllers[i]->type == VIR_DOMAIN_CONTROLLER_TYPE_PCI) {
@@ -7679,6 +7832,9 @@ qemuBuildCommandLine(virConnectPtr conn,
                                        _("SATA is not supported with this "
                                          "QEMU binary"));
                         goto error;
+                    } else if (cont->idx == 0 && qemuDomainMachineIsQ35(def)) {
+                        /* first SATA controller on Q35 machines is implicit */
+                        continue;
                     } else {
                         char *devstr;
 
@@ -7692,6 +7848,7 @@ qemuBuildCommandLine(virConnectPtr conn,
                     }
                 } else if (cont->type == VIR_DOMAIN_CONTROLLER_TYPE_USB &&
                            cont->model == -1 &&
+                           !qemuDomainMachineIsQ35(def) &&
                            (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_PIIX3_USB_UHCI) ||
                             def->os.arch == VIR_ARCH_PPC64)) {
                     if (usblegacy) {
@@ -7716,7 +7873,7 @@ qemuBuildCommandLine(virConnectPtr conn,
         }
     }
 
-    if (usbcontroller == 0)
+    if (usbcontroller == 0 && !qemuDomainMachineIsQ35(def))
         virCommandAddArg(cmd, "-usb");
 
     for (i = 0; i < def->nhubs; i++) {
index 206d471287ab2bfcb2ab41a2f2c33e5112358c2d..d0f09ae6ad4d29389e906814324061692f0917e6 100644 (file)
@@ -700,6 +700,7 @@ qemuDomainDefPostParse(virDomainDefPtr def,
                        void *opaque ATTRIBUTE_UNUSED)
 {
     bool addDefaultUSB = true;
+    bool addImplicitSATA = false;
     bool addPCIRoot = false;
     bool addPCIeRoot = false;
 
@@ -722,6 +723,7 @@ qemuDomainDefPostParse(virDomainDefPtr def,
             STREQ(def->os.machine, "q35")) {
            addPCIeRoot = true;
            addDefaultUSB = false;
+           addImplicitSATA = true;
            break;
         }
         if (!STRPREFIX(def->os.machine, "pc-0.") &&
@@ -754,6 +756,11 @@ qemuDomainDefPostParse(virDomainDefPtr def,
             def, VIR_DOMAIN_CONTROLLER_TYPE_USB, 0, -1) < 0)
         return -1;
 
+    if (addImplicitSATA &&
+        virDomainDefMaybeAddController(
+            def, VIR_DOMAIN_CONTROLLER_TYPE_SATA, 0, -1) < 0)
+        return -1;
+
     if (addPCIRoot &&
         virDomainDefMaybeAddController(
             def, VIR_DOMAIN_CONTROLLER_TYPE_PCI, 0,
index 23db85cbef5baf6cd2d7a9aa574a38492e4dc4dd..cecef7b5ad14e63a13a3cf0c596dbaa67bbb1d66 100644 (file)
@@ -1,5 +1,5 @@
 LC_ALL=C PATH=/bin HOME=/home/test USER=test LOGNAME=test /usr/libexec/qemu-kvm \
 -S -M q35 -m 2048 -smp 2 -nographic -nodefaults \
 -monitor unix:/tmp/test-monitor,server,nowait -no-acpi -boot c \
--device i82801b11-bridge,id=pci.1,bus=pci.0,addr=0x1 \
--device pci-bridge,chassis_nr=2,id=pci.2,bus=pci.1,addr=0x1 -usb
+-device i82801b11-bridge,id=pci.1,bus=pci.0,addr=0x2 \
+-device pci-bridge,chassis_nr=2,id=pci.2,bus=pci.1,addr=0x1
index 1aa54558ded8dcba8bea7fea00b3ed80520ab334..d7fb90cde70b4a8d8a0cb138521f204056db384a 100644 (file)
@@ -15,7 +15,6 @@
   <devices>
     <emulator>/usr/libexec/qemu-kvm</emulator>
     <controller type='pci' index='0' model='pcie-root'/>
-    <controller type='usb' index='0'/>
     <memballoon model='none'/>
   </devices>
 </domain>
index ddff6f0d9eac3c99915d22784a76380196c91beb..6c24407a655f7ea413f123e2d12e38be423d66b9 100644 (file)
@@ -1,7 +1,6 @@
 LC_ALL=C PATH=/bin HOME=/home/test USER=test LOGNAME=test \
 /usr/libexec/qemu-kvm -S -M q35 -m 2048 -smp 2 -nographic -nodefaults \
 -monitor unix:/tmp/test-monitor,server,nowait -no-acpi -boot c \
--device i82801b11-bridge,id=pci.1,bus=pci.0,addr=0x1 \
+-device i82801b11-bridge,id=pci.1,bus=pci.0,addr=0x2 \
 -device pci-bridge,chassis_nr=2,id=pci.2,bus=pci.1,addr=0x1 \
--usb \
 -vga qxl -global qxl.ram_size=67108864 -global qxl.vram_size=18874368
index aba0f88b6697e368f9aa35309f0dbf8b5ff17d1a..0068d27ed2f9254dfc1ebcbce9c0d909105162a5 100644 (file)
@@ -995,11 +995,13 @@ mymain(void)
     DO_TEST("pci-bridge-many-disks",
             QEMU_CAPS_DEVICE, QEMU_CAPS_DRIVE, QEMU_CAPS_DEVICE_PCI_BRIDGE);
     DO_TEST("pcie-root",
+            QEMU_CAPS_ICH9_AHCI,
             QEMU_CAPS_DEVICE, QEMU_CAPS_DEVICE_PCI_BRIDGE,
             QEMU_CAPS_DEVICE_DMI_TO_PCI_BRIDGE);
     DO_TEST("q35",
             QEMU_CAPS_DEVICE, QEMU_CAPS_DEVICE_PCI_BRIDGE,
             QEMU_CAPS_DEVICE_DMI_TO_PCI_BRIDGE,
+            QEMU_CAPS_ICH9_AHCI,
             QEMU_CAPS_VGA, QEMU_CAPS_DEVICE_VIDEO_PRIMARY,
             QEMU_CAPS_VGA, QEMU_CAPS_VGA_QXL, QEMU_CAPS_DEVICE_QXL);
 
index 25c77f14431403cfb6a3a158533b94b5b9986a16..f10e85b2f218c6ea06f8a32284f3ccf877197952 100644 (file)
@@ -15,7 +15,7 @@
   <devices>
     <emulator>/usr/libexec/qemu-kvm</emulator>
     <controller type='pci' index='0' model='pcie-root'/>
-    <controller type='usb' index='0'/>
+    <controller type='sata' index='0'/>
     <controller type='pci' index='1' model='dmi-to-pci-bridge'/>
     <controller type='pci' index='2' model='pci-bridge'/>
     <memballoon model='none'/>
diff --git a/tests/qemuxml2xmloutdata/qemuxml2xmlout-q35.xml b/tests/qemuxml2xmloutdata/qemuxml2xmlout-q35.xml
new file mode 100644 (file)
index 0000000..2a86e61
--- /dev/null
@@ -0,0 +1,26 @@
+<domain type='qemu'>
+  <name>q35-test</name>
+  <uuid>11dbdcdd-4c3b-482b-8903-9bdb8c0a2774</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='q35'>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/libexec/qemu-kvm</emulator>
+    <controller type='pci' index='0' model='pcie-root'/>
+    <controller type='pci' index='1' model='dmi-to-pci-bridge'/>
+    <controller type='pci' index='2' model='pci-bridge'/>
+    <controller type='sata' index='0'/>
+    <video>
+      <model type='qxl' ram='65536' vram='18432' heads='1'/>
+    </video>
+    <memballoon model='none'/>
+  </devices>
+</domain>
index 8b4590af28f42b7fa90d270433f8bac366072a47..5c6730d25ebc54e0dd5cbc4b8f87d106f0646e7f 100644 (file)
@@ -295,7 +295,7 @@ mymain(void)
     DO_TEST_DIFFERENT("pci-autoadd-addr");
     DO_TEST_DIFFERENT("pci-autoadd-idx");
     DO_TEST_DIFFERENT("pcie-root");
-    DO_TEST("q35");
+    DO_TEST_DIFFERENT("q35");
 
     DO_TEST("hostdev-scsi-lsi");
     DO_TEST("hostdev-scsi-virtio-scsi");