Commit 9e8ce4b9 authored by Rafael J. Wysocki's avatar Rafael J. Wysocki

Revert "x86/PCI: Refine the way to release PCI IRQ resources"

Commit b4b55cda (Refine the way to release PCI IRQ resources)
introduced a regression in the PCI IRQ resource management by causing
the IRQ resource of a device, established when pci_enabled_device()
is called on a fully disabled device, to be released when the driver
is unbound from the device, regardless of the enable_cnt.

This leads to the situation that an ill-behaved driver can now make a
device unusable to subsequent drivers by an imbalance in their use of
pci_enable/disable_device().  That is a serious problem for secondary
drivers like vfio-pci, which are innocent of the transgressions of
the previous driver.

Since the solution of this problem is not immediate and requires
further discussion, revert commit b4b55cda and the issue it was
supposed to address (a bug related to xen-pciback) will be taken
care of in a different way going forward.
Reported-by: default avatarAlex Williamson <alex.williamson@redhat.com>
Signed-off-by: default avatarRafael J. Wysocki <rafael.j.wysocki@intel.com>
parent 06e5801b
...@@ -93,6 +93,8 @@ extern raw_spinlock_t pci_config_lock; ...@@ -93,6 +93,8 @@ 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);
......
...@@ -513,31 +513,6 @@ void __init pcibios_set_cache_line_size(void) ...@@ -513,31 +513,6 @@ 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) {
...@@ -550,9 +525,6 @@ int __init pcibios_init(void) ...@@ -550,9 +525,6 @@ 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;
} }
...@@ -711,6 +683,12 @@ int pcibios_enable_device(struct pci_dev *dev, int mask) ...@@ -711,6 +683,12 @@ 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) ...@@ -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 (dev->irq_managed && dev->irq > 0) { if (!mp_should_keep_irq(&dev->dev) && dev->irq_managed &&
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,9 +1256,22 @@ static int pirq_enable_irq(struct pci_dev *dev) ...@@ -1256,9 +1256,22 @@ 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 && dev->irq_managed && dev->irq) { if (io_apic_assign_pci_irqs && !mp_should_keep_irq(&dev->dev) &&
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,6 +485,14 @@ void acpi_pci_irq_disable(struct pci_dev *dev) ...@@ -485,6 +485,14 @@ 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;
...@@ -505,6 +513,5 @@ void acpi_pci_irq_disable(struct pci_dev *dev) ...@@ -505,6 +513,5 @@ 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;
} }
} }
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment