Skip to content

Commit

Permalink
x86, irq: Keep balance of IOAPIC pin reference count
Browse files Browse the repository at this point in the history
To keep balance of IOAPIC pin reference count, we need to protect
pirq_enable_irq(), acpi_pci_irq_enable() and intel_mid_pci_irq_enable()
from reentrance. There are two cases which will cause reentrance.

The first case is caused by suspend/hibernation. If pcibios_disable_irq
is called during suspending/hibernating, we don't release the assigned
IRQ number, otherwise it may break the suspend/hibernation. So late when
pcibios_enable_irq is called during resume, we shouldn't allocate IRQ
number again.

The second case is that function acpi_pci_irq_enable() may be called
twice for PCI devices present at boot time as below:
1) pci_acpi_init()
	--> acpi_pci_irq_enable() if pci_routeirq is true
2) pci_enable_device()
	--> pcibios_enable_device()
		--> acpi_pci_irq_enable()
We can't kill kernel parameter pci_routeirq yet because it's still
needed for debugging purpose.

So flag irq_managed is introduced to track whether IRQ number is
assigned by OS and to protect pirq_enable_irq(), acpi_pci_irq_enable()
and intel_mid_pci_irq_enable() from reentrance.

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: Borislav Petkov <bp@alien8.de>
Cc: Len Brown <lenb@kernel.org>
Link: http://lkml.kernel.org/r/1414387308-27148-13-git-send-email-jiang.liu@linux.intel.com
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
  • Loading branch information
Jiang Liu authored and KAGA-KOKO committed Dec 16, 2014
1 parent 67dc5e7 commit cffe0a2
Show file tree
Hide file tree
Showing 4 changed files with 25 additions and 4 deletions.
10 changes: 9 additions & 1 deletion arch/x86/pci/intel_mid_pci.c
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,9 @@ static int intel_mid_pci_irq_enable(struct pci_dev *dev)
{
int polarity;

if (dev->irq_managed && dev->irq > 0)
return 0;

if (intel_mid_identify_cpu() == INTEL_MID_CPU_CHIP_TANGIER)
polarity = 0; /* active high */
else
Expand All @@ -224,13 +227,18 @@ static int intel_mid_pci_irq_enable(struct pci_dev *dev)
if (mp_map_gsi_to_irq(dev->irq, IOAPIC_MAP_ALLOC) < 0)
return -EBUSY;

dev->irq_managed = 1;

return 0;
}

static void intel_mid_pci_irq_disable(struct pci_dev *dev)
{
if (!mp_should_keep_irq(&dev->dev) && dev->irq > 0)
if (!mp_should_keep_irq(&dev->dev) && dev->irq_managed &&
dev->irq > 0) {
mp_unmap_irq(dev->irq);
dev->irq_managed = 0;
}
}

struct pci_ops intel_mid_pci_ops = {
Expand Down
7 changes: 6 additions & 1 deletion arch/x86/pci/irq.c
Original file line number Diff line number Diff line change
Expand Up @@ -1202,6 +1202,9 @@ static int pirq_enable_irq(struct pci_dev *dev)
int irq;
struct io_apic_irq_attr irq_attr;

if (dev->irq_managed && dev->irq > 0)
return 0;

irq = IO_APIC_get_PCI_irq_vector(dev->bus->number,
PCI_SLOT(dev->devfn),
pin - 1, &irq_attr);
Expand All @@ -1228,6 +1231,7 @@ static int pirq_enable_irq(struct pci_dev *dev)
}
dev = temp_dev;
if (irq >= 0) {
dev->irq_managed = 1;
dev->irq = irq;
dev_info(&dev->dev, "PCI->APIC IRQ transform: "
"INT %c -> IRQ %d\n", 'A' + pin - 1, irq);
Expand Down Expand Up @@ -1269,8 +1273,9 @@ bool mp_should_keep_irq(struct device *dev)
static void pirq_disable_irq(struct pci_dev *dev)
{
if (io_apic_assign_pci_irqs && !mp_should_keep_irq(&dev->dev) &&
dev->irq) {
dev->irq_managed && dev->irq) {
mp_unmap_irq(dev->irq);
dev->irq = 0;
dev->irq_managed = 0;
}
}
11 changes: 9 additions & 2 deletions drivers/acpi/pci_irq.c
Original file line number Diff line number Diff line change
Expand Up @@ -413,6 +413,9 @@ int acpi_pci_irq_enable(struct pci_dev *dev)
return 0;
}

if (dev->irq_managed && dev->irq > 0)
return 0;

entry = acpi_pci_irq_lookup(dev, pin);
if (!entry) {
/*
Expand Down Expand Up @@ -456,6 +459,7 @@ int acpi_pci_irq_enable(struct pci_dev *dev)
return rc;
}
dev->irq = rc;
dev->irq_managed = 1;

if (link)
snprintf(link_desc, sizeof(link_desc), " -> Link[%s]", link);
Expand All @@ -478,7 +482,7 @@ void acpi_pci_irq_disable(struct pci_dev *dev)
u8 pin;

pin = dev->pin;
if (!pin)
if (!pin || !dev->irq_managed || dev->irq <= 0)
return;

/* Keep IOAPIC pin configuration when suspending */
Expand Down Expand Up @@ -506,6 +510,9 @@ void acpi_pci_irq_disable(struct pci_dev *dev)
*/

dev_dbg(&dev->dev, "PCI INT %c disabled\n", pin_name(pin));
if (gsi >= 0 && dev->irq > 0)
if (gsi >= 0) {
acpi_unregister_gsi(gsi);
dev->irq = 0;
dev->irq_managed = 0;
}
}
1 change: 1 addition & 0 deletions include/linux/pci.h
Original file line number Diff line number Diff line change
Expand Up @@ -349,6 +349,7 @@ struct pci_dev {
unsigned int __aer_firmware_first:1;
unsigned int broken_intx_masking:1;
unsigned int io_window_1k:1; /* Intel P2P bridge 1K I/O windows */
unsigned int irq_managed:1;
pci_dev_flags_t dev_flags;
atomic_t enable_cnt; /* pci_enable_device has been called */

Expand Down

0 comments on commit cffe0a2

Please sign in to comment.