===================================================================
@@ -139,6 +139,16 @@ struct acpi_scan_handler {
};
/*
+ * ACPI Hotplug Context
+ * --------------------
+ */
+
+struct acpi_hotplug_context {
+ struct acpi_device *self;
+ void (*release)(struct acpi_hotplug_context *);
+};
+
+/*
* ACPI Driver
* -----------
*/
@@ -331,6 +341,7 @@ struct acpi_device {
struct acpi_device_perf performance;
struct acpi_device_dir dir;
struct acpi_scan_handler *handler;
+ struct acpi_hotplug_context *hp;
struct acpi_driver *driver;
void *driver_data;
struct device dev;
===================================================================
@@ -1063,6 +1063,9 @@ static void acpi_device_del_work_fn(stru
mutex_unlock(&acpi_device_del_lock);
acpi_device_del(adev);
+ if (adev->hp && adev->hp->release)
+ adev->hp->release(adev->hp);
+
/*
* Drop references to all power resources that might have been
* used by the device.
===================================================================
@@ -116,12 +116,17 @@ struct acpiphp_func {
};
struct acpiphp_context {
+ struct acpi_hotplug_context hp;
struct acpiphp_func func;
- struct acpi_device *adev;
struct acpiphp_bridge *bridge;
unsigned int refcount;
};
+static inline struct acpiphp_context *to_acpiphp_context(struct acpi_hotplug_context *hp)
+{
+ return container_of(hp, struct acpiphp_context, hp);
+}
+
static inline struct acpiphp_context *func_to_context(struct acpiphp_func *func)
{
return container_of(func, struct acpiphp_context, func);
@@ -129,7 +134,7 @@ static inline struct acpiphp_context *fu
static inline struct acpi_device *func_to_acpi_device(struct acpiphp_func *func)
{
- return func_to_context(func)->adev;
+ return func_to_context(func)->hp.self;
}
static inline acpi_handle func_to_handle(struct acpiphp_func *func)
===================================================================
@@ -53,9 +53,13 @@
#include <linux/slab.h>
#include <linux/acpi.h>
+#include <asm/pgtable.h>
+
#include "../pci.h"
#include "acpiphp.h"
+#define INVALID_ACPI_HANDLE ((acpi_handle)empty_zero_page)
+
static LIST_HEAD(bridge_list);
static DEFINE_MUTEX(bridge_mutex);
static DEFINE_MUTEX(acpiphp_context_lock);
@@ -66,9 +70,9 @@ static void acpiphp_set_hpp_values(struc
static void hotplug_event(u32 type, void *data);
static void free_bridge(struct kref *kref);
-static void acpiphp_context_handler(acpi_handle handle, void *context)
+static void acpiphp_free_context(struct acpi_hotplug_context *hp)
{
- /* Intentionally empty. */
+ kfree(to_acpiphp_context(hp));
}
/**
@@ -80,39 +84,29 @@ static void acpiphp_context_handler(acpi
static struct acpiphp_context *acpiphp_init_context(struct acpi_device *adev)
{
struct acpiphp_context *context;
- acpi_status status;
context = kzalloc(sizeof(*context), GFP_KERNEL);
if (!context)
return NULL;
- context->adev = adev;
+ context->hp.self = adev;
+ context->hp.release = acpiphp_free_context;
context->refcount = 1;
- status = acpi_attach_data(adev->handle, acpiphp_context_handler, context);
- if (ACPI_FAILURE(status)) {
- kfree(context);
- return NULL;
- }
+ adev->hp = &context->hp;
return context;
}
/**
* acpiphp_get_context - Get hotplug context and grab a reference to it.
- * @handle: ACPI object handle to get the context for.
+ * @adev: ACPI device object to get the context for.
*
* Call under acpiphp_context_lock.
*/
-static struct acpiphp_context *acpiphp_get_context(acpi_handle handle)
+static struct acpiphp_context *acpiphp_get_context(struct acpi_device *adev)
{
- struct acpiphp_context *context = NULL;
- acpi_status status;
- void *data;
+ struct acpiphp_context *context = to_acpiphp_context(adev->hp);
- status = acpi_get_data(handle, acpiphp_context_handler, &data);
- if (ACPI_SUCCESS(status)) {
- context = data;
- context->refcount++;
- }
+ context->refcount++;
return context;
}
@@ -130,7 +124,7 @@ static void acpiphp_put_context(struct a
return;
WARN_ON(context->bridge);
- acpi_detach_data(context->adev->handle, acpiphp_context_handler);
+ context->hp.self->hp = NULL;
kfree(context);
}
@@ -395,9 +389,13 @@ static struct acpiphp_bridge *acpiphp_ha
{
struct acpiphp_context *context;
struct acpiphp_bridge *bridge = NULL;
+ struct acpi_device *adev;
+
+ if (acpi_bus_get_device(handle, &adev))
+ return NULL;
mutex_lock(&acpiphp_context_lock);
- context = acpiphp_get_context(handle);
+ context = acpiphp_get_context(adev);
if (context) {
bridge = context->bridge;
if (bridge)
@@ -793,7 +791,7 @@ static void hotplug_event(u32 type, void
struct acpiphp_context *context = data;
struct acpiphp_func *func = &context->func;
struct acpiphp_slot *slot = func->slot;
- acpi_handle handle = context->adev->handle;
+ acpi_handle handle = context->hp.self->handle;
struct acpiphp_bridge *bridge;
mutex_lock(&acpiphp_context_lock);
@@ -842,18 +840,39 @@ static void hotplug_event(u32 type, void
static void hotplug_event_work(void *data, u32 type)
{
- struct acpiphp_context *context = data;
+ struct acpi_device *adev = data;
+ struct acpiphp_context *context;
+ u32 ost_code = ACPI_OST_SC_NON_SPECIFIC_FAILURE;
acpi_scan_lock_acquire();
- pci_lock_rescan_remove();
+ /*
+ * The device object's ACPI handle cannot become invalid as long as we
+ * are holding acpi_scan_lock, but it might have become invalid before
+ * that lock was acquired.
+ */
+ if (adev->handle == INVALID_ACPI_HANDLE)
+ goto out;
- hotplug_event(type, context);
+ mutex_lock(&acpiphp_context_lock);
+ context = acpiphp_get_context(adev);
+ if (!context) {
+ mutex_unlock(&acpiphp_context_lock);
+ goto out;
+ }
+ get_bridge(context->func.parent);
+ acpiphp_put_context(context);
+ mutex_unlock(&acpiphp_context_lock);
+ pci_lock_rescan_remove();
+ hotplug_event(type, context);
pci_unlock_rescan_remove();
- acpi_scan_lock_release();
- acpi_evaluate_hotplug_ost(context->adev->handle, type,
- ACPI_OST_SC_SUCCESS, NULL);
put_bridge(context->func.parent);
+ ost_code = ACPI_OST_SC_SUCCESS;
+
+ out:
+ acpi_evaluate_hotplug_ost(adev->handle, type, ost_code, NULL);
+ put_device(&adev->dev);
+ acpi_scan_lock_release();
}
/**
@@ -866,7 +885,8 @@ static void hotplug_event_work(void *dat
*/
static void handle_hotplug_event(acpi_handle handle, u32 type, void *data)
{
- struct acpiphp_context *context;
+ struct acpi_device *adev;
+ acpi_status status;
u32 ost_code = ACPI_OST_SC_SUCCESS;
switch (type) {
@@ -901,17 +921,16 @@ static void handle_hotplug_event(acpi_ha
goto out;
}
- mutex_lock(&acpiphp_context_lock);
- context = acpiphp_get_context(handle);
- if (context && !WARN_ON(context->adev->handle != handle)) {
- get_bridge(context->func.parent);
- acpiphp_put_context(context);
- acpi_hotplug_execute(hotplug_event_work, context, type);
- mutex_unlock(&acpiphp_context_lock);
- return;
- }
- mutex_unlock(&acpiphp_context_lock);
ost_code = ACPI_OST_SC_NON_SPECIFIC_FAILURE;
+ if (acpi_bus_get_device(handle, &adev))
+ goto out;
+
+ get_device(&adev->dev);
+ status = acpi_hotplug_execute(hotplug_event_work, adev, type);
+ if (ACPI_SUCCESS(status))
+ return;
+
+ put_device(&adev->dev);
out:
acpi_evaluate_hotplug_ost(handle, type, ost_code, NULL);
@@ -967,7 +986,7 @@ void acpiphp_enumerate_slots(struct pci_
* bridge is not interesting to us either.
*/
mutex_lock(&acpiphp_context_lock);
- context = acpiphp_get_context(handle);
+ context = acpiphp_get_context(adev);
if (!context) {
mutex_unlock(&acpiphp_context_lock);
put_device(&bus->dev);