ACPI / hotplug / PCI: Add hotplug contexts to PCI host bridges
After relatively recent changes in the ACPI-based PCI hotplug
(ACPIPHP) code, the acpiphp_check_host_bridge() executed for PCI
host bridges via acpi_pci_root_scan_dependent() doesn't do anything
useful, because those bridges do not have hotplug contexts. That
happens by mistake, so fix it by making acpiphp_enumerate_slots()
add hotplug contexts to PCI host bridges too and modify
acpiphp_remove_slots() to drop those contexts for host bridges
as appropriate.
Link: https://bugzilla.kernel.org/show_bug.cgi?id=76901
Fixes: 2d8b1d566a
(ACPI / hotplug / PCI: Get rid of check_sub_bridges())
Reported-and-tested-by: Gavin Guo <gavin.guo@canonical.com>
Acked-by: Bjorn Helgaas <bhelgaas@google.com>
Reviewed-by: Mika Westerberg <mika.westerberg@linux.intel.com>
Cc: 3.15+ <stable@vger.kernel.org> # 3.15+
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
This commit is contained in:
parent
4dc4226f99
commit
882d18a702
|
@ -142,6 +142,16 @@ static inline acpi_handle func_to_handle(struct acpiphp_func *func)
|
||||||
return func_to_acpi_device(func)->handle;
|
return func_to_acpi_device(func)->handle;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct acpiphp_root_context {
|
||||||
|
struct acpi_hotplug_context hp;
|
||||||
|
struct acpiphp_bridge *root_bridge;
|
||||||
|
};
|
||||||
|
|
||||||
|
static inline struct acpiphp_root_context *to_acpiphp_root_context(struct acpi_hotplug_context *hp)
|
||||||
|
{
|
||||||
|
return container_of(hp, struct acpiphp_root_context, hp);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* struct acpiphp_attention_info - device specific attention registration
|
* struct acpiphp_attention_info - device specific attention registration
|
||||||
*
|
*
|
||||||
|
|
|
@ -373,17 +373,13 @@ static acpi_status acpiphp_add_context(acpi_handle handle, u32 lvl, void *data,
|
||||||
|
|
||||||
static struct acpiphp_bridge *acpiphp_dev_to_bridge(struct acpi_device *adev)
|
static struct acpiphp_bridge *acpiphp_dev_to_bridge(struct acpi_device *adev)
|
||||||
{
|
{
|
||||||
struct acpiphp_context *context;
|
|
||||||
struct acpiphp_bridge *bridge = NULL;
|
struct acpiphp_bridge *bridge = NULL;
|
||||||
|
|
||||||
acpi_lock_hp_context();
|
acpi_lock_hp_context();
|
||||||
context = acpiphp_get_context(adev);
|
if (adev->hp) {
|
||||||
if (context) {
|
bridge = to_acpiphp_root_context(adev->hp)->root_bridge;
|
||||||
bridge = context->bridge;
|
|
||||||
if (bridge)
|
if (bridge)
|
||||||
get_bridge(bridge);
|
get_bridge(bridge);
|
||||||
|
|
||||||
acpiphp_put_context(context);
|
|
||||||
}
|
}
|
||||||
acpi_unlock_hp_context();
|
acpi_unlock_hp_context();
|
||||||
return bridge;
|
return bridge;
|
||||||
|
@ -881,7 +877,17 @@ void acpiphp_enumerate_slots(struct pci_bus *bus)
|
||||||
*/
|
*/
|
||||||
get_device(&bus->dev);
|
get_device(&bus->dev);
|
||||||
|
|
||||||
if (!pci_is_root_bus(bridge->pci_bus)) {
|
acpi_lock_hp_context();
|
||||||
|
if (pci_is_root_bus(bridge->pci_bus)) {
|
||||||
|
struct acpiphp_root_context *root_context;
|
||||||
|
|
||||||
|
root_context = kzalloc(sizeof(*root_context), GFP_KERNEL);
|
||||||
|
if (!root_context)
|
||||||
|
goto err;
|
||||||
|
|
||||||
|
root_context->root_bridge = bridge;
|
||||||
|
acpi_set_hp_context(adev, &root_context->hp, NULL, NULL, NULL);
|
||||||
|
} else {
|
||||||
struct acpiphp_context *context;
|
struct acpiphp_context *context;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -890,21 +896,16 @@ void acpiphp_enumerate_slots(struct pci_bus *bus)
|
||||||
* parent is going to be handled by pciehp, in which case this
|
* parent is going to be handled by pciehp, in which case this
|
||||||
* bridge is not interesting to us either.
|
* bridge is not interesting to us either.
|
||||||
*/
|
*/
|
||||||
acpi_lock_hp_context();
|
|
||||||
context = acpiphp_get_context(adev);
|
context = acpiphp_get_context(adev);
|
||||||
if (!context) {
|
if (!context)
|
||||||
acpi_unlock_hp_context();
|
goto err;
|
||||||
put_device(&bus->dev);
|
|
||||||
pci_dev_put(bridge->pci_dev);
|
|
||||||
kfree(bridge);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
bridge->context = context;
|
bridge->context = context;
|
||||||
context->bridge = bridge;
|
context->bridge = bridge;
|
||||||
/* Get a reference to the parent bridge. */
|
/* Get a reference to the parent bridge. */
|
||||||
get_bridge(context->func.parent);
|
get_bridge(context->func.parent);
|
||||||
acpi_unlock_hp_context();
|
|
||||||
}
|
}
|
||||||
|
acpi_unlock_hp_context();
|
||||||
|
|
||||||
/* Must be added to the list prior to calling acpiphp_add_context(). */
|
/* Must be added to the list prior to calling acpiphp_add_context(). */
|
||||||
mutex_lock(&bridge_mutex);
|
mutex_lock(&bridge_mutex);
|
||||||
|
@ -919,6 +920,30 @@ void acpiphp_enumerate_slots(struct pci_bus *bus)
|
||||||
cleanup_bridge(bridge);
|
cleanup_bridge(bridge);
|
||||||
put_bridge(bridge);
|
put_bridge(bridge);
|
||||||
}
|
}
|
||||||
|
return;
|
||||||
|
|
||||||
|
err:
|
||||||
|
acpi_unlock_hp_context();
|
||||||
|
put_device(&bus->dev);
|
||||||
|
pci_dev_put(bridge->pci_dev);
|
||||||
|
kfree(bridge);
|
||||||
|
}
|
||||||
|
|
||||||
|
void acpiphp_drop_bridge(struct acpiphp_bridge *bridge)
|
||||||
|
{
|
||||||
|
if (pci_is_root_bus(bridge->pci_bus)) {
|
||||||
|
struct acpiphp_root_context *root_context;
|
||||||
|
struct acpi_device *adev;
|
||||||
|
|
||||||
|
acpi_lock_hp_context();
|
||||||
|
adev = ACPI_COMPANION(bridge->pci_bus->bridge);
|
||||||
|
root_context = to_acpiphp_root_context(adev->hp);
|
||||||
|
adev->hp = NULL;
|
||||||
|
acpi_unlock_hp_context();
|
||||||
|
kfree(root_context);
|
||||||
|
}
|
||||||
|
cleanup_bridge(bridge);
|
||||||
|
put_bridge(bridge);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -936,8 +961,7 @@ void acpiphp_remove_slots(struct pci_bus *bus)
|
||||||
list_for_each_entry(bridge, &bridge_list, list)
|
list_for_each_entry(bridge, &bridge_list, list)
|
||||||
if (bridge->pci_bus == bus) {
|
if (bridge->pci_bus == bus) {
|
||||||
mutex_unlock(&bridge_mutex);
|
mutex_unlock(&bridge_mutex);
|
||||||
cleanup_bridge(bridge);
|
acpiphp_drop_bridge(bridge);
|
||||||
put_bridge(bridge);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue