diff options
author | Jiang Liu <jiang.liu@linux.intel.com> | 2015-02-05 00:44:47 -0500 |
---|---|---|
committer | Rafael J. Wysocki <rafael.j.wysocki@intel.com> | 2015-02-05 09:09:26 -0500 |
commit | b4b55cda587442477a3a9f0669e26bba4b7800c0 (patch) | |
tree | e60388e241582d3d9a04e11e230a7805e9b5969b /arch/x86/pci | |
parent | 593669c2ac0fe18baee04a3cd5539a148aa48574 (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.c | 34 | ||||
-rw-r--r-- | arch/x86/pci/intel_mid_pci.c | 4 | ||||
-rw-r--r-- | arch/x86/pci/irq.c | 15 |
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 | */ | ||
506 | static 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 | |||
520 | static struct notifier_block pci_irq_nb = { | ||
521 | .notifier_call = pci_irq_notifier, | ||
522 | .priority = INT_MIN, | ||
523 | }; | ||
524 | |||
500 | int __init pcibios_init(void) | 525 | int __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 | ||
670 | void 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 | |||
676 | int pci_ext_cfg_avail(void) | 698 | int 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 | ||
235 | static void intel_mid_pci_irq_disable(struct pci_dev *dev) | 235 | static 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 | ||
1259 | bool 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 | |||
1271 | static void pirq_disable_irq(struct pci_dev *dev) | 1259 | static 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; |