forked from mirrors/linux
		
	PCI: acpiphp: Do not use ACPI PCI subdriver mechanism
Previously the acpiphp driver registered itself as an ACPI PCI subdriver,
so its callbacks were invoked when creating/destroying PCI root
buses to manage ACPI-based PCI hotplug slots.  But it doesn't handle
P2P bridge hotplug events, so it will cause strange behaviour if there
are hotplug slots associated with a hot-removed P2P bridge.
This patch fixes this issue by:
1) Directly hooking into PCI core to update hotplug slot devices when
   creating/destroying PCI buses through:
	pci_{add|remove}_bus() -> acpi_pci_{add|remove}_bus()
2) Getting rid of unused ACPI PCI subdriver-related code
It also cleans up unused code in the acpiphp driver.
[bhelgaas: keep acpi_pci_add_bus() stub for CONFIG_ACPI=n]
Signed-off-by: Jiang Liu <jiang.liu@huawei.com>
Signed-off-by: Yijing Wang <wangyijing@huawei.com>
Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
Reviewed-by: Yinghai Lu <yinghai@kernel.org>
Cc: "Rafael J. Wysocki" <rafael.j.wysocki@intel.com>
Cc: Toshi Kani <toshi.kani@hp.com>
			
			
This commit is contained in:
		
							parent
							
								
									6037a803b0
								
							
						
					
					
						commit
						3b63aaa70e
					
				
					 5 changed files with 71 additions and 247 deletions
				
			
		|  | @ -119,7 +119,6 @@ struct acpiphp_slot { | |||
|  */ | ||||
| struct acpiphp_func { | ||||
| 	struct acpiphp_slot *slot;	/* parent */ | ||||
| 	struct acpiphp_bridge *bridge;	/* Ejectable PCI-to-PCI bridge */ | ||||
| 
 | ||||
| 	struct list_head sibling; | ||||
| 	struct notifier_block nb; | ||||
|  | @ -176,8 +175,6 @@ extern int acpiphp_register_hotplug_slot(struct acpiphp_slot *slot); | |||
| extern void acpiphp_unregister_hotplug_slot(struct acpiphp_slot *slot); | ||||
| 
 | ||||
| /* acpiphp_glue.c */ | ||||
| extern int acpiphp_glue_init (void); | ||||
| extern void acpiphp_glue_exit (void); | ||||
| typedef int (*acpiphp_callback)(struct acpiphp_slot *slot, void *data); | ||||
| 
 | ||||
| extern int acpiphp_enable_slot (struct acpiphp_slot *slot); | ||||
|  |  | |||
|  | @ -37,6 +37,7 @@ | |||
| 
 | ||||
| #include <linux/kernel.h> | ||||
| #include <linux/pci.h> | ||||
| #include <linux/pci-acpi.h> | ||||
| #include <linux/pci_hotplug.h> | ||||
| #include <linux/slab.h> | ||||
| #include <linux/smp.h> | ||||
|  | @ -354,19 +355,9 @@ void acpiphp_unregister_hotplug_slot(struct acpiphp_slot *acpiphp_slot) | |||
| } | ||||
| 
 | ||||
| 
 | ||||
| static int __init acpiphp_init(void) | ||||
| void __init acpiphp_init(void) | ||||
| { | ||||
| 	info(DRIVER_DESC " version: " DRIVER_VERSION "%s\n", | ||||
| 		acpiphp_disabled ? ", disabled by user; please report a bug" | ||||
| 				 : ""); | ||||
| 
 | ||||
| 	if (acpi_pci_disabled || acpiphp_disabled) | ||||
| 		return 0; | ||||
| 
 | ||||
| 	/* read all the ACPI info from the system */ | ||||
| 	/* initialize internal data structure etc. */ | ||||
| 	return acpiphp_glue_init(); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| module_init(acpiphp_init); | ||||
|  |  | |||
|  | @ -157,6 +157,7 @@ register_slot(acpi_handle handle, u32 lvl, void *context, void **rv) | |||
| 	int device, function, retval; | ||||
| 	struct pci_bus *pbus = bridge->pci_bus; | ||||
| 	struct pci_dev *pdev; | ||||
| 	u32 val; | ||||
| 
 | ||||
| 	if (!acpi_pci_check_ejectable(pbus, handle) && !is_dock_device(handle)) | ||||
| 		return AE_OK; | ||||
|  | @ -249,11 +250,9 @@ register_slot(acpi_handle handle, u32 lvl, void *context, void **rv) | |||
| 	newfunc->slot = slot; | ||||
| 	list_add_tail(&newfunc->sibling, &slot->funcs); | ||||
| 
 | ||||
| 	pdev = pci_get_slot(pbus, PCI_DEVFN(device, function)); | ||||
| 	if (pdev) { | ||||
| 	if (pci_bus_read_dev_vendor_id(pbus, PCI_DEVFN(device, function), | ||||
| 				       &val, 60*1000)) | ||||
| 		slot->flags |= (SLOT_ENABLED | SLOT_POWEREDON); | ||||
| 		pci_dev_put(pdev); | ||||
| 	} | ||||
| 
 | ||||
| 	if (is_dock_device(handle)) { | ||||
| 		/* we don't want to call this device's _EJ0
 | ||||
|  | @ -366,148 +365,6 @@ static struct acpiphp_func *acpiphp_bridge_handle_to_function(acpi_handle handle | |||
| } | ||||
| 
 | ||||
| 
 | ||||
| static inline void config_p2p_bridge_flags(struct acpiphp_bridge *bridge) | ||||
| { | ||||
| 	acpi_handle dummy_handle; | ||||
| 	struct acpiphp_func *func; | ||||
| 
 | ||||
| 	if (ACPI_SUCCESS(acpi_get_handle(bridge->handle, | ||||
| 					"_EJ0", &dummy_handle))) { | ||||
| 		bridge->flags |= BRIDGE_HAS_EJ0; | ||||
| 
 | ||||
| 		dbg("found ejectable p2p bridge\n"); | ||||
| 
 | ||||
| 		/* make link between PCI bridge and PCI function */ | ||||
| 		func = acpiphp_bridge_handle_to_function(bridge->handle); | ||||
| 		if (!func) | ||||
| 			return; | ||||
| 		bridge->func = func; | ||||
| 		func->bridge = bridge; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| /* allocate and initialize host bridge data structure */ | ||||
| static void add_host_bridge(struct acpi_pci_root *root) | ||||
| { | ||||
| 	struct acpiphp_bridge *bridge; | ||||
| 	acpi_handle handle = root->device->handle; | ||||
| 
 | ||||
| 	bridge = kzalloc(sizeof(struct acpiphp_bridge), GFP_KERNEL); | ||||
| 	if (bridge == NULL) | ||||
| 		return; | ||||
| 
 | ||||
| 	bridge->handle = handle; | ||||
| 
 | ||||
| 	bridge->pci_bus = root->bus; | ||||
| 
 | ||||
| 	init_bridge_misc(bridge); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| /* allocate and initialize PCI-to-PCI bridge data structure */ | ||||
| static void add_p2p_bridge(acpi_handle *handle) | ||||
| { | ||||
| 	struct acpiphp_bridge *bridge; | ||||
| 
 | ||||
| 	bridge = kzalloc(sizeof(struct acpiphp_bridge), GFP_KERNEL); | ||||
| 	if (bridge == NULL) { | ||||
| 		err("out of memory\n"); | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	bridge->handle = handle; | ||||
| 	config_p2p_bridge_flags(bridge); | ||||
| 
 | ||||
| 	bridge->pci_dev = acpi_get_pci_dev(handle); | ||||
| 	bridge->pci_bus = bridge->pci_dev->subordinate; | ||||
| 	if (!bridge->pci_bus) { | ||||
| 		err("This is not a PCI-to-PCI bridge!\n"); | ||||
| 		goto err; | ||||
| 	} | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * Grab a ref to the subordinate PCI bus in case the bus is | ||||
| 	 * removed via PCI core logical hotplug. The ref pins the bus | ||||
| 	 * (which we access during module unload). | ||||
| 	 */ | ||||
| 	get_device(&bridge->pci_bus->dev); | ||||
| 
 | ||||
| 	init_bridge_misc(bridge); | ||||
| 	return; | ||||
|  err: | ||||
| 	pci_dev_put(bridge->pci_dev); | ||||
| 	kfree(bridge); | ||||
| 	return; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| /* callback routine to find P2P bridges */ | ||||
| static acpi_status | ||||
| find_p2p_bridge(acpi_handle handle, u32 lvl, void *context, void **rv) | ||||
| { | ||||
| 	acpi_status status; | ||||
| 	struct pci_dev *dev; | ||||
| 
 | ||||
| 	dev = acpi_get_pci_dev(handle); | ||||
| 	if (!dev || !dev->subordinate) | ||||
| 		goto out; | ||||
| 
 | ||||
| 	/* check if this bridge has ejectable slots */ | ||||
| 	if ((detect_ejectable_slots(handle) > 0)) { | ||||
| 		dbg("found PCI-to-PCI bridge at PCI %s\n", pci_name(dev)); | ||||
| 		add_p2p_bridge(handle); | ||||
| 	} | ||||
| 
 | ||||
| 	/* search P2P bridges under this p2p bridge */ | ||||
| 	status = acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, (u32)1, | ||||
| 				     find_p2p_bridge, NULL, NULL, NULL); | ||||
| 	if (ACPI_FAILURE(status)) | ||||
| 		warn("find_p2p_bridge failed (error code = 0x%x)\n", status); | ||||
| 
 | ||||
|  out: | ||||
| 	pci_dev_put(dev); | ||||
| 	return AE_OK; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| /* find hot-pluggable slots, and then find P2P bridge */ | ||||
| static int add_bridge(struct acpi_pci_root *root) | ||||
| { | ||||
| 	acpi_status status; | ||||
| 	unsigned long long tmp; | ||||
| 	acpi_handle dummy_handle; | ||||
| 	acpi_handle handle = root->device->handle; | ||||
| 
 | ||||
| 	/* if the bridge doesn't have _STA, we assume it is always there */ | ||||
| 	status = acpi_get_handle(handle, "_STA", &dummy_handle); | ||||
| 	if (ACPI_SUCCESS(status)) { | ||||
| 		status = acpi_evaluate_integer(handle, "_STA", NULL, &tmp); | ||||
| 		if (ACPI_FAILURE(status)) { | ||||
| 			dbg("%s: _STA evaluation failure\n", __func__); | ||||
| 			return 0; | ||||
| 		} | ||||
| 		if ((tmp & ACPI_STA_DEVICE_FUNCTIONING) == 0) | ||||
| 			/* don't register this object */ | ||||
| 			return 0; | ||||
| 	} | ||||
| 
 | ||||
| 	/* check if this bridge has ejectable slots */ | ||||
| 	if (detect_ejectable_slots(handle) > 0) { | ||||
| 		dbg("found PCI host-bus bridge with hot-pluggable slots\n"); | ||||
| 		add_host_bridge(root); | ||||
| 	} | ||||
| 
 | ||||
| 	/* search P2P bridges under this host bridge */ | ||||
| 	status = acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, (u32)1, | ||||
| 				     find_p2p_bridge, NULL, NULL, NULL); | ||||
| 
 | ||||
| 	if (ACPI_FAILURE(status)) | ||||
| 		warn("find_p2p_bridge failed (error code = 0x%x)\n", status); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static struct acpiphp_bridge *acpiphp_handle_to_bridge(acpi_handle handle) | ||||
| { | ||||
| 	struct acpiphp_bridge *bridge; | ||||
|  | @ -567,56 +424,12 @@ static void cleanup_bridge(struct acpiphp_bridge *bridge) | |||
| 		slot = next; | ||||
| 	} | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * Only P2P bridges have a pci_dev | ||||
| 	 */ | ||||
| 	if (bridge->pci_dev) | ||||
| 	put_device(&bridge->pci_bus->dev); | ||||
| 
 | ||||
| 	pci_dev_put(bridge->pci_dev); | ||||
| 	list_del(&bridge->list); | ||||
| 	kfree(bridge); | ||||
| } | ||||
| 
 | ||||
| static acpi_status | ||||
| cleanup_p2p_bridge(acpi_handle handle, u32 lvl, void *context, void **rv) | ||||
| { | ||||
| 	struct acpiphp_bridge *bridge; | ||||
| 
 | ||||
| 	/* cleanup p2p bridges under this P2P bridge
 | ||||
| 	   in a depth-first manner */ | ||||
| 	acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, (u32)1, | ||||
| 				cleanup_p2p_bridge, NULL, NULL, NULL); | ||||
| 
 | ||||
| 	bridge = acpiphp_handle_to_bridge(handle); | ||||
| 	if (bridge) | ||||
| 		cleanup_bridge(bridge); | ||||
| 
 | ||||
| 	return AE_OK; | ||||
| } | ||||
| 
 | ||||
| static void remove_bridge(struct acpi_pci_root *root) | ||||
| { | ||||
| 	struct acpiphp_bridge *bridge; | ||||
| 	acpi_handle handle = root->device->handle; | ||||
| 
 | ||||
| 	/* cleanup p2p bridges under this host bridge
 | ||||
| 	   in a depth-first manner */ | ||||
| 	acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, | ||||
| 				(u32)1, cleanup_p2p_bridge, NULL, NULL, NULL); | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * On root bridges with hotplug slots directly underneath (ie, | ||||
| 	 * no p2p bridge between), we call cleanup_bridge().  | ||||
| 	 * | ||||
| 	 * The else clause cleans up root bridges that either had no | ||||
| 	 * hotplug slots at all, or had a p2p bridge underneath. | ||||
| 	 */ | ||||
| 	bridge = acpiphp_handle_to_bridge(handle); | ||||
| 	if (bridge) | ||||
| 		cleanup_bridge(bridge); | ||||
| } | ||||
| 
 | ||||
| static int power_on_slot(struct acpiphp_slot *slot) | ||||
| { | ||||
| 	acpi_status status; | ||||
|  | @ -798,6 +611,7 @@ static void check_hotplug_bridge(struct acpiphp_slot *slot, struct pci_dev *dev) | |||
| 		} | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * enable_device - enable, configure a slot | ||||
|  * @slot: slot to be enabled | ||||
|  | @ -812,7 +626,6 @@ static int __ref enable_device(struct acpiphp_slot *slot) | |||
| 	struct acpiphp_func *func; | ||||
| 	int retval = 0; | ||||
| 	int num, max, pass; | ||||
| 	acpi_status status; | ||||
| 
 | ||||
| 	if (slot->flags & SLOT_ENABLED) | ||||
| 		goto err_exit; | ||||
|  | @ -867,18 +680,6 @@ static int __ref enable_device(struct acpiphp_slot *slot) | |||
| 			slot->flags &= (~SLOT_ENABLED); | ||||
| 			continue; | ||||
| 		} | ||||
| 
 | ||||
| 		if (dev->hdr_type != PCI_HEADER_TYPE_BRIDGE && | ||||
| 		    dev->hdr_type != PCI_HEADER_TYPE_CARDBUS) { | ||||
| 			pci_dev_put(dev); | ||||
| 			continue; | ||||
| 		} | ||||
| 
 | ||||
| 		status = find_p2p_bridge(func->handle, (u32)1, bus, NULL); | ||||
| 		if (ACPI_FAILURE(status)) | ||||
| 			warn("find_p2p_bridge failed (error code = 0x%x)\n", | ||||
| 				status); | ||||
| 		pci_dev_put(dev); | ||||
| 	} | ||||
| 
 | ||||
| 
 | ||||
|  | @ -912,16 +713,6 @@ static int disable_device(struct acpiphp_slot *slot) | |||
| { | ||||
| 	struct acpiphp_func *func; | ||||
| 	struct pci_dev *pdev; | ||||
| 	struct pci_bus *bus = slot->bridge->pci_bus; | ||||
| 
 | ||||
| 	list_for_each_entry(func, &slot->funcs, sibling) { | ||||
| 		if (func->bridge) { | ||||
| 			/* cleanup p2p bridges under this P2P bridge */ | ||||
| 			cleanup_p2p_bridge(func->bridge->handle, | ||||
| 						(u32)1, NULL, NULL); | ||||
| 			func->bridge = NULL; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * enable_device() enumerates all functions in this device via | ||||
|  | @ -940,7 +731,6 @@ static int disable_device(struct acpiphp_slot *slot) | |||
| 
 | ||||
| 	slot->flags &= (~SLOT_ENABLED); | ||||
| 
 | ||||
| err_exit: | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
|  | @ -1287,30 +1077,62 @@ static void handle_hotplug_event_func(acpi_handle handle, u32 type, | |||
| 	alloc_acpi_hp_work(handle, type, context, _handle_hotplug_event_func); | ||||
| } | ||||
| 
 | ||||
| static struct acpi_pci_driver acpi_pci_hp_driver = { | ||||
| 	.add =		add_bridge, | ||||
| 	.remove =	remove_bridge, | ||||
| }; | ||||
| 
 | ||||
| /**
 | ||||
|  * acpiphp_glue_init - initializes all PCI hotplug - ACPI glue data structures | ||||
| /*
 | ||||
|  * Create hotplug slots for the PCI bus. | ||||
|  * It should always return 0 to avoid skipping following notifiers. | ||||
|  */ | ||||
| int __init acpiphp_glue_init(void) | ||||
| void acpiphp_enumerate_slots(struct pci_bus *bus, acpi_handle handle) | ||||
| { | ||||
| 	acpi_pci_register_driver(&acpi_pci_hp_driver); | ||||
| 	acpi_handle dummy_handle; | ||||
| 	struct acpiphp_bridge *bridge; | ||||
| 
 | ||||
| 	return 0; | ||||
| 	if (acpiphp_disabled) | ||||
| 		return; | ||||
| 
 | ||||
| 	if (detect_ejectable_slots(handle) <= 0) | ||||
| 		return; | ||||
| 
 | ||||
| 	bridge = kzalloc(sizeof(struct acpiphp_bridge), GFP_KERNEL); | ||||
| 	if (bridge == NULL) { | ||||
| 		err("out of memory\n"); | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	bridge->handle = handle; | ||||
| 	bridge->pci_dev = pci_dev_get(bus->self); | ||||
| 	bridge->pci_bus = bus; | ||||
| 
 | ||||
| /**
 | ||||
|  * acpiphp_glue_exit - terminates all PCI hotplug - ACPI glue data structures | ||||
|  * | ||||
|  * This function frees all data allocated in acpiphp_glue_init(). | ||||
| 	/*
 | ||||
| 	 * Grab a ref to the subordinate PCI bus in case the bus is | ||||
| 	 * removed via PCI core logical hotplug. The ref pins the bus | ||||
| 	 * (which we access during module unload). | ||||
| 	 */ | ||||
| void  acpiphp_glue_exit(void) | ||||
| 	get_device(&bus->dev); | ||||
| 
 | ||||
| 	if (!pci_is_root_bus(bridge->pci_bus) && | ||||
| 	    ACPI_SUCCESS(acpi_get_handle(bridge->handle, | ||||
| 					"_EJ0", &dummy_handle))) { | ||||
| 		dbg("found ejectable p2p bridge\n"); | ||||
| 		bridge->flags |= BRIDGE_HAS_EJ0; | ||||
| 		bridge->func = acpiphp_bridge_handle_to_function(handle); | ||||
| 	} | ||||
| 
 | ||||
| 	init_bridge_misc(bridge); | ||||
| } | ||||
| 
 | ||||
| /* Destroy hotplug slots associated with the PCI bus */ | ||||
| void acpiphp_remove_slots(struct pci_bus *bus) | ||||
| { | ||||
| 	acpi_pci_unregister_driver(&acpi_pci_hp_driver); | ||||
| 	struct acpiphp_bridge *bridge, *tmp; | ||||
| 
 | ||||
| 	if (acpiphp_disabled) | ||||
| 		return; | ||||
| 
 | ||||
| 	list_for_each_entry_safe(bridge, tmp, &bridge_list, list) | ||||
| 		if (bridge->pci_bus == bus) { | ||||
| 			cleanup_bridge(bridge); | ||||
| 			break; | ||||
| 		} | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  |  | |||
|  | @ -297,6 +297,7 @@ void acpi_pci_add_bus(struct pci_bus *bus) | |||
| 		return; | ||||
| 
 | ||||
| 	acpi_pci_slot_enumerate(bus, handle); | ||||
| 	acpiphp_enumerate_slots(bus, handle); | ||||
| } | ||||
| 
 | ||||
| void acpi_pci_remove_bus(struct pci_bus *bus) | ||||
|  | @ -308,6 +309,7 @@ void acpi_pci_remove_bus(struct pci_bus *bus) | |||
| 	if (acpi_pci_disabled) | ||||
| 		return; | ||||
| 
 | ||||
| 	acpiphp_remove_slots(bus); | ||||
| 	acpi_pci_slot_remove(bus); | ||||
| } | ||||
| 
 | ||||
|  | @ -388,6 +390,7 @@ static int __init acpi_pci_init(void) | |||
| 
 | ||||
| 	pci_set_platform_pm(&acpi_pci_platform_pm); | ||||
| 	acpi_pci_slot_init(); | ||||
| 	acpiphp_init(); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
|  |  | |||
|  | @ -56,6 +56,17 @@ static inline void acpi_pci_slot_enumerate(struct pci_bus *bus, | |||
| static inline void acpi_pci_slot_remove(struct pci_bus *bus) { } | ||||
| #endif | ||||
| 
 | ||||
| #ifdef	CONFIG_HOTPLUG_PCI_ACPI | ||||
| void acpiphp_init(void); | ||||
| void acpiphp_enumerate_slots(struct pci_bus *bus, acpi_handle handle); | ||||
| void acpiphp_remove_slots(struct pci_bus *bus); | ||||
| #else | ||||
| static inline void acpiphp_init(void) { } | ||||
| static inline void acpiphp_enumerate_slots(struct pci_bus *bus, | ||||
| 					   acpi_handle handle) { } | ||||
| static inline void acpiphp_remove_slots(struct pci_bus *bus) { } | ||||
| #endif | ||||
| 
 | ||||
| #else	/* CONFIG_ACPI */ | ||||
| static inline void acpi_pci_add_bus(struct pci_bus *bus) { } | ||||
| static inline void acpi_pci_remove_bus(struct pci_bus *bus) { } | ||||
|  |  | |||
		Loading…
	
		Reference in a new issue
	
	 Jiang Liu
						Jiang Liu