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