x86, irq, PCI: Keep IRQ assignment for PCI devices during suspend/hibernation
authorJiang Liu <jiang.liu@linux.intel.com>
Fri, 8 Aug 2014 06:07:51 +0000 (14:07 +0800)
committerThomas Gleixner <tglx@linutronix.de>
Fri, 8 Aug 2014 09:14:45 +0000 (11:14 +0200)
Now IOAPIC driver dynamically allocates IRQ numbers for IOAPIC pins.
We need to keep IRQ assignment for PCI devices during suspend/hibernation,
otherwise it may cause failure of suspend/hibernation due to:
1) Device driver calls pci_enable_device() to allocate an IRQ number
   and register interrupt handler on the returned IRQ.
2) Device driver's suspend callback calls pci_disable_device() and
   release assigned IRQ in turn.
3) Device driver's resume callback calls pci_enable_device() to
   allocate IRQ number again. A different IRQ number may be assigned
   by IOAPIC driver this time.
4) Now the hardware delivers interrupt to the new IRQ but interrupt
   handler is still registered against the old IRQ, so it breaks
   suspend/hibernation.

To fix this issue, we keep IRQ assignment during suspend/hibernation.
Flag pci_dev.dev.power.is_prepared is used to detect that
pci_disable_device() is called during suspend/hibernation.

Reported-and-Tested-by: Borislav Petkov <bp@suse.de>
Signed-off-by: Jiang Liu <jiang.liu@linux.intel.com>
Cc: Konrad Rzeszutek Wilk <konrad.wilk@oracle.com>
Cc: Tony Luck <tony.luck@intel.com>
Cc: Joerg Roedel <joro@8bytes.org>
Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Cc: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Cc: Rafael J. Wysocki <rjw@rjwysocki.net>
Cc: Bjorn Helgaas <bhelgaas@google.com>
Cc: Randy Dunlap <rdunlap@infradead.org>
Cc: Yinghai Lu <yinghai@kernel.org>
Cc: Grant Likely <grant.likely@linaro.org>
Cc: Len Brown <lenb@kernel.org>
Link: http://lkml.kernel.org/r/1407478071-29399-1-git-send-email-jiang.liu@linux.intel.com
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
arch/x86/pci/intel_mid_pci.c
arch/x86/pci/irq.c
drivers/acpi/pci_irq.c

index 09fece3685921127471f583303b41ec68167b3cb..3865116c51fbf583a923d4131882c653b5fc3c24 100644 (file)
@@ -229,7 +229,7 @@ static int intel_mid_pci_irq_enable(struct pci_dev *dev)
 
 static void intel_mid_pci_irq_disable(struct pci_dev *dev)
 {
-       if (dev->irq > 0)
+       if (!dev->dev.power.is_prepared && dev->irq > 0)
                mp_unmap_irq(dev->irq);
 }
 
index 748cfe8ab32246d17210238b74029652a499ffef..bc1a2c341891034d04c86b834c5d132e7f954e33 100644 (file)
@@ -1256,7 +1256,8 @@ static int pirq_enable_irq(struct pci_dev *dev)
 
 static void pirq_disable_irq(struct pci_dev *dev)
 {
-       if (io_apic_assign_pci_irqs && dev->irq) {
+       if (io_apic_assign_pci_irqs && !dev->dev.power.is_prepared &&
+           dev->irq) {
                mp_unmap_irq(dev->irq);
                dev->irq = 0;
        }
index 6ba463ceccc634781108c543845bbe9c0276c95a..c96887d5289eaed997e29eaab738f8bb4bb488ee 100644 (file)
@@ -481,6 +481,10 @@ void acpi_pci_irq_disable(struct pci_dev *dev)
        if (!pin)
                return;
 
+       /* Keep IOAPIC pin configuration when suspending */
+       if (dev->dev.power.is_prepared)
+               return;
+
        entry = acpi_pci_irq_lookup(dev, pin);
        if (!entry)
                return;