Blame SOURCES/kvm-uhci-UNfix-irq-routing-for-RHEL-6-machtypes-RHEL-only.patch

d81766
From 639e4c3be0cc6d92b75d9267fe40645d5f515b47 Mon Sep 17 00:00:00 2001
d81766
From: Laszlo Ersek <lersek@redhat.com>
d81766
Date: Thu, 10 Jul 2014 13:54:30 +0200
d81766
Subject: uhci: UNfix irq routing for RHEL-6 machtypes (RHEL only)
d81766
d81766
Message-id: <1405000470-9229-2-git-send-email-lersek@redhat.com>
d81766
Patchwork-id: 59740
d81766
O-Subject: [RHEV-7.1 qemu-kvm-rhev PATCH 1/1] uhci: UNfix irq routing for RHEL-6 machtypes (RHEL only)
d81766
Bugzilla: 1103581
d81766
RH-Acked-by: Gerd Hoffmann <kraxel@redhat.com>
d81766
RH-Acked-by: Michael S. Tsirkin <mst@redhat.com>
d81766
RH-Acked-by: Paolo Bonzini <pbonzini@redhat.com>
d81766
d81766
RH-Author: Laszlo Ersek <lersek@redhat.com>
d81766
Message-id: <1398337043-4967-1-git-send-email-lersek@redhat.com>
d81766
Patchwork-id: 58540
d81766
O-Subject: [RHEL-7.0 0day qemu-kvm PATCH] uhci: UNfix irq routing for RHEL-6 machtypes (RHEL only)
d81766
Bugzilla: 1085701
d81766
RH-Acked-by: Gerd Hoffmann <kraxel@redhat.com>
d81766
RH-Acked-by: Michael S. Tsirkin <mst@redhat.com>
d81766
RH-Acked-by: Paolo Bonzini <pbonzini@redhat.com>
d81766
d81766
Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=1085701
d81766
Brew:     https://brewweb.devel.redhat.com/taskinfo?taskID=7368968
d81766
d81766
Tested by myself and QE.
d81766
d81766
(Note that the bulk of the work related to this issue happened in bug
d81766
1027565 comments 64 to 74, because QE was experiencing it with the other
d81766
(original) issue reported in bug 1027565. Only later did I realize that
d81766
bug 1085701 already existed for this specific problem.)
d81766
d81766
Refer to the following upstream commit, present in v1.2.0 and RHEL-7:
d81766
d81766
    commit 973002c11460efd3c17fe61a76711a103e30e1f9
d81766
    Author: Gerd Hoffmann <kraxel@redhat.com>
d81766
    Date:   Fri May 25 12:53:47 2012 +0200
d81766
d81766
        uhci: fix irq routing
d81766
d81766
        The multifunction ich9 ehci controller with uhci companions uses a
d81766
        different interrupt pin for each function.  The three uhci devices
d81766
        get pins A, B and C, whereas ehci uses pin D.  This way the guest
d81766
        can assign different IRQ lines to each controller.
d81766
d81766
        Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
d81766
d81766
This is a performance optimization so that IRQ sharing can be avoided in
d81766
the guest.
d81766
d81766
Contrarily, the RHEL-6 emulator exclusively assigns pin D to each of the
d81766
three devices (see "hw/usb-uhci.c" there):
d81766
- usb_uhci_ich9_1_initfn()
d81766
- usb_uhci_ich9_2_initfn()
d81766
- usb_uhci_ich9_3_initfn()
d81766
d81766
These init functions call usb_uhci_common_initfn(), which in turn assigns:
d81766
d81766
    pci_conf[0x3d] = 4; // interrupt pin 3
d81766
d81766
This mismatch breaks migration for rhel6.x.0 machine types from the
d81766
RHEL-6.5 emulator to the RHEL-7.0 one.
d81766
d81766
For example, considering the uhci1 controller, the guest kernel, started
d81766
on the RHEL-6.5 source host, will see pin D advertised, and (according to
d81766
the PCI interrupt routing table provided by SeaBIOS) will route it to IRQ
d81766
11:
d81766
d81766
  dmesg:
d81766
d81766
    uhci_hcd 0000:00:11.0: PCI INT D -> Link[LNKD] -> GSI 11 (level, high)
d81766
    -> IRQ 11
d81766
d81766
  /proc/interrupts:
d81766
d81766
     10: ... IO-APIC-fasteoi   virtio0
d81766
     11: ... IO-APIC-fasteoi   uhci_hcd:usb1
d81766
d81766
When the same guest kernel, using the same rhel6.5.0 machine type, is
d81766
started fresh on the RHEL-7.0 target host, pin A is advertised instead
d81766
(and routed to a different IRQ, based on the same table from SeaBIOS):
d81766
d81766
  dmesg:
d81766
d81766
    uhci_hcd 0000:00:11.0: PCI INT A -> Link[LNKA] -> GSI 10 (level, high)
d81766
    -> IRQ 10
d81766
d81766
  /proc/interrupts:
d81766
d81766
     10: ... IO-APIC-fasteoi   uhci_hcd:usb1, virtio0
