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. Commitc03b3b0738
("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 commit3eec595235
("x86, irq, PCI: Keep IRQ assignment for PCI devices during suspend/hibernation") and9eabc99a63
("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 commitcffe0a2b5a
("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>
This commit is contained in:
parent
593669c2ac
commit
b4b55cda58
|
@ -93,8 +93,6 @@ extern raw_spinlock_t pci_config_lock;
|
||||||
extern int (*pcibios_enable_irq)(struct pci_dev *dev);
|
extern int (*pcibios_enable_irq)(struct pci_dev *dev);
|
||||||
extern void (*pcibios_disable_irq)(struct pci_dev *dev);
|
extern void (*pcibios_disable_irq)(struct pci_dev *dev);
|
||||||
|
|
||||||
extern bool mp_should_keep_irq(struct device *dev);
|
|
||||||
|
|
||||||
struct pci_raw_ops {
|
struct pci_raw_ops {
|
||||||
int (*read)(unsigned int domain, unsigned int bus, unsigned int devfn,
|
int (*read)(unsigned int domain, unsigned int bus, unsigned int devfn,
|
||||||
int reg, int len, u32 *val);
|
int reg, int len, u32 *val);
|
||||||
|
|
|
@ -497,6 +497,31 @@ void __init pcibios_set_cache_line_size(void)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Some device drivers assume dev->irq won't change after calling
|
||||||
|
* pci_disable_device(). So delay releasing of IRQ resource to driver
|
||||||
|
* unbinding time. Otherwise it will break PM subsystem and drivers
|
||||||
|
* like xen-pciback etc.
|
||||||
|
*/
|
||||||
|
static int pci_irq_notifier(struct notifier_block *nb, unsigned long action,
|
||||||
|
void *data)
|
||||||
|
{
|
||||||
|
struct pci_dev *dev = to_pci_dev(data);
|
||||||
|
|
||||||
|
if (action != BUS_NOTIFY_UNBOUND_DRIVER)
|
||||||
|
return NOTIFY_DONE;
|
||||||
|
|
||||||
|
if (pcibios_disable_irq)
|
||||||
|
pcibios_disable_irq(dev);
|
||||||
|
|
||||||
|
return NOTIFY_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct notifier_block pci_irq_nb = {
|
||||||
|
.notifier_call = pci_irq_notifier,
|
||||||
|
.priority = INT_MIN,
|
||||||
|
};
|
||||||
|
|
||||||
int __init pcibios_init(void)
|
int __init pcibios_init(void)
|
||||||
{
|
{
|
||||||
if (!raw_pci_ops) {
|
if (!raw_pci_ops) {
|
||||||
|
@ -509,6 +534,9 @@ int __init pcibios_init(void)
|
||||||
|
|
||||||
if (pci_bf_sort >= pci_force_bf)
|
if (pci_bf_sort >= pci_force_bf)
|
||||||
pci_sort_breadthfirst();
|
pci_sort_breadthfirst();
|
||||||
|
|
||||||
|
bus_register_notifier(&pci_bus_type, &pci_irq_nb);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -667,12 +695,6 @@ int pcibios_enable_device(struct pci_dev *dev, int mask)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void pcibios_disable_device (struct pci_dev *dev)
|
|
||||||
{
|
|
||||||
if (!pci_dev_msi_enabled(dev) && pcibios_disable_irq)
|
|
||||||
pcibios_disable_irq(dev);
|
|
||||||
}
|
|
||||||
|
|
||||||
int pci_ext_cfg_avail(void)
|
int pci_ext_cfg_avail(void)
|
||||||
{
|
{
|
||||||
if (raw_pci_ext_ops)
|
if (raw_pci_ext_ops)
|
||||||
|
|
|
@ -234,10 +234,10 @@ static int intel_mid_pci_irq_enable(struct pci_dev *dev)
|
||||||
|
|
||||||
static void intel_mid_pci_irq_disable(struct pci_dev *dev)
|
static void intel_mid_pci_irq_disable(struct pci_dev *dev)
|
||||||
{
|
{
|
||||||
if (!mp_should_keep_irq(&dev->dev) && dev->irq_managed &&
|
if (dev->irq_managed && dev->irq > 0) {
|
||||||
dev->irq > 0) {
|
|
||||||
mp_unmap_irq(dev->irq);
|
mp_unmap_irq(dev->irq);
|
||||||
dev->irq_managed = 0;
|
dev->irq_managed = 0;
|
||||||
|
dev->irq = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1256,22 +1256,9 @@ static int pirq_enable_irq(struct pci_dev *dev)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool mp_should_keep_irq(struct device *dev)
|
|
||||||
{
|
|
||||||
if (dev->power.is_prepared)
|
|
||||||
return true;
|
|
||||||
#ifdef CONFIG_PM
|
|
||||||
if (dev->power.runtime_status == RPM_SUSPENDING)
|
|
||||||
return true;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void pirq_disable_irq(struct pci_dev *dev)
|
static void pirq_disable_irq(struct pci_dev *dev)
|
||||||
{
|
{
|
||||||
if (io_apic_assign_pci_irqs && !mp_should_keep_irq(&dev->dev) &&
|
if (io_apic_assign_pci_irqs && dev->irq_managed && dev->irq) {
|
||||||
dev->irq_managed && dev->irq) {
|
|
||||||
mp_unmap_irq(dev->irq);
|
mp_unmap_irq(dev->irq);
|
||||||
dev->irq = 0;
|
dev->irq = 0;
|
||||||
dev->irq_managed = 0;
|
dev->irq_managed = 0;
|
||||||
|
|
|
@ -485,14 +485,6 @@ void acpi_pci_irq_disable(struct pci_dev *dev)
|
||||||
if (!pin || !dev->irq_managed || dev->irq <= 0)
|
if (!pin || !dev->irq_managed || dev->irq <= 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
/* Keep IOAPIC pin configuration when suspending */
|
|
||||||
if (dev->dev.power.is_prepared)
|
|
||||||
return;
|
|
||||||
#ifdef CONFIG_PM
|
|
||||||
if (dev->dev.power.runtime_status == RPM_SUSPENDING)
|
|
||||||
return;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
entry = acpi_pci_irq_lookup(dev, pin);
|
entry = acpi_pci_irq_lookup(dev, pin);
|
||||||
if (!entry)
|
if (!entry)
|
||||||
return;
|
return;
|
||||||
|
@ -513,5 +505,6 @@ void acpi_pci_irq_disable(struct pci_dev *dev)
|
||||||
if (gsi >= 0) {
|
if (gsi >= 0) {
|
||||||
acpi_unregister_gsi(gsi);
|
acpi_unregister_gsi(gsi);
|
||||||
dev->irq_managed = 0;
|
dev->irq_managed = 0;
|
||||||
|
dev->irq = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue