Merge branch 'acpi-hotplug'

* acpi-hotplug:
  ACPI / scan: ACPI device object sysfs attribute for _STA evaluation
  ACPI / hotplug / driver core: Handle containers in a special way
  ACPI / hotplug: Add demand_offline hotplug profile flag
  ACPI / bind: Move acpi_get_child() to drivers/ide/ide-acpi.c
  ACPI / bind: Pass struct acpi_device pointer to acpi_bind_one()
  ACPI / bind: Rework struct acpi_bus_type
  ACPI / bind: Redefine acpi_preset_companion()
  ACPI / bind: Redefine acpi_get_child()
  PCI / ACPI: Use acpi_find_child_device() for child devices lookup
  ACPI / bind: Simplify child device lookups
  ACPI / scan: Use direct recurrence for device hierarchy walks
  ACPI: Introduce acpi_set_device_status()
  ACPI / hotplug: Drop unfinished global notification handling routines
  ACPI / hotplug: Rework generic code to handle suprise removals
  ACPI / hotplug: Move container-specific code out of the core
  ACPI / hotplug: Make ACPI PCI root hotplug use common hotplug code
  ACPI / hotplug: Introduce common hotplug function acpi_device_hotplug()
  ACPI / hotplug: Do not fail bus and device checks for disabled hotplug
  ACPI / scan: Add acpi_device objects for all device nodes in the namespace
  ACPI / scan: Define non-empty device removal handler
This commit is contained in:
Rafael J. Wysocki 2014-01-12 23:45:04 +01:00
commit 25d412d932
28 changed files with 654 additions and 629 deletions

View file

@ -180,14 +180,14 @@ static unsigned long acpi_meminfo_end_pfn(struct acpi_memory_info *info)
static int acpi_bind_memblk(struct memory_block *mem, void *arg)
{
return acpi_bind_one(&mem->dev, (acpi_handle)arg);
return acpi_bind_one(&mem->dev, arg);
}
static int acpi_bind_memory_blocks(struct acpi_memory_info *info,
acpi_handle handle)
struct acpi_device *adev)
{
return walk_memory_range(acpi_meminfo_start_pfn(info),
acpi_meminfo_end_pfn(info), (void *)handle,
acpi_meminfo_end_pfn(info), adev,
acpi_bind_memblk);
}
@ -197,8 +197,7 @@ static int acpi_unbind_memblk(struct memory_block *mem, void *arg)
return 0;
}
static void acpi_unbind_memory_blocks(struct acpi_memory_info *info,
acpi_handle handle)
static void acpi_unbind_memory_blocks(struct acpi_memory_info *info)
{
walk_memory_range(acpi_meminfo_start_pfn(info),
acpi_meminfo_end_pfn(info), NULL, acpi_unbind_memblk);
@ -242,9 +241,9 @@ static int acpi_memory_enable_device(struct acpi_memory_device *mem_device)
if (result && result != -EEXIST)
continue;
result = acpi_bind_memory_blocks(info, handle);
result = acpi_bind_memory_blocks(info, mem_device->device);
if (result) {
acpi_unbind_memory_blocks(info, handle);
acpi_unbind_memory_blocks(info);
return -ENODEV;
}
@ -285,7 +284,7 @@ static void acpi_memory_remove_memory(struct acpi_memory_device *mem_device)
if (nid == NUMA_NO_NODE)
nid = memory_add_physaddr_to_nid(info->start_addr);
acpi_unbind_memory_blocks(info, handle);
acpi_unbind_memory_blocks(info);
remove_memory(nid, info->start_addr, info->length);
list_del(&info->list);
kfree(info);

View file

@ -395,7 +395,7 @@ static int acpi_processor_add(struct acpi_device *device,
goto err;
}
result = acpi_bind_one(dev, pr->handle);
result = acpi_bind_one(dev, device);
if (result)
goto err;

View file

@ -50,9 +50,6 @@ struct acpi_device *acpi_root;
struct proc_dir_entry *acpi_root_dir;
EXPORT_SYMBOL(acpi_root_dir);
#define STRUCT_TO_INT(s) (*((int*)&s))
#ifdef CONFIG_X86
static int set_copy_dsdt(const struct dmi_system_id *id)
{
@ -113,18 +110,16 @@ int acpi_bus_get_status(struct acpi_device *device)
if (ACPI_FAILURE(status))
return -ENODEV;
STRUCT_TO_INT(device->status) = (int) sta;
acpi_set_device_status(device, sta);
if (device->status.functional && !device->status.present) {
ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Device [%s] status [%08x]: "
"functional but not present;\n",
device->pnp.bus_id,
(u32) STRUCT_TO_INT(device->status)));
device->pnp.bus_id, (u32)sta));
}
ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Device [%s] status [%08x]\n",
device->pnp.bus_id,
(u32) STRUCT_TO_INT(device->status)));
device->pnp.bus_id, (u32)sta));
return 0;
}
EXPORT_SYMBOL(acpi_bus_get_status);
@ -337,58 +332,6 @@ static void acpi_bus_osc_support(void)
Notification Handling
-------------------------------------------------------------------------- */
static void acpi_bus_check_device(acpi_handle handle)
{
struct acpi_device *device;
acpi_status status;
struct acpi_device_status old_status;
if (acpi_bus_get_device(handle, &device))
return;
if (!device)
return;
old_status = device->status;
/*
* Make sure this device's parent is present before we go about
* messing with the device.
*/
if (device->parent && !device->parent->status.present) {
device->status = device->parent->status;
return;
}
status = acpi_bus_get_status(device);
if (ACPI_FAILURE(status))
return;
if (STRUCT_TO_INT(old_status) == STRUCT_TO_INT(device->status))
return;
/*
* Device Insertion/Removal
*/
if ((device->status.present) && !(old_status.present)) {
ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Device insertion detected\n"));
/* TBD: Handle device insertion */
} else if (!(device->status.present) && (old_status.present)) {
ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Device removal detected\n"));
/* TBD: Handle device removal */
}
}
static void acpi_bus_check_scope(acpi_handle handle)
{
/* Status Change? */
acpi_bus_check_device(handle);
/*
* TBD: Enumerate child devices within this device's scope and
* run acpi_bus_check_device()'s on them.
*/
}
/**
* acpi_bus_notify
* ---------------
@ -405,19 +348,11 @@ static void acpi_bus_notify(acpi_handle handle, u32 type, void *data)
switch (type) {
case ACPI_NOTIFY_BUS_CHECK:
acpi_bus_check_scope(handle);
/*
* TBD: We'll need to outsource certain events to non-ACPI
* drivers via the device manager (device.c).
*/
/* TBD */
break;
case ACPI_NOTIFY_DEVICE_CHECK:
acpi_bus_check_device(handle);
/*
* TBD: We'll need to outsource certain events to non-ACPI
* drivers via the device manager (device.c).
*/
/* TBD */
break;
case ACPI_NOTIFY_DEVICE_WAKE:

View file

@ -27,8 +27,7 @@
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/
#include <linux/acpi.h>
#include "internal.h"
#include <linux/container.h>
#include "internal.h"
@ -44,19 +43,65 @@ static const struct acpi_device_id container_device_ids[] = {
{"", 0},
};
static int container_device_attach(struct acpi_device *device,
static int acpi_container_offline(struct container_dev *cdev)
{
struct acpi_device *adev = ACPI_COMPANION(&cdev->dev);
struct acpi_device *child;
/* Check all of the dependent devices' physical companions. */
list_for_each_entry(child, &adev->children, node)
if (!acpi_scan_is_offline(child, false))
return -EBUSY;
return 0;
}
static void acpi_container_release(struct device *dev)
{
kfree(to_container_dev(dev));
}
static int container_device_attach(struct acpi_device *adev,
const struct acpi_device_id *not_used)
{
/* This is necessary for container hotplug to work. */
struct container_dev *cdev;
struct device *dev;
int ret;
cdev = kzalloc(sizeof(*cdev), GFP_KERNEL);
if (!cdev)
return -ENOMEM;
cdev->offline = acpi_container_offline;
dev = &cdev->dev;
dev->bus = &container_subsys;
dev_set_name(dev, "%s", dev_name(&adev->dev));
ACPI_COMPANION_SET(dev, adev);
dev->release = acpi_container_release;
ret = device_register(dev);
if (ret)
return ret;
adev->driver_data = dev;
return 1;
}
static void container_device_detach(struct acpi_device *adev)
{
struct device *dev = acpi_driver_data(adev);
adev->driver_data = NULL;
if (dev)
device_unregister(dev);
}
static struct acpi_scan_handler container_handler = {
.ids = container_device_ids,
.attach = container_device_attach,
.detach = container_device_detach,
.hotplug = {
.enabled = true,
.mode = AHM_CONTAINER,
.demand_offline = true,
},
};

View file

@ -256,6 +256,8 @@ int acpi_bus_init_power(struct acpi_device *device)
return -EINVAL;
device->power.state = ACPI_STATE_UNKNOWN;
if (!acpi_device_is_present(device))
return 0;
result = acpi_device_get_power(device, &state);
if (result)
@ -302,15 +304,18 @@ int acpi_device_fix_up_power(struct acpi_device *device)
return ret;
}
int acpi_bus_update_power(acpi_handle handle, int *state_p)
int acpi_device_update_power(struct acpi_device *device, int *state_p)
{
struct acpi_device *device;
int state;
int result;
result = acpi_bus_get_device(handle, &device);
if (result)
if (device->power.state == ACPI_STATE_UNKNOWN) {
result = acpi_bus_init_power(device);
if (!result && state_p)
*state_p = device->power.state;
return result;
}
result = acpi_device_get_power(device, &state);
if (result)
@ -338,6 +343,15 @@ int acpi_bus_update_power(acpi_handle handle, int *state_p)
return 0;
}
int acpi_bus_update_power(acpi_handle handle, int *state_p)
{
struct acpi_device *device;
int result;
result = acpi_bus_get_device(handle, &device);
return result ? result : acpi_device_update_power(device, state_p);
}
EXPORT_SYMBOL_GPL(acpi_bus_update_power);
bool acpi_bus_power_manageable(acpi_handle handle)

View file

