aboutsummaryrefslogtreecommitdiffstats
path: root/arch/x86/pci
diff options
context:
space:
mode:
authorJiang Liu <jiang.liu@linux.intel.com>2015-02-05 00:44:47 -0500
committerRafael J. Wysocki <rafael.j.wysocki@intel.com>2015-02-05 09:09:26 -0500
commitb4b55cda587442477a3a9f0669e26bba4b7800c0 (patch)
treee60388e241582d3d9a04e11e230a7805e9b5969b /arch/x86/pci
parent593669c2ac0fe18baee04a3cd5539a148aa48574 (diff)
x86/PCI: Refine the way to release PCI IRQ resources
Some PCI device drivers assume that pci_dev->irq won't change after calling pci_disable_device() and pci_enable_device() during suspend and resume. Commit c03b3b0738a5 ("x86, irq, mpparse: Release IOAPIC pin when PCI device is disabled") frees PCI IRQ resources when pci_disable_device() is called and reallocate IRQ resources when pci_enable_device() is called again. This breaks above assumption. So commit 3eec595235c1 ("x86, irq, PCI: Keep IRQ assignment for PCI devices during suspend/hibernation") and 9eabc99a635a ("x86, irq, PCI: Keep IRQ assignment for runtime power management") fix the issue by avoiding freeing/reallocating IRQ resources during PCI device suspend/resume. They achieve this by checking dev.power.is_prepared and dev.power.runtime_status. PM maintainer, Rafael, then pointed out that it's really an ugly fix which leaking PM internal state information to IRQ subsystem. Recently David Vrabel <david.vrabel@citrix.com> also reports an regression in pciback driver caused by commit cffe0a2b5a34 ("x86, irq: Keep balance of IOAPIC pin reference count"). Please refer to: http://lkml.org/lkml/2015/1/14/546 So this patch refine the way to release PCI IRQ resources. Instead of releasing PCI IRQ resources in pci_disable_device()/ pcibios_disable_device(), we now release it at driver unbinding notification BUS_NOTIFY_UNBOUND_DRIVER. In other word, we only release PCI IRQ resources when there's no driver bound to the PCI device, and it keeps the assumption that pci_dev->irq won't through multiple invocation of pci_enable_device()/pci_disable_device(). Signed-off-by: Jiang Liu <jiang.liu@linux.intel.com> Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
Diffstat (limited to 'arch/x86/pci')
-rw-r--r--arch/x86/pci/common.c34
-rw-r--r--arch/x86/pci/intel_mid_pci.c4
-rw-r--r--arch/x86/pci/irq.c15
3 files changed, 31 insertions, 22 deletions
diff --git a/arch/x86/pci/common.c b/arch/x86/pci/common.c
index 7b20bccf3648..ff1f0afa5ed1 100644
--- a/arch/x86/pci/common.c
+++ b/arch/x86/pci/common.c
@@ -497,6 +497,31 @@ void __init pcibios_set_cache_line_size(void)
497 } 497 }
498} 498}
499 499
500/*
501 * Some device drivers assume dev->irq won't change after calling
502 * pci_disable_device(). So delay releasing of IRQ resource to driver
503 * unbinding time. Otherwise it will break PM subsystem and drivers
504 * like xen-pciback etc.
505 */
506static int pci_irq_notifier(struct notifier_block *nb, unsigned long action,
507 void *data)
508{
509 struct pci_dev *dev = to_pci_dev(data);
510
511 if (action != BUS_NOTIFY_UNBOUND_DRIVER)
512 return NOTIFY_DONE;
513
514 if (pcibios_disable_irq)
515 pcibios_disable_irq(dev);
516
517 return NOTIFY_OK;
518}
519
520static struct notifier_block pci_irq_nb = {
521 .notifier_call = pci_irq_notifier,
522 .priority = INT_MIN,
523};
524
500int __init pcibios_init(void) 525int __init pcibios_init(void)
501{ 526{
502 if (!raw_pci_ops) { 527 if (!raw_pci_ops) {
@@ -509,6 +534,9 @@ int __init pcibios_init(void)
509 534
510 if (pci_bf_sort >= pci_force_bf) 535 if (pci_bf_sort >= pci_force_bf)
511 pci_sort_breadthfirst(); 536 pci_sort_breadthfirst();
537
538 bus_register_notifier(&pci_bus_type, &pci_irq_nb);
539
512 return 0; 540 return 0;
513} 541}
514 542
@@ -667,12 +695,6 @@ int pcibios_enable_device(struct pci_dev *dev, int mask)
667 return 0; 695 return 0;
668} 696}
669 697
670void pcibios_disable_device (struct pci_dev *dev)
671{
672 if (!pci_dev_msi_enabled(dev) && pcibios_disable_irq)
673 pcibios_disable_irq(dev);
674}
675
676int pci_ext_cfg_avail(void) 698int pci_ext_cfg_avail(void)
677{ 699{
678 if (raw_pci_ext_ops) 700 if (raw_pci_ext_ops)
diff --git a/arch/x86/pci/intel_mid_pci.c b/arch/x86/pci/intel_mid_pci.c
index 44b9271580b5..95c2471f6819 100644
--- a/arch/x86/pci/intel_mid_pci.c
+++ b/arch/x86/pci/intel_mid_pci.c
@@ -234,10 +234,10 @@ static int intel_mid_pci_irq_enable(struct pci_dev *dev)
234 234
235static void intel_mid_pci_irq_disable(struct pci_dev *dev) 235static void intel_mid_pci_irq_disable(struct pci_dev *dev)
236{ 236{
237 if (!mp_should_keep_irq(&dev->dev) && dev->irq_managed && 237 if (dev->irq_managed && dev->irq > 0) {
238 dev->irq > 0) {
239 mp_unmap_irq(dev->irq); 238 mp_unmap_irq(dev->irq);
240 dev->irq_managed = 0; 239 dev->irq_managed = 0;
240 dev->irq = 0;
241 } 241 }
242} 242}
243 243
diff --git a/arch/x86/pci/irq.c b/arch/x86/pci/irq.c
index 5dc6ca5e1741..e71b3dbd87b8 100644
--- a/arch/x86/pci/irq.c
+++ b/arch/x86/pci/irq.c
@@ -1256,22 +1256,9 @@ static int pirq_enable_irq(struct pci_dev *dev)
1256 return 0; 1256 return 0;
1257} 1257}
1258 1258
1259bool mp_should_keep_irq(struct device *dev)
1260{
1261 if (dev->power.is_prepared)
1262 return true;
1263#ifdef CONFIG_PM
1264 if (dev->power.runtime_status == RPM_SUSPENDING)
1265 return true;
1266#endif
1267
1268 return false;
1269}
1270
1271static void pirq_disable_irq(struct pci_dev *dev) 1259static void pirq_disable_irq(struct pci_dev *dev)
1272{ 1260{
1273 if (io_apic_assign_pci_irqs && !mp_should_keep_irq(&dev->dev) && 1261 if (io_apic_assign_pci_irqs && dev->irq_managed && dev->irq) {
1274 dev->irq_managed && dev->irq) {
1275 mp_unmap_irq(dev->irq); 1262 mp_unmap_irq(dev->irq);
1276 dev->irq = 0; 1263 dev->irq = 0;
1277 dev->irq_managed = 0; 1264 dev->irq_managed = 0;