forked from mirrors/linux
		
	x86/PCI: Refine the way to release PCI IRQ resources
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. Commitc03b3b0738("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 commit3eec595235("x86, irq, PCI: Keep IRQ assignment for PCI devices during suspend/hibernation") and9eabc99a63("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 commitcffe0a2b5a("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>
This commit is contained in:
		
							parent
							
								
									593669c2ac
								
							
						
					
					
						commit
						b4b55cda58
					
				
					 5 changed files with 32 additions and 32 deletions
				
			
		|  | @ -93,8 +93,6 @@ 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); | ||||||
|  |  | ||||||
|  | @ -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) | int __init pcibios_init(void) | ||||||
| { | { | ||||||
| 	if (!raw_pci_ops) { | 	if (!raw_pci_ops) { | ||||||
|  | @ -509,6 +534,9 @@ 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; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -667,12 +695,6 @@ 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) | ||||||
| 
 | 
 | ||||||
| static void intel_mid_pci_irq_disable(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 && | 	if (dev->irq_managed && dev->irq > 0) { | ||||||
| 	    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,22 +1256,9 @@ 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 && !mp_should_keep_irq(&dev->dev) && | 	if (io_apic_assign_pci_irqs && dev->irq_managed && dev->irq) { | ||||||
| 	    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,14 +485,6 @@ 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; | ||||||
|  | @ -513,5 +505,6 @@ 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; | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
		Loading…
	
		Reference in a new issue
	
	 Jiang Liu
						Jiang Liu