aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJiang Liu <jiang.liu@linux.intel.com>2014-10-27 01:21:42 -0400
committerThomas Gleixner <tglx@linutronix.de>2014-12-16 08:08:15 -0500
commitcffe0a2b5a34c95a4dadc9ec7132690a5b0f6687 (patch)
tree136c802b57a9724e64510ae7531c7e366db479a8
parent67dc5e701fda884d49ed5c1904986bd5333610f4 (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.c10
-rw-r--r--arch/x86/pci/irq.c7
-rw-r--r--drivers/acpi/pci_irq.c11
-rw-r--r--include/linux/pci.h1
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
230static void intel_mid_pci_irq_disable(struct pci_dev *dev) 235static 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
236struct pci_ops intel_mid_pci_ops = { 244struct 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)
1269static void pirq_disable_irq(struct pci_dev *dev) 1273static 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