d81766
d81766
This is no problem as long as we don't migrate.
d81766
d81766
When we migrate the rhel6.x.0 machtype guest from the RHEL-6.5 host to the
d81766
RHEL-7.0 host, the guest kernel (having booted on the source host) will
d81766
expect the interrupts for the UHCI device on pin D / IRQ 11. However, the
d81766
target host will inject the interrupts on pin A / IRQ 10. No handler in
d81766
the guest kernel will claim such IRQ 10 instances (examples are: HDA or
d81766
virtio-balloon), hence IRQ 10 will be disabled.
d81766
d81766
We can fix this in at least two ways:
d81766
(1) Drop the persistent "UHCIState.irq_pin" field, and simply use the pin
d81766
    identifier that is stored in (already migrated) PCI config space (at
d81766
    offset 0x3d).
d81766
(2) Introduce yet another RHEL-6 compatibility knob that selects pin D for
d81766
    all three ICH9-UHCI controllers.
d81766
d81766
Since PCI config space could be write-accessible to the guest, plus it
d81766
could originate from an untrusted migration source as well, and we'd use
d81766
the setting as a subscript into the s->dev.irq[] array in
d81766
uhci_update_irq(), it seems safer to go with (2).
d81766
d81766
Signed-off-by: Laszlo Ersek <lersek@redhat.com>
d81766
d81766
diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c
d81766
index 021cf22..1393ca1 100644
d81766
--- a/hw/i386/pc_piix.c
d81766
+++ b/hw/i386/pc_piix.c
d81766
@@ -1157,6 +1157,7 @@ static void pc_compat_rhel650(MachineState *machine)
d81766
     has_acpi_build = false;
d81766
     gigabyte_align = false;
d81766
     shadow_bios_after_incoming = true;
d81766
+    ich9_uhci123_irqpin_override = true;
d81766
 }
d81766
 
d81766
 static void pc_init_rhel650(MachineState *machine)
d81766
diff --git a/hw/usb/hcd-uhci.c b/hw/usb/hcd-uhci.c
d81766
index c3bf72c..61f3578 100644
d81766
--- a/hw/usb/hcd-uhci.c
d81766
+++ b/hw/usb/hcd-uhci.c
d81766
@@ -153,6 +153,8 @@ typedef struct UHCI_QH {
d81766
     uint32_t el_link;
d81766
 } UHCI_QH;
d81766
 
d81766
+bool ich9_uhci123_irqpin_override;
d81766
+
d81766
 static void uhci_async_cancel(UHCIAsync *async);
d81766
 static void uhci_queue_fill(UHCIQueue *q, UHCI_TD *td);
d81766
 static void uhci_resume(void *opaque);
d81766
@@ -1200,12 +1202,23 @@ static int usb_uhci_common_initfn(PCIDevice *dev)
d81766
     UHCIState *s = DO_UPCAST(UHCIState, dev, dev);
d81766
     uint8_t *pci_conf = s->dev.config;
d81766
     int i;
d81766
+    int irq_pin;
d81766
 
d81766
     pci_conf[PCI_CLASS_PROG] = 0x00;
d81766
     /* TODO: reset value should be 0. */
d81766
     pci_conf[USB_SBRN] = USB_RELEASE_1; // release number
d81766
 
d81766
-    pci_config_set_interrupt_pin(pci_conf, u->info.irq_pin + 1);
d81766
+    if (ich9_uhci123_irqpin_override &&
d81766
+        u->info.vendor_id == PCI_VENDOR_ID_INTEL &&
d81766
+        (u->info.device_id == PCI_DEVICE_ID_INTEL_82801I_UHCI1 ||
d81766
+         u->info.device_id == PCI_DEVICE_ID_INTEL_82801I_UHCI2 ||
d81766
+         u->info.device_id == PCI_DEVICE_ID_INTEL_82801I_UHCI3)) {
d81766
+        fprintf(stderr, "RHEL-6 compat: %s: irq_pin = 3\n", u->info.name);
d81766
+        irq_pin = 3;
d81766
+    } else {
d81766
+        irq_pin = u->info.irq_pin;
d81766
+    }
d81766
+    pci_config_set_interrupt_pin(pci_conf, irq_pin + 1);
d81766
 
d81766
     if (s->masterbus) {
d81766
         USBPort *ports[NB_PORTS];
d81766
diff --git a/include/hw/usb.h b/include/hw/usb.h
d81766
index 8bcab48..fad90bf 100644
d81766
--- a/include/hw/usb.h
d81766
+++ b/include/hw/usb.h
d81766
@@ -600,4 +600,8 @@ int usb_get_quirks(uint16_t vendor_id, uint16_t product_id,
d81766
                    uint8_t interface_class, uint8_t interface_subclass,
d81766
                    uint8_t interface_protocol);
d81766
 
d81766
+
d81766
+/* hcd-uhci.c -- RHEL-6 machine type compatibility */
d81766
+extern bool ich9_uhci123_irqpin_override;
d81766
+
d81766
 #endif