@ -323,14 +323,11 @@ static int dock_present(struct dock_station *ds)
*/
static void dock_create_acpi_device(acpi_handle handle)
{
struct acpi_device *device;
struct acpi_device *device = NULL;
int ret;
if (acpi_bus_get_device(handle, &device)) {
/*
* no device created for this object,
* so we should create one.
*/
acpi_bus_get_device(handle, &device);
if (!acpi_device_enumerated(device)) {
ret = acpi_bus_scan(handle);
if (ret)
pr_debug("error adding bus, %x\n", -ret);

View file

@ -37,7 +37,7 @@ int register_acpi_bus_type(struct acpi_bus_type *type)
{
if (acpi_disabled)
return -ENODEV;
if (type && type->match && type->find_device) {
if (type && type->match && type->find_companion) {
down_write(&bus_type_sem);
list_add_tail(&type->list, &bus_type_list);
up_write(&bus_type_sem);
@ -82,109 +82,74 @@ static struct acpi_bus_type *acpi_get_bus_type(struct device *dev)
#define FIND_CHILD_MIN_SCORE 1
#define FIND_CHILD_MAX_SCORE 2
static acpi_status acpi_dev_present(acpi_handle handle, u32 lvl_not_used,
void *not_used, void **ret_p)
{
struct acpi_device *adev = NULL;
acpi_bus_get_device(handle, &adev);
if (adev) {
*ret_p = handle;
return AE_CTRL_TERMINATE;
}
return AE_OK;
}
static int do_find_child_checks(acpi_handle handle, bool is_bridge)
static int find_child_checks(struct acpi_device *adev, bool check_children)
{
bool sta_present = true;
unsigned long long sta;
acpi_status status;
status = acpi_evaluate_integer(handle, "_STA", NULL, &sta);
status = acpi_evaluate_integer(adev->handle, "_STA", NULL, &sta);
if (status == AE_NOT_FOUND)
sta_present = false;
else if (ACPI_FAILURE(status) || !(sta & ACPI_STA_DEVICE_ENABLED))
return -ENODEV;
if (is_bridge) {
void *test = NULL;
if (check_children && list_empty(&adev->children))
return -ENODEV;
/* Check if this object has at least one child device. */
acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, 1,
acpi_dev_present, NULL, NULL, &test);
if (!test)
return -ENODEV;
}
return sta_present ? FIND_CHILD_MAX_SCORE : FIND_CHILD_MIN_SCORE;
}
struct find_child_context {
u64 addr;
bool is_bridge;
acpi_handle ret;
int ret_score;
};
static acpi_status do_find_child(acpi_handle handle, u32 lvl_not_used,
void *data, void **not_used)
struct acpi_device *acpi_find_child_device(struct acpi_device *parent,
u64 address, bool check_children)
{
struct find_child_context *context = data;
unsigned long long addr;
acpi_status status;
int score;
struct acpi_device *adev, *ret = NULL;
int ret_score = 0;
status = acpi_evaluate_integer(handle, METHOD_NAME__ADR, NULL, &addr);
if (ACPI_FAILURE(status) || addr != context->addr)
return AE_OK;
if (!parent)
return NULL;
if (!context->ret) {
/* This is the first matching object. Save its handle. */
context->ret = handle;
return AE_OK;
list_for_each_entry(adev, &parent->children, node) {
unsigned long long addr;
acpi_status status;
int score;
status = acpi_evaluate_integer(adev->handle, METHOD_NAME__ADR,
NULL, &addr);
if (ACPI_FAILURE(status) || addr != address)
continue;
if (!ret) {
/* This is the first matching object. Save it. */
ret = adev;
continue;
}
/*
* There is more than one matching device object with the same
* _ADR value. That really is unexpected, so we are kind of
* beyond the scope of the spec here. We have to choose which
* one to return, though.
*
* First, check if the previously found object is good enough
* and return it if so. Second, do the same for the object that
* we've just found.
*/
if (!ret_score) {
ret_score = find_child_checks(ret, check_children);
if (ret_score == FIND_CHILD_MAX_SCORE)
return ret;
}
score = find_child_checks(adev, check_children);
if (score == FIND_CHILD_MAX_SCORE) {
return adev;
} else if (score > ret_score) {
ret = adev;
ret_score = score;
}
}
/*
* There is more than one matching object with the same _ADR value.
* That really is unexpected, so we are kind of beyond the scope of the
* spec here. We have to choose which one to return, though.
*
* First, check if the previously found object is good enough and return
* its handle if so. Second, check the same for the object that we've
* just found.
*/
if (!context->ret_score) {
score = do_find_child_checks(context->ret, context->is_bridge);
if (score == FIND_CHILD_MAX_SCORE)
return AE_CTRL_TERMINATE;
else
context->ret_score = score;
}
score = do_find_child_checks(handle, context->is_bridge);
if (score == FIND_CHILD_MAX_SCORE) {
context->ret = handle;
return AE_CTRL_TERMINATE;
} else if (score > context->ret_score) {
context->ret = handle;
context->ret_score = score;
}
return AE_OK;
return ret;
}
acpi_handle acpi_find_child(acpi_handle parent, u64 addr, bool is_bridge)
{
if (parent) {
struct find_child_context context = {
.addr = addr,
.is_bridge = is_bridge,
};
acpi_walk_namespace(ACPI_TYPE_DEVICE, parent, 1, do_find_child,
NULL, &context, NULL);
return context.ret;
}
return NULL;
}
EXPORT_SYMBOL_GPL(acpi_find_child);
EXPORT_SYMBOL_GPL(acpi_find_child_device);
static void acpi_physnode_link_name(char *buf, unsigned int node_id)
{
@ -195,9 +160,8 @@ static void acpi_physnode_link_name(char *buf, unsigned int node_id)
strcpy(buf, PHYSICAL_NODE_STRING);
}
int acpi_bind_one(struct device *dev, acpi_handle handle)
int acpi_bind_one(struct device *dev, struct acpi_device *acpi_dev)
{
struct acpi_device *acpi_dev = NULL;
struct acpi_device_physical_node *physical_node, *pn;
char physical_node_name[PHYSICAL_NODE_NAME_SIZE];
struct list_head *physnode_list;
@ -205,14 +169,12 @@ int acpi_bind_one(struct device *dev, acpi_handle handle)
int retval = -EINVAL;
if (ACPI_COMPANION(dev)) {
if (handle) {
if (acpi_dev) {
dev_warn(dev, "ACPI companion already set\n");
return -EINVAL;
} else {
acpi_dev = ACPI_COMPANION(dev);
}
} else {
acpi_bus_get_device(handle, &acpi_dev);
}
if (!acpi_dev)
return -EINVAL;
@ -322,29 +284,22 @@ int acpi_unbind_one(struct device *dev)
}
EXPORT_SYMBOL_GPL(acpi_unbind_one);
void acpi_preset_companion(struct device *dev, acpi_handle parent, u64 addr)
{
struct acpi_device *adev;
if (!acpi_bus_get_device(acpi_get_child(parent, addr), &adev))
ACPI_COMPANION_SET(dev, adev);
}
EXPORT_SYMBOL_GPL(acpi_preset_companion);
static int acpi_platform_notify(struct device *dev)
{
struct acpi_bus_type *type = acpi_get_bus_type(dev);
acpi_handle handle;
int ret;
ret = acpi_bind_one(dev, NULL);
if (ret && type) {
ret = type->find_device(dev, &handle);
if (ret) {
struct acpi_device *adev;
adev = type->find_companion(dev);
if (!adev) {
DBG("Unable to get handle for %s\n", dev_name(dev));
ret = -ENODEV;
goto out;
}
ret = acpi_bind_one(dev, handle);
ret = acpi_bind_one(dev, adev);
if (ret)
goto out;
}

View file

@ -28,7 +28,6 @@ int init_acpi_device_notify(void);
int acpi_scan_init(void);
void acpi_pci_root_init(void);
void acpi_pci_link_init(void);
void acpi_pci_root_hp_init(void);
void acpi_processor_init(void);
void acpi_platform_init(void);
int acpi_sysfs_init(void);
@ -73,6 +72,9 @@ void acpi_lpss_init(void);
static inline void acpi_lpss_init(void) {}
#endif
bool acpi_queue_hotplug_work(struct work_struct *work);
bool acpi_scan_is_offline(struct acpi_device *adev, bool uevent);
/* --------------------------------------------------------------------------
Device Node Initialization / Removal
-------------------------------------------------------------------------- */
@ -85,9 +87,9 @@ void acpi_init_device_object(struct acpi_device *device, acpi_handle handle,
int type, unsigned long long sta);
void acpi_device_add_finalize(struct acpi_device *device);
void acpi_free_pnp_ids(struct acpi_device_pnp *pnp);
int acpi_bind_one(struct device *dev, acpi_handle handle);
int acpi_bind_one(struct device *dev, struct acpi_device *adev);
int acpi_unbind_one(struct device *dev);
void acpi_bus_device_eject(void *data, u32 ost_src);
bool acpi_device_is_present(struct acpi_device *adev);
/* --------------------------------------------------------------------------
Power Resource
@ -105,6 +107,8 @@ int acpi_power_get_inferred_state(struct acpi_device *device, int *state);
int acpi_power_on_resources(struct acpi_device *device, int state);
int acpi_power_transition(struct acpi_device *device, int state);
int acpi_device_update_power(struct acpi_device *device, int *state_p);
int acpi_wakeup_device_init(void);
void acpi_early_processor_set_pdc(void);

View file

@ -1211,6 +1211,10 @@ acpi_status acpi_hotplug_execute(acpi_hp_callback func, void *data, u32 src)
return AE_OK;
}
bool acpi_queue_hotplug_work(struct work_struct *work)
{
return queue_work(kacpi_hotplug_wq, work);
}
acpi_status
acpi_os_create_semaphore(u32 max_units, u32 initial_units, acpi_handle * handle)
@ -1790,7 +1794,7 @@ acpi_status __init acpi_os_initialize1(void)
{
kacpid_wq = alloc_workqueue("kacpid", 0, 1);
kacpi_notify_wq = alloc_workqueue("kacpi_notify", 0, 1);
kacpi_hotplug_wq = alloc_workqueue("kacpi_hotplug", 0, 1);
kacpi_hotplug_wq = alloc_ordered_workqueue("kacpi_hotplug", 0);
BUG_ON(!kacpid_wq);
BUG_ON(!kacpi_notify_wq);
BUG_ON(!kacpi_hotplug_wq);

View file

@ -49,6 +49,12 @@ static int acpi_pci_root_add(struct acpi_device *device,
const struct acpi_device_id *not_used);
static void acpi_pci_root_remove(struct acpi_device *device);
static int acpi_pci_root_scan_dependent(struct acpi_device *adev)
{
acpiphp_check_host_bridge(adev->handle);
return 0;
}
#define ACPI_PCIE_REQ_SUPPORT (OSC_PCI_EXT_CONFIG_SUPPORT \
| OSC_PCI_ASPM_SUPPORT \
| OSC_PCI_CLOCK_PM_SUPPORT \
@ -64,7 +70,8 @@ static struct acpi_scan_handler pci_root_handler = {
.attach = acpi_pci_root_add,
.detach = acpi_pci_root_remove,
.hotplug = {
.ignore = true,
.enabled = true,
.scan_dependent = acpi_pci_root_scan_dependent,
},
};
@ -622,116 +629,9 @@ static void acpi_pci_root_remove(struct acpi_device *device)
void __init acpi_pci_root_init(void)
{
acpi_hest_init();
if (!acpi_pci_disabled) {
pci_acpi_crs_quirks();
acpi_scan_add_handler(&pci_root_handler);
}
}
/* Support root bridge hotplug */
static void handle_root_bridge_insertion(acpi_handle handle)
{
struct acpi_device *device;
if (!acpi_bus_get_device(handle, &device)) {
dev_printk(KERN_DEBUG, &device->dev,
"acpi device already exists; ignoring notify\n");
if (acpi_pci_disabled)
return;
}
if (acpi_bus_scan(handle))
acpi_handle_err(handle, "cannot add bridge to acpi list\n");
}
static void hotplug_event_root(void *data, u32 type)
{
acpi_handle handle = data;
struct acpi_pci_root *root;
acpi_scan_lock_acquire();
root = acpi_pci_find_root(handle);
switch (type) {
case ACPI_NOTIFY_BUS_CHECK:
/* bus enumerate */
acpi_handle_printk(KERN_DEBUG, handle,
"Bus check notify on %s\n", __func__);
if (root)
acpiphp_check_host_bridge(handle);
else
handle_root_bridge_insertion(handle);
break;
case ACPI_NOTIFY_DEVICE_CHECK:
/* device check */
acpi_handle_printk(KERN_DEBUG, handle,
"Device check notify on %s\n", __func__);
if (!root)
handle_root_bridge_insertion(handle);
break;
case ACPI_NOTIFY_EJECT_REQUEST:
/* request device eject */
acpi_handle_printk(KERN_DEBUG, handle,
"Device eject notify on %s\n", __func__);
if (!root)
break;
get_device(&root->device->dev);
acpi_scan_lock_release();
acpi_bus_device_eject(root->device, ACPI_NOTIFY_EJECT_REQUEST);
return;
default:
acpi_handle_warn(handle,
"notify_handler: unknown event type 0x%x\n",
type);
break;
}
acpi_scan_lock_release();
}
static void handle_hotplug_event_root(acpi_handle handle, u32 type,
void *context)
{
acpi_hotplug_execute(hotplug_event_root, handle, type);
}
static acpi_status __init
find_root_bridges(acpi_handle handle, u32 lvl, void *context, void **rv)
{
acpi_status status;
int *count = (int *)context;
if (!acpi_is_root_bridge(handle))
return AE_OK;
(*count)++;
status = acpi_install_notify_handler(handle, ACPI_SYSTEM_NOTIFY,
handle_hotplug_event_root, NULL);
if (ACPI_FAILURE(status))
acpi_handle_printk(KERN_DEBUG, handle,
"notify handler is not installed, exit status: %u\n",
(unsigned int)status);
else
acpi_handle_printk(KERN_DEBUG, handle,
"notify handler is installed\n");
return AE_OK;
}
void __init acpi_pci_root_hp_init(void)
{
int num = 0;
acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
ACPI_UINT32_MAX, find_root_bridges, NULL, &num, NULL);
printk(KERN_DEBUG "Found %d acpi root devices\n", num);
pci_acpi_crs_quirks();
acpi_scan_add_handler_with_hotplug(&pci_root_handler, "pci_root");
}

View file

@ -12,11 +12,12 @@
#include <linux/dmi.h>
#include <linux/nls.h>
#include <asm/pgtable.h>
#include "internal.h"
#define _COMPONENT ACPI_BUS_COMPONENT
ACPI_MODULE_NAME("scan");
#define STRUCT_TO_INT(s) (*((int*)&s))
extern struct acpi_device *acpi_root;
#define ACPI_BUS_CLASS "system_bus"
@ -25,6 +26,8 @@ extern struct acpi_device *acpi_root;
#define ACPI_IS_ROOT_DEVICE(device) (!(device)->parent)
#define INVALID_ACPI_HANDLE ((acpi_handle)empty_zero_page)
/*
* If set, devices will be hot-removed even if they cannot be put offline
* gracefully (from the kernel's standpoint).
@ -123,6 +126,26 @@ acpi_device_modalias_show(struct device *dev, struct device_attribute *attr, cha
}
static DEVICE_ATTR(modalias, 0444, acpi_device_modalias_show, NULL);
bool acpi_scan_is_offline(struct acpi_device *adev, bool uevent)
{
struct acpi_device_physical_node *pn;
bool offline = true;
mutex_lock(&adev->physical_node_lock);
list_for_each_entry(pn, &adev->physical_node_list, node)
if (device_supports_offline(pn->dev) && !pn->dev->offline) {
if (uevent)
kobject_uevent(&pn->dev->kobj, KOBJ_CHANGE);
offline = false;
break;
}
mutex_unlock(&adev->physical_node_lock);
return offline;
}
static acpi_status acpi_bus_offline(acpi_handle handle, u32 lvl, void *data,
void **ret_p)
{
@ -193,19 +216,11 @@ static acpi_status acpi_bus_online(acpi_handle handle, u32 lvl, void *data,
return AE_OK;
}
static int acpi_scan_hot_remove(struct acpi_device *device)
static int acpi_scan_try_to_offline(struct acpi_device *device)
{
acpi_handle handle = device->handle;
struct device *errdev;
struct device *errdev = NULL;
acpi_status status;
unsigned long long sta;
/* If there is no handle, the device node has been unregistered. */
if (!handle) {
dev_dbg(&device->dev, "ACPI handle missing\n");
put_device(&device->dev);
return -EINVAL;
}
/*
* Carry out two passes here and ignore errors in the first pass,
@ -216,7 +231,6 @@ static int acpi_scan_hot_remove(struct acpi_device *device)
*
* If the first pass is successful, the second one isn't needed, though.
*/
errdev = NULL;
status = acpi_walk_namespace(ACPI_TYPE_ANY, handle, ACPI_UINT32_MAX,
NULL, acpi_bus_offline, (void *)false,
(void **)&errdev);
@ -224,7 +238,6 @@ static int acpi_scan_hot_remove(struct acpi_device *device)
dev_warn(errdev, "Offline disabled.\n");
acpi_walk_namespace(ACPI_TYPE_ANY, handle, ACPI_UINT32_MAX,
acpi_bus_online, NULL, NULL, NULL);
put_device(&device->dev);
return -EPERM;
}
acpi_bus_offline(handle, 0, (void *)false, (void **)&errdev);
@ -243,20 +256,32 @@ static int acpi_scan_hot_remove(struct acpi_device *device)
acpi_walk_namespace(ACPI_TYPE_ANY, handle,
ACPI_UINT32_MAX, acpi_bus_online,
NULL, NULL, NULL);
put_device(&device->dev);
return -EBUSY;
}
}
return 0;
}
static int acpi_scan_hot_remove(struct acpi_device *device)
{
acpi_handle handle = device->handle;
unsigned long long sta;
acpi_status status;
if (device->handler->hotplug.demand_offline && !acpi_force_hot_remove) {
if (!acpi_scan_is_offline(device, true))
return -EBUSY;
} else {
int error = acpi_scan_try_to_offline(device);
if (error)
return error;
}
ACPI_DEBUG_PRINT((ACPI_DB_INFO,
"Hot-removing device %s...\n", dev_name(&device->dev)));
acpi_bus_trim(device);
/* Device node has been unregistered. */
put_device(&device->dev);
device = NULL;
acpi_evaluate_lck(handle, 0);
/*
* TBD: _EJD support.
@ -283,115 +308,127 @@ static int acpi_scan_hot_remove(struct acpi_device *device)
return 0;
}
void acpi_bus_device_eject(void *data, u32 ost_src)
static int acpi_scan_device_not_present(struct acpi_device *adev)
{
struct acpi_device *device = data;
acpi_handle handle = device->handle;
u32 ost_code = ACPI_OST_SC_NON_SPECIFIC_FAILURE;
int error;
lock_device_hotplug();
mutex_lock(&acpi_scan_lock);
if (ost_src == ACPI_NOTIFY_EJECT_REQUEST)
acpi_evaluate_hotplug_ost(handle, ACPI_NOTIFY_EJECT_REQUEST,
ACPI_OST_SC_EJECT_IN_PROGRESS, NULL);
if (device->handler && device->handler->hotplug.mode == AHM_CONTAINER)
kobject_uevent(&device->dev.kobj, KOBJ_OFFLINE);
error = acpi_scan_hot_remove(device);
if (error == -EPERM) {
goto err_support;
} else if (error) {
goto err_out;
if (!acpi_device_enumerated(adev)) {
dev_warn(&adev->dev, "Still not present\n");
return -EALREADY;
}
out:
mutex_unlock(&acpi_scan_lock);
unlock_device_hotplug();
return;
err_support:
ost_code = ACPI_OST_SC_EJECT_NOT_SUPPORTED;
err_out:
acpi_evaluate_hotplug_ost(handle, ost_src, ost_code, NULL);
goto out;
acpi_bus_trim(adev);
return 0;
}
static void acpi_scan_bus_device_check(void *data, u32 ost_source)
static int acpi_scan_device_check(struct acpi_device *adev)
{
acpi_handle handle = data;
struct acpi_device *device = NULL;
u32 ost_code = ACPI_OST_SC_NON_SPECIFIC_FAILURE;
int error;
lock_device_hotplug();
mutex_lock(&acpi_scan_lock);
if (ost_source != ACPI_NOTIFY_BUS_CHECK) {
acpi_bus_get_device(handle, &device);
if (device) {
dev_warn(&device->dev, "Attempt to re-insert\n");
goto out;
acpi_bus_get_status(adev);
if (adev->status.present || adev->status.functional) {
/*
* This function is only called for device objects for which
* matching scan handlers exist. The only situation in which
* the scan handler is not attached to this device object yet
* is when the device has just appeared (either it wasn't
* present at all before or it was removed and then added
* again).
*/
if (adev->handler) {
dev_warn(&adev->dev, "Already enumerated\n");
return -EALREADY;
}
error = acpi_bus_scan(adev->handle);
if (error) {
dev_warn(&adev->dev, "Namespace scan failure\n");
return error;
}
if (!adev->handler) {
dev_warn(&adev->dev, "Enumeration failure\n");
error = -ENODEV;
}
} else {
error = acpi_scan_device_not_present(adev);
}
error = acpi_bus_scan(handle);
if (error) {
acpi_handle_warn(handle, "Namespace scan failure\n");
goto out;
}
error = acpi_bus_get_device(handle, &device);
if (error) {
acpi_handle_warn(handle, "Missing device node object\n");
goto out;
}
ost_code = ACPI_OST_SC_SUCCESS;
if (device->handler && device->handler->hotplug.mode == AHM_CONTAINER)
kobject_uevent(&device->dev.kobj, KOBJ_ONLINE);
out:
acpi_evaluate_hotplug_ost(handle, ost_source, ost_code, NULL);
mutex_unlock(&acpi_scan_lock);
unlock_device_hotplug();
return error;
}
static void acpi_hotplug_unsupported(acpi_handle handle, u32 type)
static int acpi_scan_bus_check(struct acpi_device *adev)
{
u32 ost_status;
struct acpi_scan_handler *handler = adev->handler;
struct acpi_device *child;
int error;
switch (type) {
acpi_bus_get_status(adev);
if (!(adev->status.present || adev->status.functional)) {
acpi_scan_device_not_present(adev);
return 0;
}
if (handler && handler->hotplug.scan_dependent)
return handler->hotplug.scan_dependent(adev);
error = acpi_bus_scan(adev->handle);
if (error) {
dev_warn(&adev->dev, "Namespace scan failure\n");
return error;
}
list_for_each_entry(child, &adev->children, node) {
error = acpi_scan_bus_check(child);
if (error)
return error;
}
return 0;
}
static void acpi_device_hotplug(void *data, u32 src)
{
u32 ost_code = ACPI_OST_SC_NON_SPECIFIC_FAILURE;
struct acpi_device *adev = data;
int error;
lock_device_hotplug();
mutex_lock(&acpi_scan_lock);
/*
* The device object's ACPI handle cannot become invalid as long as we
* are holding acpi_scan_lock, but it may have become invalid before
* that lock was acquired.
*/
if (adev->handle == INVALID_ACPI_HANDLE)
goto out;
switch (src) {
case ACPI_NOTIFY_BUS_CHECK:
acpi_handle_debug(handle,
"ACPI_NOTIFY_BUS_CHECK event: unsupported\n");
ost_status = ACPI_OST_SC_INSERT_NOT_SUPPORTED;
error = acpi_scan_bus_check(adev);
break;
case ACPI_NOTIFY_DEVICE_CHECK:
acpi_handle_debug(handle,
"ACPI_NOTIFY_DEVICE_CHECK event: unsupported\n");
ost_status = ACPI_OST_SC_INSERT_NOT_SUPPORTED;
error = acpi_scan_device_check(adev);
break;
case ACPI_NOTIFY_EJECT_REQUEST:
acpi_handle_debug(handle,
"ACPI_NOTIFY_EJECT_REQUEST event: unsupported\n");
ost_status = ACPI_OST_SC_EJECT_NOT_SUPPORTED;
case ACPI_OST_EC_OSPM_EJECT:
error = acpi_scan_hot_remove(adev);
break;
default:
/* non-hotplug event; possibly handled by other handler */
return;
error = -EINVAL;
break;
}
if (!error)
ost_code = ACPI_OST_SC_SUCCESS;
acpi_evaluate_hotplug_ost(handle, type, ost_status, NULL);
out:
acpi_evaluate_hotplug_ost(adev->handle, src, ost_code, NULL);
put_device(&adev->dev);
mutex_unlock(&acpi_scan_lock);
unlock_device_hotplug();
}
static void acpi_hotplug_notify_cb(acpi_handle handle, u32 type, void *data)
{
u32 ost_code = ACPI_OST_SC_NON_SPECIFIC_FAILURE;
struct acpi_scan_handler *handler = data;
struct acpi_device *adev;
acpi_status status;
if (!handler->hotplug.enabled)
return acpi_hotplug_unsupported(handle, type);
if (acpi_bus_get_device(handle, &adev))
goto err_out;
switch (type) {
case ACPI_NOTIFY_BUS_CHECK:
@ -402,27 +439,27 @@ static void acpi_hotplug_notify_cb(acpi_handle handle, u32 type, void *data)
break;
case ACPI_NOTIFY_EJECT_REQUEST:
acpi_handle_debug(handle, "ACPI_NOTIFY_EJECT_REQUEST event\n");
if (acpi_bus_get_device(handle, &adev))
if (!handler->hotplug.enabled) {
acpi_handle_err(handle, "Eject disabled\n");
ost_code = ACPI_OST_SC_EJECT_NOT_SUPPORTED;
goto err_out;
get_device(&adev->dev);
status = acpi_hotplug_execute(acpi_bus_device_eject, adev, type);
if (ACPI_SUCCESS(status))
return;
put_device(&adev->dev);
goto err_out;
}
acpi_evaluate_hotplug_ost(handle, ACPI_NOTIFY_EJECT_REQUEST,
ACPI_OST_SC_EJECT_IN_PROGRESS, NULL);
break;
default:
/* non-hotplug event; possibly handled by other handler */
return;
}
status = acpi_hotplug_execute(acpi_scan_bus_device_check, handle, type);
get_device(&adev->dev);
status = acpi_hotplug_execute(acpi_device_hotplug, adev, type);
if (ACPI_SUCCESS(status))
return;
put_device(&adev->dev);
err_out:
acpi_evaluate_hotplug_ost(handle, type,
ACPI_OST_SC_NON_SPECIFIC_FAILURE, NULL);
acpi_evaluate_hotplug_ost(handle, type, ost_code, NULL);
}
static ssize_t real_power_state_show(struct device *dev,
@ -473,7 +510,7 @@ acpi_eject_store(struct device *d, struct device_attribute *attr,
acpi_evaluate_hotplug_ost(acpi_device->handle, ACPI_OST_EC_OSPM_EJECT,
ACPI_OST_SC_EJECT_IN_PROGRESS, NULL);
get_device(&acpi_device->dev);
status = acpi_hotplug_execute(acpi_bus_device_eject, acpi_device,
status = acpi_hotplug_execute(acpi_device_hotplug, acpi_device,
ACPI_OST_EC_OSPM_EJECT);
if (ACPI_SUCCESS(status))
return count;
@ -565,6 +602,20 @@ acpi_device_sun_show(struct device *dev, struct device_attribute *attr,
}
static DEVICE_ATTR(sun, 0444, acpi_device_sun_show, NULL);
static ssize_t status_show(struct device *dev, struct device_attribute *attr,
char *buf) {
struct acpi_device *acpi_dev = to_acpi_device(dev);
acpi_status status;
unsigned long long sta;
status = acpi_evaluate_integer(acpi_dev->handle, "_STA", NULL, &sta);
if (ACPI_FAILURE(status))
return -ENODEV;
return sprintf(buf, "%llu\n", sta);
}
static DEVICE_ATTR_RO(status);
static int acpi_device_setup_files(struct acpi_device *dev)
{
struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL};
@ -620,6 +671,12 @@ static int acpi_device_setup_files(struct acpi_device *dev)
dev->pnp.sun = (unsigned long)-1;
}
if (acpi_has_method(dev->handle, "_STA")) {
result = device_create_file(&dev->dev, &dev_attr_status);
if (result)
goto end;
}
/*
* If device has _EJ0, 'eject' file is created that is used to trigger
* hot-removal function from userland.
@ -675,6 +732,8 @@ static void acpi_device_remove_files(struct acpi_device *dev)
device_remove_file(&dev->dev, &dev_attr_adr);
device_remove_file(&dev->dev, &dev_attr_modalias);
device_remove_file(&dev->dev, &dev_attr_hid);
if (acpi_has_method(dev->handle, "_STA"))
device_remove_file(&dev->dev, &dev_attr_status);
if (dev->handle)
device_remove_file(&dev->dev, &dev_attr_path);
}
@ -905,9 +964,91 @@ struct bus_type acpi_bus_type = {
.uevent = acpi_device_uevent,
};
static void acpi_bus_data_handler(acpi_handle handle, void *context)
static void acpi_device_del(struct acpi_device *device)
{
/* Intentionally empty. */
mutex_lock(&acpi_device_lock);
if (device->parent)
list_del(&device->node);
list_del(&device->wakeup_list);
mutex_unlock(&acpi_device_lock);
acpi_power_add_remove_device(device, false);
acpi_device_remove_files(device);
if (device->remove)
device->remove(device);
device_del(&device->dev);
}
static LIST_HEAD(acpi_device_del_list);
static DEFINE_MUTEX(acpi_device_del_lock);
static void acpi_device_del_work_fn(struct work_struct *work_not_used)
{
for (;;) {
struct acpi_device *adev;
mutex_lock(&acpi_device_del_lock);
if (list_empty(&acpi_device_del_list)) {
mutex_unlock(&acpi_device_del_lock);
break;
}
adev = list_first_entry(&acpi_device_del_list,
struct acpi_device, del_list);
list_del(&adev->del_list);
mutex_unlock(&acpi_device_del_lock);
acpi_device_del(adev);
/*
* Drop references to all power resources that might have been
* used by the device.
*/
acpi_power_transition(adev, ACPI_STATE_D3_COLD);
put_device(&adev->dev);
}
}
/**
* acpi_scan_drop_device - Drop an ACPI device object.
* @handle: Handle of an ACPI namespace node, not used.
* @context: Address of the ACPI device object to drop.
*
* This is invoked by acpi_ns_delete_node() during the removal of the ACPI
* namespace node the device object pointed to by @context is attached to.
*
* The unregistration is carried out asynchronously to avoid running
* acpi_device_del() under the ACPICA's namespace mutex and the list is used to
* ensure the correct ordering (the device objects must be unregistered in the
* same order in which the corresponding namespace nodes are deleted).
*/
static void acpi_scan_drop_device(acpi_handle handle, void *context)
{
static DECLARE_WORK(work, acpi_device_del_work_fn);
struct acpi_device *adev = context;
mutex_lock(&acpi_device_del_lock);
/*
* Use the ACPI hotplug workqueue which is ordered, so this work item
* won't run after any hotplug work items submitted subsequently. That
* prevents attempts to register device objects identical to those being
* deleted from happening concurrently (such attempts result from
* hotplug events handled via the ACPI hotplug workqueue). It also will
* run after all of the work items submitted previosuly, which helps
* those work items to ensure that they are not accessing stale device
* objects.
*/
if (list_empty(&acpi_device_del_list))
acpi_queue_hotplug_work(&work);
list_add_tail(&adev->del_list, &acpi_device_del_list);
/* Make acpi_ns_validate_handle() return NULL for this handle. */
adev->handle = INVALID_ACPI_HANDLE;
mutex_unlock(&acpi_device_del_lock);
}
int acpi_bus_get_device(acpi_handle handle, struct acpi_device **device)
@ -917,7 +1058,7 @@ int acpi_bus_get_device(acpi_handle handle, struct acpi_device **device)
if (!device)
return -EINVAL;
status = acpi_get_data(handle, acpi_bus_data_handler, (void **)device);
status = acpi_get_data(handle, acpi_scan_drop_device, (void **)device);
if (ACPI_FAILURE(status) || !*device) {
ACPI_DEBUG_PRINT((ACPI_DB_INFO, "No context for object [%p]\n",
handle));
@ -937,7 +1078,7 @@ int acpi_device_add(struct acpi_device *device,
if (device->handle) {
acpi_status status;
status = acpi_attach_data(device->handle, acpi_bus_data_handler,
status = acpi_attach_data(device->handle, acpi_scan_drop_device,
device);
if (ACPI_FAILURE(status)) {
acpi_handle_err(device->handle,
@ -955,6 +1096,7 @@ int acpi_device_add(struct acpi_device *device,
INIT_LIST_HEAD(&device->node);
INIT_LIST_HEAD(&device->wakeup_list);
INIT_LIST_HEAD(&device->physical_node_list);
INIT_LIST_HEAD(&device->del_list);
mutex_init(&device->physical_node_lock);
new_bus_id = kzalloc(sizeof(struct acpi_device_bus_id), GFP_KERNEL);
@ -1018,37 +1160,10 @@ int acpi_device_add(struct acpi_device *device,
mutex_unlock(&acpi_device_lock);
err_detach:
acpi_detach_data(device->handle, acpi_bus_data_handler);
acpi_detach_data(device->handle, acpi_scan_drop_device);
return result;
}
static void acpi_device_unregister(struct acpi_device *device)
{
mutex_lock(&acpi_device_lock);
if (device->parent)
list_del(&device->node);
list_del(&device->wakeup_list);
mutex_unlock(&acpi_device_lock);
acpi_detach_data(device->handle, acpi_bus_data_handler);
acpi_power_add_remove_device(device, false);
acpi_device_remove_files(device);
if (device->remove)
device->remove(device);
device_del(&device->dev);
/*
* Transition the device to D3cold to drop the reference counts of all
* power resources the device depends on and turn off the ones that have
* no more references.
*/
acpi_device_set_power(device, ACPI_STATE_D3_COLD);
device->handle = NULL;
put_device(&device->dev);
}
/* --------------------------------------------------------------------------
Driver Management
-------------------------------------------------------------------------- */
@ -1622,11 +1737,13 @@ void acpi_init_device_object(struct acpi_device *device, acpi_handle handle,
device->device_type = type;
device->handle = handle;
device->parent = acpi_bus_get_parent(handle);
STRUCT_TO_INT(device->status) = sta;
acpi_set_device_status(device, sta);
acpi_device_get_busid(device);
acpi_set_pnp_ids(handle, &device->pnp, type);
acpi_bus_get_flags(device);
device->flags.match_driver = false;
device->flags.initialized = true;
device->flags.visited = false;
device_initialize(&device->dev);
dev_set_uevent_suppress(&device->dev, true);
}
@ -1711,6 +1828,15 @@ static int acpi_bus_type_and_status(acpi_handle handle, int *type,
return 0;
}
bool acpi_device_is_present(struct acpi_device *adev)
{
if (adev->status.present || adev->status.functional)
return true;
adev->flags.initialized = false;
return false;
}
static bool acpi_scan_handler_matching(struct acpi_scan_handler *handler,
char *idstr,
const struct acpi_device_id **matchid)
@ -1770,7 +1896,7 @@ static void acpi_scan_init_hotplug(acpi_handle handle, int type)
*/
list_for_each_entry(hwid, &pnp.ids, list) {
handler = acpi_scan_match_handler(hwid->id, NULL);
if (handler && !handler->hotplug.ignore) {
if (handler) {
acpi_install_notify_handler(handle, ACPI_SYSTEM_NOTIFY,
acpi_hotplug_notify_cb, handler);
break;
@ -1804,18 +1930,6 @@ static acpi_status acpi_bus_check_add(acpi_handle handle, u32 lvl_not_used,
acpi_scan_init_hotplug(handle, type);
if (!(sta & ACPI_STA_DEVICE_PRESENT) &&
!(sta & ACPI_STA_DEVICE_FUNCTIONING)) {
struct acpi_device_wakeup wakeup;
if (acpi_has_method(handle, "_PRW")) {
acpi_bus_extract_wakeup_device_power_package(handle,
&wakeup);
acpi_power_resources_list_free(&wakeup.resources);
}
return AE_CTRL_DEPTH;
}
acpi_add_single_object(&device, handle, type, sta);
if (!device)
return AE_CTRL_DEPTH;
@ -1850,36 +1964,40 @@ static int acpi_scan_attach_handler(struct acpi_device *device)
return ret;
}
static acpi_status acpi_bus_device_attach(acpi_handle handle, u32 lvl_not_used,
void *not_used, void **ret_not_used)
static void acpi_bus_attach(struct acpi_device *device)
{
struct acpi_device *device;
unsigned long long sta_not_used;
struct acpi_device *child;
int ret;
/*
* Ignore errors ignored by acpi_bus_check_add() to avoid terminating
* namespace walks prematurely.
*/
if (acpi_bus_type_and_status(handle, &ret, &sta_not_used))
return AE_OK;
if (acpi_bus_get_device(handle, &device))
return AE_CTRL_DEPTH;
acpi_bus_get_status(device);
/* Skip devices that are not present. */
if (!acpi_device_is_present(device)) {
device->flags.visited = false;
return;
}
if (device->handler)
return AE_OK;
goto ok;
if (!device->flags.initialized) {
acpi_bus_update_power(device, NULL);
device->flags.initialized = true;
}
device->flags.visited = false;
ret = acpi_scan_attach_handler(device);
if (ret < 0)
return AE_CTRL_DEPTH;
return;
device->flags.match_driver = true;
if (ret > 0)
return AE_OK;
if (!ret) {
ret = device_attach(&device->dev);
if (ret < 0)
return;
}
device->flags.visited = true;
ret = device_attach(&device->dev);
return ret >= 0 ? AE_OK : AE_CTRL_DEPTH;
ok:
list_for_each_entry(child, &device->children, node)
acpi_bus_attach(child);
}
/**
@ -1899,75 +2017,48 @@ static acpi_status acpi_bus_device_attach(acpi_handle handle, u32 lvl_not_used,
int acpi_bus_scan(acpi_handle handle)
{
void *device = NULL;
int error = 0;
if (ACPI_SUCCESS(acpi_bus_check_add(handle, 0, NULL, &device)))
acpi_walk_namespace(ACPI_TYPE_ANY, handle, ACPI_UINT32_MAX,
acpi_bus_check_add, NULL, NULL, &device);
if (!device)
error = -ENODEV;
else if (ACPI_SUCCESS(acpi_bus_device_attach(handle, 0, NULL, NULL)))
acpi_walk_namespace(ACPI_TYPE_ANY, handle, ACPI_UINT32_MAX,
acpi_bus_device_attach, NULL, NULL, NULL);
return error;
if (device) {
acpi_bus_attach(device);
return 0;
}
return -ENODEV;
}
EXPORT_SYMBOL(acpi_bus_scan);
static acpi_status acpi_bus_device_detach(acpi_handle handle, u32 lvl_not_used,
void *not_used, void **ret_not_used)
{
struct acpi_device *device = NULL;
if (!acpi_bus_get_device(handle, &device)) {
struct acpi_scan_handler *dev_handler = device->handler;
if (dev_handler) {
if (dev_handler->detach)
dev_handler->detach(device);
device->handler = NULL;
} else {
device_release_driver(&device->dev);
}
}
return AE_OK;
}
static acpi_status acpi_bus_remove(acpi_handle handle, u32 lvl_not_used,
void *not_used, void **ret_not_used)
{
struct acpi_device *device = NULL;
if (!acpi_bus_get_device(handle, &device))
acpi_device_unregister(device);
return AE_OK;
}
/**
* acpi_bus_trim - Remove ACPI device node and all of its descendants
* @start: Root of the ACPI device nodes subtree to remove.
* acpi_bus_trim - Detach scan handlers and drivers from ACPI device objects.
* @adev: Root of the ACPI namespace scope to walk.
*
* Must be called under acpi_scan_lock.
*/
void acpi_bus_trim(struct acpi_device *start)
void acpi_bus_trim(struct acpi_device *adev)
{
struct acpi_scan_handler *handler = adev->handler;
struct acpi_device *child;
list_for_each_entry_reverse(child, &adev->children, node)
acpi_bus_trim(child);
if (handler) {
if (handler->detach)
handler->detach(adev);
adev->handler = NULL;
} else {
device_release_driver(&adev->dev);
}
/*
* Execute acpi_bus_device_detach() as a post-order callback to detach
* all ACPI drivers from the device nodes being removed.
* Most likely, the device is going away, so put it into D3cold before
* that.
*/
acpi_walk_namespace(ACPI_TYPE_ANY, start->handle, ACPI_UINT32_MAX, NULL,
acpi_bus_device_detach, NULL, NULL);
acpi_bus_device_detach(start->handle, 0, NULL, NULL);
/*
* Execute acpi_bus_remove() as a post-order callback to remove device
* nodes in the given namespace scope.
*/
acpi_walk_namespace(ACPI_TYPE_ANY, start->handle, ACPI_UINT32_MAX, NULL,
acpi_bus_remove, NULL, NULL);
acpi_bus_remove(start->handle, 0, NULL, NULL);
acpi_device_set_power(adev, ACPI_STATE_D3_COLD);
adev->flags.initialized = false;
adev->flags.visited = false;
}
EXPORT_SYMBOL_GPL(acpi_bus_trim);
@ -2045,14 +2136,14 @@ int __init acpi_scan_init(void)
result = acpi_bus_scan_fixed();
if (result) {
acpi_device_unregister(acpi_root);
acpi_detach_data(acpi_root->handle, acpi_scan_drop_device);
acpi_device_del(acpi_root);
put_device(&acpi_root->dev);
goto out;
}
acpi_update_all_gpes();
acpi_pci_root_hp_init();
out:
mutex_unlock(&acpi_scan_lock);
return result;