diff options
25 files changed, 209 insertions, 44 deletions
diff --git a/arch/powerpc/kernel/eeh_driver.c b/arch/powerpc/kernel/eeh_driver.c index 36bed5a12750..c17f90d0f73c 100644 --- a/arch/powerpc/kernel/eeh_driver.c +++ b/arch/powerpc/kernel/eeh_driver.c @@ -369,7 +369,9 @@ static void *eeh_rmv_device(void *data, void *userdata)  	edev->mode |= EEH_DEV_DISCONNECTED;  	(*removed)++; +	pci_lock_rescan_remove();  	pci_stop_and_remove_bus_device(dev); +	pci_unlock_rescan_remove();  	return NULL;  } @@ -416,10 +418,13 @@ static int eeh_reset_device(struct eeh_pe *pe, struct pci_bus *bus)  	 * into pcibios_add_pci_devices().  	 */  	eeh_pe_state_mark(pe, EEH_PE_KEEP); -	if (bus) +	if (bus) { +		pci_lock_rescan_remove();  		pcibios_remove_pci_devices(bus); -	else if (frozen_bus) +		pci_unlock_rescan_remove(); +	} else if (frozen_bus) {  		eeh_pe_dev_traverse(pe, eeh_rmv_device, &removed); +	}  	/* Reset the pci controller. (Asserts RST#; resets config space).  	 * Reconfigure bridges and devices. Don't try to bring the system @@ -429,6 +434,8 @@ static int eeh_reset_device(struct eeh_pe *pe, struct pci_bus *bus)  	if (rc)  		return rc; +	pci_lock_rescan_remove(); +  	/* Restore PE */  	eeh_ops->configure_bridge(pe);  	eeh_pe_restore_bars(pe); @@ -462,6 +469,7 @@ static int eeh_reset_device(struct eeh_pe *pe, struct pci_bus *bus)  	pe->tstamp = tstamp;  	pe->freeze_count = cnt; +	pci_unlock_rescan_remove();  	return 0;  } @@ -618,8 +626,11 @@ perm_error:  	eeh_pe_dev_traverse(pe, eeh_report_failure, NULL);  	/* Shut down the device drivers for good. */ -	if (frozen_bus) +	if (frozen_bus) { +		pci_lock_rescan_remove();  		pcibios_remove_pci_devices(frozen_bus); +		pci_unlock_rescan_remove(); +	}  }  static void eeh_handle_special_event(void) @@ -692,6 +703,7 @@ static void eeh_handle_special_event(void)  	if (rc == 2 || rc == 1)  		eeh_handle_normal_event(pe);  	else { +		pci_lock_rescan_remove();  		list_for_each_entry_safe(hose, tmp,  			&hose_list, list_node) {  			phb_pe = eeh_phb_pe_get(hose); @@ -703,6 +715,7 @@ static void eeh_handle_special_event(void)  			eeh_pe_dev_traverse(pe, eeh_report_failure, NULL);  			pcibios_remove_pci_devices(bus);  		} +		pci_unlock_rescan_remove();  	}  } diff --git a/drivers/acpi/pci_root.c b/drivers/acpi/pci_root.c index 0703bff5e60e..07ee02aa3c51 100644 --- a/drivers/acpi/pci_root.c +++ b/drivers/acpi/pci_root.c @@ -596,7 +596,9 @@ static int acpi_pci_root_add(struct acpi_device *device,  		pci_assign_unassigned_root_bus_resources(root->bus);  	} +	pci_lock_rescan_remove();  	pci_bus_add_devices(root->bus); +	pci_unlock_rescan_remove();  	return 1;  end: @@ -608,6 +610,8 @@ static void acpi_pci_root_remove(struct acpi_device *device)  {  	struct acpi_pci_root *root = acpi_driver_data(device); +	pci_lock_rescan_remove(); +  	pci_stop_root_bus(root->bus);  	device_set_run_wake(root->bus->bridge, false); @@ -615,6 +619,8 @@ static void acpi_pci_root_remove(struct acpi_device *device)  	pci_remove_root_bus(root->bus); +	pci_unlock_rescan_remove(); +  	kfree(root);  } diff --git a/drivers/message/fusion/mptbase.c b/drivers/message/fusion/mptbase.c index 767ff4d839f4..570b18a113ff 100644 --- a/drivers/message/fusion/mptbase.c +++ b/drivers/message/fusion/mptbase.c @@ -346,7 +346,7 @@ static int mpt_remove_dead_ioc_func(void *arg)  	if ((pdev == NULL))  		return -1; -	pci_stop_and_remove_bus_device(pdev); +	pci_stop_and_remove_bus_device_locked(pdev);  	return 0;  } diff --git a/drivers/pci/hotplug/acpiphp.h b/drivers/pci/hotplug/acpiphp.h index 1592dbe4f904..b6162be4df40 100644 --- a/drivers/pci/hotplug/acpiphp.h +++ b/drivers/pci/hotplug/acpiphp.h @@ -77,6 +77,8 @@ struct acpiphp_bridge {  	/* PCI-to-PCI bridge device */  	struct pci_dev *pci_dev; + +	bool is_going_away;  }; @@ -150,6 +152,7 @@ struct acpiphp_attention_info  /* slot flags */  #define SLOT_ENABLED		(0x00000001) +#define SLOT_IS_GOING_AWAY	(0x00000002)  /* function flags */ @@ -169,7 +172,7 @@ void acpiphp_unregister_hotplug_slot(struct acpiphp_slot *slot);  typedef int (*acpiphp_callback)(struct acpiphp_slot *slot, void *data);  int acpiphp_enable_slot(struct acpiphp_slot *slot); -int acpiphp_disable_and_eject_slot(struct acpiphp_slot *slot); +int acpiphp_disable_slot(struct acpiphp_slot *slot);  u8 acpiphp_get_power_status(struct acpiphp_slot *slot);  u8 acpiphp_get_attention_status(struct acpiphp_slot *slot);  u8 acpiphp_get_latch_status(struct acpiphp_slot *slot); diff --git a/drivers/pci/hotplug/acpiphp_core.c b/drivers/pci/hotplug/acpiphp_core.c index dca66bc44578..728c31f4c2c5 100644 --- a/drivers/pci/hotplug/acpiphp_core.c +++ b/drivers/pci/hotplug/acpiphp_core.c @@ -156,7 +156,7 @@ static int disable_slot(struct hotplug_slot *hotplug_slot)  	pr_debug("%s - physical_slot = %s\n", __func__, slot_name(slot));  	/* disable the specified slot */ -	return acpiphp_disable_and_eject_slot(slot->acpi_slot); +	return acpiphp_disable_slot(slot->acpi_slot);  } diff --git a/drivers/pci/hotplug/acpiphp_glue.c b/drivers/pci/hotplug/acpiphp_glue.c index 1cf605f67673..641ba6761bd7 100644 --- a/drivers/pci/hotplug/acpiphp_glue.c +++ b/drivers/pci/hotplug/acpiphp_glue.c @@ -430,6 +430,7 @@ static void cleanup_bridge(struct acpiphp_bridge *bridge)  					pr_err("failed to remove notify handler\n");  			}  		} +		slot->flags |= SLOT_IS_GOING_AWAY;  		if (slot->slot)  			acpiphp_unregister_hotplug_slot(slot);  	} @@ -437,6 +438,8 @@ static void cleanup_bridge(struct acpiphp_bridge *bridge)  	mutex_lock(&bridge_mutex);  	list_del(&bridge->list);  	mutex_unlock(&bridge_mutex); + +	bridge->is_going_away = true;  }  /** @@ -736,6 +739,10 @@ static void acpiphp_check_bridge(struct acpiphp_bridge *bridge)  {  	struct acpiphp_slot *slot; +	/* Bail out if the bridge is going away. */ +	if (bridge->is_going_away) +		return; +  	list_for_each_entry(slot, &bridge->slots, node) {  		struct pci_bus *bus = slot->bus;  		struct pci_dev *dev, *tmp; @@ -805,6 +812,8 @@ void acpiphp_check_host_bridge(acpi_handle handle)  	}  } +static int acpiphp_disable_and_eject_slot(struct acpiphp_slot *slot); +  static void hotplug_event(acpi_handle handle, u32 type, void *data)  {  	struct acpiphp_context *context = data; @@ -834,6 +843,9 @@ static void hotplug_event(acpi_handle handle, u32 type, void *data)  		} else {  			struct acpiphp_slot *slot = func->slot; +			if (slot->flags & SLOT_IS_GOING_AWAY) +				break; +  			mutex_lock(&slot->crit_sect);  			enable_slot(slot);  			mutex_unlock(&slot->crit_sect); @@ -849,6 +861,9 @@ static void hotplug_event(acpi_handle handle, u32 type, void *data)  			struct acpiphp_slot *slot = func->slot;  			int ret; +			if (slot->flags & SLOT_IS_GOING_AWAY) +				break; +  			/*  			 * Check if anything has changed in the slot and rescan  			 * from the parent if that's the case. @@ -878,9 +893,11 @@ static void hotplug_event_work(void *data, u32 type)  	acpi_handle handle = context->handle;  	acpi_scan_lock_acquire(); +	pci_lock_rescan_remove();  	hotplug_event(handle, type, context); +	pci_unlock_rescan_remove();  	acpi_scan_lock_release();  	acpi_evaluate_hotplug_ost(handle, type, ACPI_OST_SC_SUCCESS, NULL);  	put_bridge(context->func.parent); @@ -1048,12 +1065,19 @@ void acpiphp_remove_slots(struct pci_bus *bus)   */  int acpiphp_enable_slot(struct acpiphp_slot *slot)  { +	pci_lock_rescan_remove(); + +	if (slot->flags & SLOT_IS_GOING_AWAY) +		return -ENODEV; +  	mutex_lock(&slot->crit_sect);  	/* configure all functions */  	if (!(slot->flags & SLOT_ENABLED))  		enable_slot(slot);  	mutex_unlock(&slot->crit_sect); + +	pci_unlock_rescan_remove();  	return 0;  } @@ -1061,10 +1085,12 @@ int acpiphp_enable_slot(struct acpiphp_slot *slot)   * acpiphp_disable_and_eject_slot - power off and eject slot   * @slot: ACPI PHP slot   */ -int acpiphp_disable_and_eject_slot(struct acpiphp_slot *slot) +static int acpiphp_disable_and_eject_slot(struct acpiphp_slot *slot)  {  	struct acpiphp_func *func; -	int retval = 0; + +	if (slot->flags & SLOT_IS_GOING_AWAY) +		return -ENODEV;  	mutex_lock(&slot->crit_sect); @@ -1082,9 +1108,18 @@ int acpiphp_disable_and_eject_slot(struct acpiphp_slot *slot)  		}  	mutex_unlock(&slot->crit_sect); -	return retval; +	return 0;  } +int acpiphp_disable_slot(struct acpiphp_slot *slot) +{ +	int ret; + +	pci_lock_rescan_remove(); +	ret = acpiphp_disable_and_eject_slot(slot); +	pci_unlock_rescan_remove(); +	return ret; +}  /*   * slot enabled:  1 @@ -1095,7 +1130,6 @@ u8 acpiphp_get_power_status(struct acpiphp_slot *slot)  	return (slot->flags & SLOT_ENABLED);  } -  /*   * latch   open:  1   * latch closed:  0 @@ -1105,7 +1139,6 @@ u8 acpiphp_get_latch_status(struct acpiphp_slot *slot)  	return !(get_slot_status(slot) & ACPI_STA_DEVICE_UI);  } -  /*   * adapter presence : 1   *          absence : 0 diff --git a/drivers/pci/hotplug/cpci_hotplug_pci.c b/drivers/pci/hotplug/cpci_hotplug_pci.c index d3add9819f63..8c1464851768 100644 --- a/drivers/pci/hotplug/cpci_hotplug_pci.c +++ b/drivers/pci/hotplug/cpci_hotplug_pci.c @@ -254,9 +254,12 @@ int __ref cpci_configure_slot(struct slot *slot)  {  	struct pci_dev *dev;  	struct pci_bus *parent; +	int ret = 0;  	dbg("%s - enter", __func__); +	pci_lock_rescan_remove(); +  	if (slot->dev == NULL) {  		dbg("pci_dev null, finding %02x:%02x:%x",  		    slot->bus->number, PCI_SLOT(slot->devfn), PCI_FUNC(slot->devfn)); @@ -277,7 +280,8 @@ int __ref cpci_configure_slot(struct slot *slot)  		slot->dev = pci_get_slot(slot->bus, slot->devfn);  		if (slot->dev == NULL) {  			err("Could not find PCI device for slot %02x", slot->number); -			return -ENODEV; +			ret = -ENODEV; +			goto out;  		}  	}  	parent = slot->dev->bus; @@ -294,8 +298,10 @@ int __ref cpci_configure_slot(struct slot *slot)  	pci_bus_add_devices(parent); + out: +	pci_unlock_rescan_remove();  	dbg("%s - exit", __func__); -	return 0; +	return ret;  }  int cpci_unconfigure_slot(struct slot* slot) @@ -308,6 +314,8 @@ int cpci_unconfigure_slot(struct slot* slot)  		return -ENODEV;  	} +	pci_lock_rescan_remove(); +  	list_for_each_entry_safe(dev, temp, &slot->bus->devices, bus_list) {  		if (PCI_SLOT(dev->devfn) != PCI_SLOT(slot->devfn))  			continue; @@ -318,6 +326,8 @@ int cpci_unconfigure_slot(struct slot* slot)  	pci_dev_put(slot->dev);  	slot->dev = NULL; +	pci_unlock_rescan_remove(); +  	dbg("%s - exit", __func__);  	return 0;  } diff --git a/drivers/pci/hotplug/cpqphp_pci.c b/drivers/pci/hotplug/cpqphp_pci.c index 6e4a12c91adb..a3e3c2002b58 100644 --- a/drivers/pci/hotplug/cpqphp_pci.c +++ b/drivers/pci/hotplug/cpqphp_pci.c @@ -86,6 +86,8 @@ int cpqhp_configure_device (struct controller* ctrl, struct pci_func* func)  	struct pci_bus *child;  	int num; +	pci_lock_rescan_remove(); +  	if (func->pci_dev == NULL)  		func->pci_dev = pci_get_bus_and_slot(func->bus,PCI_DEVFN(func->device, func->function)); @@ -100,7 +102,7 @@ int cpqhp_configure_device (struct controller* ctrl, struct pci_func* func)  		func->pci_dev = pci_get_bus_and_slot(func->bus, PCI_DEVFN(func->device, func->function));  		if (func->pci_dev == NULL) {  			dbg("ERROR: pci_dev still null\n"); -			return 0; +			goto out;  		}  	} @@ -113,6 +115,8 @@ int cpqhp_configure_device (struct controller* ctrl, struct pci_func* func)  	pci_dev_put(func->pci_dev); + out: +	pci_unlock_rescan_remove();  	return 0;  } @@ -123,6 +127,7 @@ int cpqhp_unconfigure_device(struct pci_func* func)  	dbg("%s: bus/dev/func = %x/%x/%x\n", __func__, func->bus, func->device, func->function); +	pci_lock_rescan_remove();  	for (j=0; j<8 ; j++) {  		struct pci_dev* temp = pci_get_bus_and_slot(func->bus, PCI_DEVFN(func->device, j));  		if (temp) { @@ -130,6 +135,7 @@ int cpqhp_unconfigure_device(struct pci_func* func)  			pci_stop_and_remove_bus_device(temp);  		}  	} +	pci_unlock_rescan_remove();  	return 0;  } diff --git a/drivers/pci/hotplug/ibmphp_core.c b/drivers/pci/hotplug/ibmphp_core.c index efdc13adbe41..cf3ac1e4b099 100644 --- a/drivers/pci/hotplug/ibmphp_core.c +++ b/drivers/pci/hotplug/ibmphp_core.c @@ -718,6 +718,8 @@ static void ibm_unconfigure_device(struct pci_func *func)  					func->device, func->function);  	debug("func->device << 3 | 0x0  = %x\n", func->device << 3 | 0x0); +	pci_lock_rescan_remove(); +  	for (j = 0; j < 0x08; j++) {  		temp = pci_get_bus_and_slot(func->busno, (func->device << 3) | j);  		if (temp) { @@ -725,7 +727,10 @@ static void ibm_unconfigure_device(struct pci_func *func)  			pci_dev_put(temp);  		}  	} +  	pci_dev_put(func->dev); + +	pci_unlock_rescan_remove();  }  /* @@ -780,6 +785,8 @@ static int ibm_configure_device(struct pci_func *func)  	int flag = 0;	/* this is to make sure we don't double scan the bus,  					for bridged devices primarily */ +	pci_lock_rescan_remove(); +  	if (!(bus_structure_fixup(func->busno)))  		flag = 1;  	if (func->dev == NULL) @@ -789,7 +796,7 @@ static int ibm_configure_device(struct pci_func *func)  	if (func->dev == NULL) {  		struct pci_bus *bus = pci_find_bus(0, func->busno);  		if (!bus) -			return 0; +			goto out;  		num = pci_scan_slot(bus,  				PCI_DEVFN(func->device, func->function)); @@ -800,7 +807,7 @@ static int ibm_configure_device(struct pci_func *func)  				PCI_DEVFN(func->device, func->function));  		if (func->dev == NULL) {  			err("ERROR... : pci_dev still NULL\n"); -			return 0; +			goto out;  		}  	}  	if (!(flag) && (func->dev->hdr_type == PCI_HEADER_TYPE_BRIDGE)) { @@ -810,6 +817,8 @@ static int ibm_configure_device(struct pci_func *func)  			pci_bus_add_devices(child);  	} + out: +	pci_unlock_rescan_remove();  	return 0;  } diff --git a/drivers/pci/hotplug/pciehp_pci.c b/drivers/pci/hotplug/pciehp_pci.c index 198355112ee7..b07d7cc2d697 100644 --- a/drivers/pci/hotplug/pciehp_pci.c +++ b/drivers/pci/hotplug/pciehp_pci.c @@ -39,22 +39,26 @@ int pciehp_configure_device(struct slot *p_slot)  	struct pci_dev *dev;  	struct pci_dev *bridge = p_slot->ctrl->pcie->port;  	struct pci_bus *parent = bridge->subordinate; -	int num; +	int num, ret = 0;  	struct controller *ctrl = p_slot->ctrl; +	pci_lock_rescan_remove(); +  	dev = pci_get_slot(parent, PCI_DEVFN(0, 0));  	if (dev) {  		ctrl_err(ctrl, "Device %s already exists "  			 "at %04x:%02x:00, cannot hot-add\n", pci_name(dev),  			 pci_domain_nr(parent), parent->number);  		pci_dev_put(dev); -		return -EINVAL; +		ret = -EINVAL; +		goto out;  	}  	num = pci_scan_slot(parent, PCI_DEVFN(0, 0));  	if (num == 0) {  		ctrl_err(ctrl, "No new device found\n"); -		return -ENODEV; +		ret = -ENODEV; +		goto out;  	}  	list_for_each_entry(dev, &parent->devices, bus_list) @@ -73,7 +77,9 @@ int pciehp_configure_device(struct slot *p_slot)  	pci_bus_add_devices(parent); -	return 0; + out: +	pci_unlock_rescan_remove(); +	return ret;  }  int pciehp_unconfigure_device(struct slot *p_slot) @@ -90,6 +96,8 @@ int pciehp_unconfigure_device(struct slot *p_slot)  		 __func__, pci_domain_nr(parent), parent->number);  	pciehp_get_adapter_status(p_slot, &presence); +	pci_lock_rescan_remove(); +  	/*  	 * Stopping an SR-IOV PF device removes all the associated VFs,  	 * which will update the bus->devices list and confuse the @@ -124,5 +132,6 @@ int pciehp_unconfigure_device(struct slot *p_slot)  		pci_dev_put(dev);  	} +	pci_unlock_rescan_remove();  	return rc;  } diff --git a/drivers/pci/hotplug/rpadlpar_core.c b/drivers/pci/hotplug/rpadlpar_core.c index e9c044d15add..4fcdeedda31b 100644 --- a/drivers/pci/hotplug/rpadlpar_core.c +++ b/drivers/pci/hotplug/rpadlpar_core.c @@ -354,10 +354,15 @@ int dlpar_remove_pci_slot(char *drc_name, struct device_node *dn)  {  	struct pci_bus *bus;  	struct slot *slot; +	int ret = 0; + +	pci_lock_rescan_remove();  	bus = pcibios_find_pci_bus(dn); -	if (!bus) -		return -EINVAL; +	if (!bus) { +		ret = -EINVAL; +		goto out; +	}  	pr_debug("PCI: Removing PCI slot below EADS bridge %s\n",  		 bus->self ? pci_name(bus->self) : "<!PHB!>"); @@ -371,7 +376,8 @@ int dlpar_remove_pci_slot(char *drc_name, struct device_node *dn)  			printk(KERN_ERR  				"%s: unable to remove hotplug slot %s\n",  				__func__, drc_name); -			return -EIO; +			ret = -EIO; +			goto out;  		}  	} @@ -382,7 +388,8 @@ int dlpar_remove_pci_slot(char *drc_name, struct device_node *dn)  	if (pcibios_unmap_io_space(bus)) {  		printk(KERN_ERR "%s: failed to unmap bus range\n",  			__func__); -		return -ERANGE; +		ret = -ERANGE; +		goto out;  	}  	/* Remove the EADS bridge device itself */ @@ -390,7 +397,9 @@ int dlpar_remove_pci_slot(char *drc_name, struct device_node *dn)  	pr_debug("PCI: Now removing bridge device %s\n", pci_name(bus->self));  	pci_stop_and_remove_bus_device(bus->self); -	return 0; + out: +	pci_unlock_rescan_remove(); +	return ret;  }  /** diff --git a/drivers/pci/hotplug/rpaphp_core.c b/drivers/pci/hotplug/rpaphp_core.c index b7fc5c9255a5..4796c15fba94 100644 --- a/drivers/pci/hotplug/rpaphp_core.c +++ b/drivers/pci/hotplug/rpaphp_core.c @@ -398,7 +398,9 @@ static int enable_slot(struct hotplug_slot *hotplug_slot)  		return retval;  	if (state == PRESENT) { +		pci_lock_rescan_remove();  		pcibios_add_pci_devices(slot->bus); +		pci_unlock_rescan_remove();  		slot->state = CONFIGURED;  	} else if (state == EMPTY) {  		slot->state = EMPTY; @@ -418,7 +420,9 @@ static int disable_slot(struct hotplug_slot *hotplug_slot)  	if (slot->state == NOT_CONFIGURED)  		return -EINVAL; +	pci_lock_rescan_remove();  	pcibios_remove_pci_devices(slot->bus); +	pci_unlock_rescan_remove();  	vm_unmap_aliases();  	slot->state = NOT_CONFIGURED; diff --git a/drivers/pci/hotplug/s390_pci_hpc.c b/drivers/pci/hotplug/s390_pci_hpc.c index 3c7eb5dd91c6..8d2ce22151eb 100644 --- a/drivers/pci/hotplug/s390_pci_hpc.c +++ b/drivers/pci/hotplug/s390_pci_hpc.c @@ -80,7 +80,9 @@ static int enable_slot(struct hotplug_slot *hotplug_slot)  		goto out_deconfigure;  	pci_scan_slot(slot->zdev->bus, ZPCI_DEVFN); +	pci_lock_rescan_remove();  	pci_bus_add_devices(slot->zdev->bus); +	pci_unlock_rescan_remove();  	return rc; @@ -98,7 +100,7 @@ static int disable_slot(struct hotplug_slot *hotplug_slot)  		return -EIO;  	if (slot->zdev->pdev) -		pci_stop_and_remove_bus_device(slot->zdev->pdev); +		pci_stop_and_remove_bus_device_locked(slot->zdev->pdev);  	rc = zpci_disable_device(slot->zdev);  	if (rc) diff --git a/drivers/pci/hotplug/sgi_hotplug.c b/drivers/pci/hotplug/sgi_hotplug.c index 5b05a68cca6c..613043f7576f 100644 --- a/drivers/pci/hotplug/sgi_hotplug.c +++ b/drivers/pci/hotplug/sgi_hotplug.c @@ -459,12 +459,15 @@ static int enable_slot(struct hotplug_slot *bss_hotplug_slot)  		acpi_scan_lock_release();  	} +	pci_lock_rescan_remove(); +  	/* Call the driver for the new device */  	pci_bus_add_devices(slot->pci_bus);  	/* Call the drivers for the new devices subordinate to PPB */  	if (new_ppb)  		pci_bus_add_devices(new_bus); +	pci_unlock_rescan_remove();  	mutex_unlock(&sn_hotplug_mutex);  	if (rc == 0) @@ -540,6 +543,7 @@ static int disable_slot(struct hotplug_slot *bss_hotplug_slot)  		acpi_scan_lock_release();  	} +	pci_lock_rescan_remove();  	/* Free the SN resources assigned to the Linux device.*/  	list_for_each_entry_safe(dev, temp, &slot->pci_bus->devices, bus_list) {  		if (PCI_SLOT(dev->devfn) != slot->device_num + 1) @@ -550,6 +554,7 @@ static int disable_slot(struct hotplug_slot *bss_hotplug_slot)  		pci_stop_and_remove_bus_device(dev);  		pci_dev_put(dev);  	} +	pci_unlock_rescan_remove();  	/* Remove the SSDT for the slot from the ACPI namespace */  	if (SN_ACPI_BASE_SUPPORT() && ssdt_id) { diff --git a/drivers/pci/hotplug/shpchp_pci.c b/drivers/pci/hotplug/shpchp_pci.c index b0e83132542e..2bf69fe1926c 100644 --- a/drivers/pci/hotplug/shpchp_pci.c +++ b/drivers/pci/hotplug/shpchp_pci.c @@ -40,7 +40,9 @@ int __ref shpchp_configure_device(struct slot *p_slot)  	struct controller *ctrl = p_slot->ctrl;  	struct pci_dev *bridge = ctrl->pci_dev;  	struct pci_bus *parent = bridge->subordinate; -	int num; +	int num, ret = 0; + +	pci_lock_rescan_remove();  	dev = pci_get_slot(parent, PCI_DEVFN(p_slot->device, 0));  	if (dev) { @@ -48,13 +50,15 @@ int __ref shpchp_configure_device(struct slot *p_slot)  			 "at %04x:%02x:%02x, cannot hot-add\n", pci_name(dev),  			 pci_domain_nr(parent), p_slot->bus, p_slot->device);  		pci_dev_put(dev); -		return -EINVAL; +		ret = -EINVAL; +		goto out;  	}  	num = pci_scan_slot(parent, PCI_DEVFN(p_slot->device, 0));  	if (num == 0) {  		ctrl_err(ctrl, "No new device found\n"); -		return -ENODEV; +		ret = -ENODEV; +		goto out;  	}  	list_for_each_entry(dev, &parent->devices, bus_list) { @@ -75,7 +79,9 @@ int __ref shpchp_configure_device(struct slot *p_slot)  	pci_bus_add_devices(parent); -	return 0; + out: +	pci_unlock_rescan_remove(); +	return ret;  }  int shpchp_unconfigure_device(struct slot *p_slot) @@ -89,6 +95,8 @@ int shpchp_unconfigure_device(struct slot *p_slot)  	ctrl_dbg(ctrl, "%s: domain:bus:dev = %04x:%02x:%02x\n",  		 __func__, pci_domain_nr(parent), p_slot->bus, p_slot->device); +	pci_lock_rescan_remove(); +  	list_for_each_entry_safe(dev, temp, &parent->devices, bus_list) {  		if (PCI_SLOT(dev->devfn) != p_slot->device)  			continue; @@ -108,6 +116,8 @@ int shpchp_unconfigure_device(struct slot *p_slot)  		pci_stop_and_remove_bus_device(dev);  		pci_dev_put(dev);  	} + +	pci_unlock_rescan_remove();  	return rc;  } diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c index c91e6c18debc..276ef9c18802 100644 --- a/drivers/pci/pci-sysfs.c +++ b/drivers/pci/pci-sysfs.c @@ -297,7 +297,6 @@ msi_bus_store(struct device *dev, struct device_attribute *attr,  }  static DEVICE_ATTR_RW(msi_bus); -static DEFINE_MUTEX(pci_remove_rescan_mutex);  static ssize_t bus_rescan_store(struct bus_type *bus, const char *buf,  				size_t count)  { @@ -308,10 +307,10 @@ static ssize_t bus_rescan_store(struct bus_type *bus, const char *buf,  		return -EINVAL;  	if (val) { -		mutex_lock(&pci_remove_rescan_mutex); +		pci_lock_rescan_remove();  		while ((b = pci_find_next_bus(b)) != NULL)  			pci_rescan_bus(b); -		mutex_unlock(&pci_remove_rescan_mutex); +		pci_unlock_rescan_remove();  	}  	return count;  } @@ -342,9 +341,9 @@ dev_rescan_store(struct device *dev, struct device_attribute *attr,  		return -EINVAL;  	if (val) { -		mutex_lock(&pci_remove_rescan_mutex); +		pci_lock_rescan_remove();  		pci_rescan_bus(pdev->bus); -		mutex_unlock(&pci_remove_rescan_mutex); +		pci_unlock_rescan_remove();  	}  	return count;  } @@ -354,11 +353,7 @@ static struct device_attribute dev_rescan_attr = __ATTR(rescan,  static void remove_callback(struct device *dev)  { -	struct pci_dev *pdev = to_pci_dev(dev); - -	mutex_lock(&pci_remove_rescan_mutex); -	pci_stop_and_remove_bus_device(pdev); -	mutex_unlock(&pci_remove_rescan_mutex); +	pci_stop_and_remove_bus_device_locked(to_pci_dev(dev));  }  static ssize_t @@ -395,12 +390,12 @@ dev_bus_rescan_store(struct device *dev, struct device_attribute *attr,  		return -EINVAL;  	if (val) { -		mutex_lock(&pci_remove_rescan_mutex); +		pci_lock_rescan_remove();  		if (!pci_is_root_bus(bus) && list_empty(&bus->devices))  			pci_rescan_bus_bridge_resize(bus->self);  		else  			pci_rescan_bus(bus); -		mutex_unlock(&pci_remove_rescan_mutex); +		pci_unlock_rescan_remove();  	}  	return count;  } diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index c86c8638d3c4..04796c056d12 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -2024,6 +2024,24 @@ EXPORT_SYMBOL(pci_scan_slot);  EXPORT_SYMBOL(pci_scan_bridge);  EXPORT_SYMBOL_GPL(pci_scan_child_bus); +/* + * pci_rescan_bus(), pci_rescan_bus_bridge_resize() and PCI device removal + * routines should always be executed under this mutex. + */ +static DEFINE_MUTEX(pci_rescan_remove_lock); + +void pci_lock_rescan_remove(void) +{ +	mutex_lock(&pci_rescan_remove_lock); +} +EXPORT_SYMBOL_GPL(pci_lock_rescan_remove); + +void pci_unlock_rescan_remove(void) +{ +	mutex_unlock(&pci_rescan_remove_lock); +} +EXPORT_SYMBOL_GPL(pci_unlock_rescan_remove); +  static int __init pci_sort_bf_cmp(const struct device *d_a, const struct device *d_b)  {  	const struct pci_dev *a = to_pci_dev(d_a); diff --git a/drivers/pci/remove.c b/drivers/pci/remove.c index f452148e6d55..4ff36bfa785e 100644 --- a/drivers/pci/remove.c +++ b/drivers/pci/remove.c @@ -20,6 +20,9 @@ static void pci_stop_dev(struct pci_dev *dev)  static void pci_destroy_dev(struct pci_dev *dev)  { +	if (!dev->dev.kobj.parent) +		return; +  	device_del(&dev->dev);  	put_device(&dev->dev); @@ -95,6 +98,14 @@ void pci_stop_and_remove_bus_device(struct pci_dev *dev)  }  EXPORT_SYMBOL(pci_stop_and_remove_bus_device); +void pci_stop_and_remove_bus_device_locked(struct pci_dev *dev) +{ +	pci_lock_rescan_remove(); +	pci_stop_and_remove_bus_device(dev); +	pci_unlock_rescan_remove(); +} +EXPORT_SYMBOL_GPL(pci_stop_and_remove_bus_device_locked); +  void pci_stop_root_bus(struct pci_bus *bus)  {  	struct pci_dev *child, *tmp; diff --git a/drivers/pci/xen-pcifront.c b/drivers/pci/xen-pcifront.c index f7197a790341..d1cd60f51f87 100644 --- a/drivers/pci/xen-pcifront.c +++ b/drivers/pci/xen-pcifront.c @@ -471,12 +471,15 @@ static int pcifront_scan_root(struct pcifront_device *pdev,  	}  	pcifront_init_sd(sd, domain, bus, pdev); +	pci_lock_rescan_remove(); +  	b = pci_scan_bus_parented(&pdev->xdev->dev, bus,  				  &pcifront_bus_ops, sd);  	if (!b) {  		dev_err(&pdev->xdev->dev,  			"Error creating PCI Frontend Bus!\n");  		err = -ENOMEM; +		pci_unlock_rescan_remove();  		goto err_out;  	} @@ -494,6 +497,7 @@ static int pcifront_scan_root(struct pcifront_device *pdev,  	/* Create SysFS and notify udev of the devices. Aka: "going live" */  	pci_bus_add_devices(b); +	pci_unlock_rescan_remove();  	return err;  err_out: @@ -556,6 +560,7 @@ static void pcifront_free_roots(struct pcifront_device *pdev)  	dev_dbg(&pdev->xdev->dev, "cleaning up root buses\n"); +	pci_lock_rescan_remove();  	list_for_each_entry_safe(bus_entry, t, &pdev->root_buses, list) {  		list_del(&bus_entry->list); @@ -568,6 +573,7 @@ static void pcifront_free_roots(struct pcifront_device *pdev)  		kfree(bus_entry);  	} +	pci_unlock_rescan_remove();  }  static pci_ers_result_t pcifront_common_process(int cmd, @@ -1043,8 +1049,10 @@ static int pcifront_detach_devices(struct pcifront_device *pdev)  				domain, bus, slot, func);  			continue;  		} +		pci_lock_rescan_remove();  		pci_stop_and_remove_bus_device(pci_dev);  		pci_dev_put(pci_dev); +		pci_unlock_rescan_remove();  		dev_dbg(&pdev->xdev->dev,  			"PCI device %04x:%02x:%02x.%d removed.\n", diff --git a/drivers/pcmcia/cardbus.c b/drivers/pcmcia/cardbus.c index b2a98cdbd0d2..8bde61952d20 100644 --- a/drivers/pcmcia/cardbus.c +++ b/drivers/pcmcia/cardbus.c @@ -70,6 +70,8 @@ int __ref cb_alloc(struct pcmcia_socket *s)  	struct pci_dev *dev;  	unsigned int max, pass; +	pci_lock_rescan_remove(); +  	s->functions = pci_scan_slot(bus, PCI_DEVFN(0, 0));  	pci_fixup_cardbus(bus); @@ -93,6 +95,7 @@ int __ref cb_alloc(struct pcmcia_socket *s)  	pci_bus_add_devices(bus); +	pci_unlock_rescan_remove();  	return 0;  } @@ -115,6 +118,10 @@ void cb_free(struct pcmcia_socket *s)  	if (!bus)  		return; +	pci_lock_rescan_remove(); +  	list_for_each_entry_safe(dev, tmp, &bus->devices, bus_list)  		pci_stop_and_remove_bus_device(dev); + +	pci_unlock_rescan_remove();  } diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c index 19c313b056c3..6fe268f6af91 100644 --- a/drivers/platform/x86/asus-wmi.c +++ b/drivers/platform/x86/asus-wmi.c @@ -606,6 +606,7 @@ static void asus_rfkill_hotplug(struct asus_wmi *asus)  	mutex_unlock(&asus->wmi_lock);  	mutex_lock(&asus->hotplug_lock); +	pci_lock_rescan_remove();  	if (asus->wlan.rfkill)  		rfkill_set_sw_state(asus->wlan.rfkill, blocked); @@ -656,6 +657,7 @@ static void asus_rfkill_hotplug(struct asus_wmi *asus)  	}  out_unlock: +	pci_unlock_rescan_remove();  	mutex_unlock(&asus->hotplug_lock);  } diff --git a/drivers/platform/x86/eeepc-laptop.c b/drivers/platform/x86/eeepc-laptop.c index aefcc32e5634..538521b00948 100644 --- a/drivers/platform/x86/eeepc-laptop.c +++ b/drivers/platform/x86/eeepc-laptop.c @@ -592,6 +592,7 @@ static void eeepc_rfkill_hotplug(struct eeepc_laptop *eeepc, acpi_handle handle)  		rfkill_set_sw_state(eeepc->wlan_rfkill, blocked);  	mutex_lock(&eeepc->hotplug_lock); +	pci_lock_rescan_remove();  	if (eeepc->hotplug_slot) {  		port = acpi_get_pci_dev(handle); @@ -649,6 +650,7 @@ out_put_dev:  	}  out_unlock: +	pci_unlock_rescan_remove();  	mutex_unlock(&eeepc->hotplug_lock);  } diff --git a/drivers/scsi/mpt2sas/mpt2sas_base.c b/drivers/scsi/mpt2sas/mpt2sas_base.c index 3901edc35812..bde63f7452bd 100644 --- a/drivers/scsi/mpt2sas/mpt2sas_base.c +++ b/drivers/scsi/mpt2sas/mpt2sas_base.c @@ -128,7 +128,7 @@ static int mpt2sas_remove_dead_ioc_func(void *arg)  		pdev = ioc->pdev;  		if ((pdev == NULL))  			return -1; -		pci_stop_and_remove_bus_device(pdev); +		pci_stop_and_remove_bus_device_locked(pdev);  		return 0;  } diff --git a/drivers/scsi/mpt3sas/mpt3sas_base.c b/drivers/scsi/mpt3sas/mpt3sas_base.c index fa785062e97b..0cf4f7000f94 100644 --- a/drivers/scsi/mpt3sas/mpt3sas_base.c +++ b/drivers/scsi/mpt3sas/mpt3sas_base.c @@ -131,7 +131,7 @@ static int mpt3sas_remove_dead_ioc_func(void *arg)  	pdev = ioc->pdev;  	if ((pdev == NULL))  		return -1; -	pci_stop_and_remove_bus_device(pdev); +	pci_stop_and_remove_bus_device_locked(pdev);  	return 0;  } diff --git a/include/linux/pci.h b/include/linux/pci.h index 9e3ec8b951b7..2087e6b35545 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -775,6 +775,7 @@ struct pci_dev *pci_dev_get(struct pci_dev *dev);  void pci_dev_put(struct pci_dev *dev);  void pci_remove_bus(struct pci_bus *b);  void pci_stop_and_remove_bus_device(struct pci_dev *dev); +void pci_stop_and_remove_bus_device_locked(struct pci_dev *dev);  void pci_stop_root_bus(struct pci_bus *bus);  void pci_remove_root_bus(struct pci_bus *bus);  void pci_setup_cardbus(struct pci_bus *bus); @@ -1011,6 +1012,8 @@ void set_pcie_hotplug_bridge(struct pci_dev *pdev);  int pci_bus_find_capability(struct pci_bus *bus, unsigned int devfn, int cap);  unsigned int pci_rescan_bus_bridge_resize(struct pci_dev *bridge);  unsigned int pci_rescan_bus(struct pci_bus *bus); +void pci_lock_rescan_remove(void); +void pci_unlock_rescan_remove(void);  /* Vital product data routines */  ssize_t pci_read_vpd(struct pci_dev *dev, loff_t pos, size_t count, void *buf);  | 
