diff options
author | Jiang Liu <jiang.liu@linux.intel.com> | 2014-10-27 01:21:42 -0400 |
---|---|---|
committer | Thomas Gleixner <tglx@linutronix.de> | 2014-12-16 08:08:15 -0500 |
commit | cffe0a2b5a34c95a4dadc9ec7132690a5b0f6687 (patch) | |
tree | 136c802b57a9724e64510ae7531c7e366db479a8 | |
parent | 67dc5e701fda884d49ed5c1904986bd5333610f4 (diff) |
x86, irq: Keep balance of IOAPIC pin reference count
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>
-rw-r--r-- | arch/x86/pci/intel_mid_pci.c | 10 | ||||
-rw-r--r-- | arch/x86/pci/irq.c | 7 | ||||
-rw-r--r-- | drivers/acpi/pci_irq.c | 11 | ||||
-rw-r--r-- | include/linux/pci.h | 1 |
4 files changed, 25 insertions, 4 deletions
diff --git a/arch/x86/pci/intel_mid_pci.c b/arch/x86/pci/intel_mid_pci.c index b9958c364075..44b9271580b5 100644 --- a/arch/x86/pci/intel_mid_pci.c +++ b/arch/x86/pci/intel_mid_pci.c | |||
@@ -210,6 +210,9 @@ static int intel_mid_pci_irq_enable(struct pci_dev *dev) | |||
210 | { | 210 | { |
211 | int polarity; | 211 | int polarity; |
212 | 212 | ||
213 | if (dev->irq_managed && dev->irq > 0) | ||
214 | return 0; | ||
215 | |||
213 | if (intel_mid_identify_cpu() == INTEL_MID_CPU_CHIP_TANGIER) | 216 | if (intel_mid_identify_cpu() == INTEL_MID_CPU_CHIP_TANGIER) |
214 | polarity = 0; /* active high */ | 217 | polarity = 0; /* active high */ |
215 | else | 218 | else |
@@ -224,13 +227,18 @@ static int intel_mid_pci_irq_enable(struct pci_dev *dev) | |||
224 | if (mp_map_gsi_to_irq(dev->irq, IOAPIC_MAP_ALLOC) < 0) | 227 | if (mp_map_gsi_to_irq(dev->irq, IOAPIC_MAP_ALLOC) < 0) |
225 | return -EBUSY; | 228 | return -EBUSY; |
226 | 229 | ||
230 | dev->irq_managed = 1; | ||
231 | |||
227 | return 0; | 232 | return 0; |
228 | } | 233 | } |
229 | 234 | ||
230 | static void intel_mid_pci_irq_disable(struct pci_dev *dev) | 235 | static void intel_mid_pci_irq_disable(struct pci_dev *dev) |
231 | { | 236 | { |
232 | if (!mp_should_keep_irq(&dev->dev) && dev->irq > 0) | 237 | if (!mp_should_keep_irq(&dev->dev) && dev->irq_managed && |
238 | dev->irq > 0) { | ||
233 | mp_unmap_irq(dev->irq); | 239 | mp_unmap_irq(dev->irq); |
240 | dev->irq_managed = 0; | ||
241 | } | ||
234 | } | 242 | } |
235 | 243 | ||
236 | struct pci_ops intel_mid_pci_ops = { | 244 | struct pci_ops intel_mid_pci_ops = { |
diff --git a/arch/x86/pci/irq.c b/arch/x86/pci/irq.c index cb50e281d838..99884588a47a 100644 --- a/arch/x86/pci/irq.c +++ b/arch/x86/pci/irq.c | |||
@@ -1202,6 +1202,9 @@ static int pirq_enable_irq(struct pci_dev *dev) | |||
1202 | int irq; | 1202 | int irq; |
1203 | struct io_apic_irq_attr irq_attr; | 1203 | struct io_apic_irq_attr irq_attr; |
1204 | 1204 | ||
1205 | if (dev->irq_managed && dev->irq > 0) | ||
1206 | return 0; | ||
1207 | |||
1205 | irq = IO_APIC_get_PCI_irq_vector(dev->bus->number, | 1208 | irq = IO_APIC_get_PCI_irq_vector(dev->bus->number, |
1206 | PCI_SLOT(dev->devfn), | 1209 | PCI_SLOT(dev->devfn), |
1207 | pin - 1, &irq_attr); | 1210 | pin - 1, &irq_attr); |
@@ -1228,6 +1231,7 @@ static int pirq_enable_irq(struct pci_dev *dev) | |||
1228 | } | 1231 | } |
1229 | dev = temp_dev; | 1232 | dev = temp_dev; |
1230 | if (irq >= 0) { | 1233 | if (irq >= 0) { |
1234 | dev->irq_managed = 1; | ||
1231 | dev->irq = irq; | 1235 | dev->irq = irq; |
1232 | dev_info(&dev->dev, "PCI->APIC IRQ transform: " | 1236 | dev_info(&dev->dev, "PCI->APIC IRQ transform: " |
1233 | "INT %c -> IRQ %d\n", 'A' + pin - 1, irq); | 1237 | "INT %c -> IRQ %d\n", 'A' + pin - 1, irq); |
@@ -1269,8 +1273,9 @@ bool mp_should_keep_irq(struct device *dev) | |||
1269 | static void pirq_disable_irq(struct pci_dev *dev) | 1273 | static void pirq_disable_irq(struct pci_dev *dev) |
1270 | { | 1274 | { |
1271 | if (io_apic_assign_pci_irqs && !mp_should_keep_irq(&dev->dev) && | 1275 | if (io_apic_assign_pci_irqs && !mp_should_keep_irq(&dev->dev) && |
1272 | dev->irq) { | 1276 | dev->irq_managed && dev->irq) { |
1273 | mp_unmap_irq(dev->irq); | 1277 | mp_unmap_irq(dev->irq); |
1274 | dev->irq = 0; | 1278 | dev->irq = 0; |
1279 | dev->irq_managed = 0; | ||
1275 | } | 1280 | } |
1276 | } | 1281 | } |
diff --git a/drivers/acpi/pci_irq.c b/drivers/acpi/pci_irq.c index 6e6b80eb0bba..5f1fdca65e5f 100644 --- a/drivers/acpi/pci_irq.c +++ b/drivers/acpi/pci_irq.c | |||
@@ -413,6 +413,9 @@ int acpi_pci_irq_enable(struct pci_dev *dev) | |||
413 | return 0; | 413 | return 0; |
414 | } | 414 | } |
415 | 415 | ||
416 | if (dev->irq_managed && dev->irq > 0) | ||
417 | return 0; | ||
418 | |||
416 | entry = acpi_pci_irq_lookup(dev, pin); | 419 | entry = acpi_pci_irq_lookup(dev, pin); |
417 | if (!entry) { | 420 | if (!entry) { |
418 | /* | 421 | /* |
@@ -456,6 +459,7 @@ int acpi_pci_irq_enable(struct pci_dev *dev) | |||
456 | return rc; | 459 | return rc; |
457 | } | 460 | } |
458 | dev->irq = rc; | 461 | dev->irq = rc; |
462 | dev->irq_managed = 1; | ||
459 | 463 | ||
460 | if (link) | 464 | if (link) |
461 | snprintf(link_desc, sizeof(link_desc), " -> Link[%s]", link); | 465 | snprintf(link_desc, sizeof(link_desc), " -> Link[%s]", link); |
@@ -478,7 +482,7 @@ void acpi_pci_irq_disable(struct pci_dev *dev) | |||
478 | u8 pin; | 482 | u8 pin; |
479 | 483 | ||
480 | pin = dev->pin; | 484 | pin = dev->pin; |
481 | if (!pin) | 485 | if (!pin || !dev->irq_managed || dev->irq <= 0) |
482 | return; | 486 | return; |
483 | 487 | ||
484 | /* Keep IOAPIC pin configuration when suspending */ | 488 | /* Keep IOAPIC pin configuration when suspending */ |
@@ -506,6 +510,9 @@ void acpi_pci_irq_disable(struct pci_dev *dev) | |||
506 | */ | 510 | */ |
507 | 511 | ||
508 | dev_dbg(&dev->dev, "PCI INT %c disabled\n", pin_name(pin)); | 512 | dev_dbg(&dev->dev, "PCI INT %c disabled\n", pin_name(pin)); |
509 | if (gsi >= 0 && dev->irq > 0) | 513 | if (gsi >= 0) { |
510 | acpi_unregister_gsi(gsi); | 514 | acpi_unregister_gsi(gsi); |
515 | dev->irq = 0; | ||
516 | dev->irq_managed = 0; | ||
517 | } | ||
511 | } | 518 | } |
diff --git a/include/linux/pci.h b/include/linux/pci.h index a523cee3abb5..c888c5e4e225 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h | |||
@@ -349,6 +349,7 @@ struct pci_dev { | |||
349 | unsigned int __aer_firmware_first:1; | 349 | unsigned int __aer_firmware_first:1; |
350 | unsigned int broken_intx_masking:1; | 350 | unsigned int broken_intx_masking:1; |
351 | unsigned int io_window_1k:1; /* Intel P2P bridge 1K I/O windows */ | 351 | unsigned int io_window_1k:1; /* Intel P2P bridge 1K I/O windows */ |
352 | unsigned int irq_managed:1; | ||
352 | pci_dev_flags_t dev_flags; | 353 | pci_dev_flags_t dev_flags; |
353 | atomic_t enable_cnt; /* pci_enable_device has been called */ | 354 | atomic_t enable_cnt; /* pci_enable_device has been called */ |
354 | 355 | ||