Skip to content

Commit

Permalink
x86/PCI: Refine the way to release PCI IRQ resources
Browse files Browse the repository at this point in the history
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 c03b3b0 ("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 3eec595
("x86, irq, PCI: Keep IRQ assignment for PCI devices during
suspend/hibernation") and 9eabc99 ("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 cffe0a2 ("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>
  • Loading branch information
Jiang Liu authored and rafaeljw committed Feb 5, 2015
1 parent 593669c commit b4b55cd
Show file tree
Hide file tree
Showing 5 changed files with 32 additions and 32 deletions.
2 changes: 0 additions & 2 deletions arch/x86/include/asm/pci_x86.h
Original file line number Diff line number Diff line change
Expand Up @@ -93,8 +93,6 @@ extern raw_spinlock_t pci_config_lock;
extern int (*pcibios_enable_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 {
int (*read)(unsigned int domain, unsigned int bus, unsigned int devfn,
int reg, int len, u32 *val);
Expand Down
34 changes: 28 additions & 6 deletions arch/x86/pci/common.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
if (!raw_pci_ops) {
Expand All @@ -509,6 +534,9 @@ int __init pcibios_init(void)

if (pci_bf_sort >= pci_force_bf)
pci_sort_breadthfirst();

bus_register_notifier(&pci_bus_type, &pci_irq_nb);

return 0;
}

Expand Down Expand Up @@ -667,12 +695,6 @@ int pcibios_enable_device(struct pci_dev *dev, int mask)
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)
{
if (raw_pci_ext_ops)
Expand Down
4 changes: 2 additions & 2 deletions arch/x86/pci/intel_mid_pci.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
if (!mp_should_keep_irq(&dev->dev) && dev->irq_managed &&
dev->irq > 0) {
if (dev->irq_managed && dev->irq > 0) {
mp_unmap_irq(dev->irq);
dev->irq_managed = 0;
dev->irq = 0;
}
}

Expand Down
15 changes: 1 addition & 14 deletions arch/x86/pci/irq.c
Original file line number Diff line number Diff line change
Expand Up @@ -1256,22 +1256,9 @@ static int pirq_enable_irq(struct pci_dev *dev)
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)
{
if (io_apic_assign_pci_irqs && !mp_should_keep_irq(&dev->dev) &&
dev->irq_managed && dev->irq) {
if (io_apic_assign_pci_irqs && dev->irq_managed && dev->irq) {
mp_unmap_irq(dev->irq);
dev->irq = 0;
dev->irq_managed = 0;
Expand Down
9 changes: 1 addition & 8 deletions drivers/acpi/pci_irq.c
Original file line number Diff line number Diff line change
Expand Up @@ -485,14 +485,6 @@ void acpi_pci_irq_disable(struct pci_dev *dev)
if (!pin || !dev->irq_managed || dev->irq <= 0)
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);
if (!entry)
return;
Expand All @@ -513,5 +505,6 @@ void acpi_pci_irq_disable(struct pci_dev *dev)
if (gsi >= 0) {
acpi_unregister_gsi(gsi);
dev->irq_managed = 0;
dev->irq = 0;
}
}

0 comments on commit b4b55cd

Please sign in to comment.