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 657 additions and 632 deletions

View File

@ -235,10 +235,6 @@ Wysocki <rafael.j.wysocki@intel.com>.
named object's type in the second column). In that case the object's
directory in sysfs will contain the 'path' attribute whose value is
the full path to the node from the namespace root.
struct acpi_device objects are created for the ACPI namespace nodes
whose _STA control methods return PRESENT or FUNCTIONING. The power
resource nodes or nodes without _STA are assumed to be both PRESENT
and FUNCTIONING.
F:
The struct acpi_device object is created for a fixed hardware
feature (as indicated by the fixed feature flag's name in the second
@ -340,7 +336,7 @@ Wysocki <rafael.j.wysocki@intel.com>.
| +-------------+-------+----------------+
| |
| | +- - - - - - - +- - - - - - +- - - - - - - -+
| +-| * PNP0C0D:00 | \_SB_.LID0 | acpi:PNP0C0D: |
| +-| PNP0C0D:00 | \_SB_.LID0 | acpi:PNP0C0D: |
| | +- - - - - - - +- - - - - - +- - - - - - - -+
| |
| | +------------+------------+-----------------------+
@ -390,6 +386,3 @@ Wysocki <rafael.j.wysocki@intel.com>.
attribute (as described earlier in this document).
NOTE: N/A indicates the device object does not have the 'path' or the
'modalias' attribute.
NOTE: The PNP0C0D device listed above is highlighted (marked by "*")
to indicate it will be created only when its _STA methods return
PRESENT or FUNCTIONING.

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;
/* 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)
if (check_children && list_empty(&adev->children))
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;
struct acpi_device *adev, *ret = NULL;
int ret_score = 0;
if (!parent)
return NULL;
list_for_each_entry(adev, &parent->children, node) {
unsigned long long addr;
acpi_status status;
int score;
status = acpi_evaluate_integer(handle, METHOD_NAME__ADR, NULL, &addr);
if (ACPI_FAILURE(status) || addr != context->addr)
return AE_OK;
status = acpi_evaluate_integer(adev->handle, METHOD_NAME__ADR,
NULL, &addr);
if (ACPI_FAILURE(status) || addr != address)
continue;
if (!context->ret) {
/* This is the first matching object. Save its handle. */
context->ret = handle;
return AE_OK;
if (!ret) {
/* This is the first matching object. Save it. */
ret = adev;
continue;
}
/*
* 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.
* 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
* its handle if so. Second, check the same for the object that we've
* just found.
* 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 (!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;
if (!ret_score) {
ret_score = find_child_checks(ret, check_children);
if (ret_score == FIND_CHILD_MAX_SCORE)
return ret;
}
score = do_find_child_checks(handle, context->is_bridge);
score = find_child_checks(adev, check_children);
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 adev;
} else if (score > ret_score) {
ret = adev;
ret_score = score;
}
return AE_OK;
}
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 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)
return;
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");
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);
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)
{
if (!acpi_device_enumerated(adev)) {
dev_warn(&adev->dev, "Still not present\n");
return -EALREADY;
}
acpi_bus_trim(adev);
return 0;
}
static int acpi_scan_device_check(struct acpi_device *adev)
{
int error;
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);
}
return error;
}
static int acpi_scan_bus_check(struct acpi_device *adev)
{
struct acpi_scan_handler *handler = adev->handler;
struct acpi_device *child;
int error;
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)
{
struct acpi_device *device = data;
acpi_handle handle = device->handle;
u32 ost_code = ACPI_OST_SC_NON_SPECIFIC_FAILURE;
struct acpi_device *adev = data;
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;
}
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);
/*
* 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;
}
static void acpi_scan_bus_device_check(void *data, u32 ost_source)
{
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;
}
}
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();
}
static void acpi_hotplug_unsupported(acpi_handle handle, u32 type)
{
u32 ost_status;
switch (type) {
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))
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);
if (!handler->hotplug.enabled) {
acpi_handle_err(handle, "Eject disabled\n");
ost_code = ACPI_OST_SC_EJECT_NOT_SUPPORTED;
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);
return ret >= 0 ? AE_OK : AE_CTRL_DEPTH;
if (ret < 0)
return;
}
device->flags.visited = true;
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;

View File

@ -178,12 +178,12 @@ static const struct acpi_dock_ops ata_acpi_ap_dock_ops = {
/* bind acpi handle to pata port */
void ata_acpi_bind_port(struct ata_port *ap)
{
acpi_handle host_handle = ACPI_HANDLE(ap->host->dev);
struct acpi_device *host_companion = ACPI_COMPANION(ap->host->dev);
if (libata_noacpi || ap->flags & ATA_FLAG_ACPI_SATA || !host_handle)
if (libata_noacpi || ap->flags & ATA_FLAG_ACPI_SATA || !host_companion)
return;
acpi_preset_companion(&ap->tdev, host_handle, ap->port_no);
acpi_preset_companion(&ap->tdev, host_companion, ap->port_no);
if (ata_acpi_gtm(ap, &ap->__acpi_init_gtm) == 0)
ap->pflags |= ATA_PFLAG_INIT_GTM_VALID;
@ -196,17 +196,17 @@ void ata_acpi_bind_port(struct ata_port *ap)
void ata_acpi_bind_dev(struct ata_device *dev)
{
struct ata_port *ap = dev->link->ap;
acpi_handle port_handle = ACPI_HANDLE(&ap->tdev);
acpi_handle host_handle = ACPI_HANDLE(ap->host->dev);
acpi_handle parent_handle;
struct acpi_device *port_companion = ACPI_COMPANION(&ap->tdev);
struct acpi_device *host_companion = ACPI_COMPANION(ap->host->dev);
struct acpi_device *parent;
u64 adr;
/*
* For both sata/pata devices, host handle is required.
* For pata device, port handle is also required.
* For both sata/pata devices, host companion device is required.
* For pata device, port companion device is also required.
*/
if (libata_noacpi || !host_handle ||
(!(ap->flags & ATA_FLAG_ACPI_SATA) && !port_handle))
if (libata_noacpi || !host_companion ||
(!(ap->flags & ATA_FLAG_ACPI_SATA) && !port_companion))
return;
if (ap->flags & ATA_FLAG_ACPI_SATA) {
@ -214,13 +214,13 @@ void ata_acpi_bind_dev(struct ata_device *dev)
adr = SATA_ADR(ap->port_no, NO_PORT_MULT);
else
adr = SATA_ADR(ap->port_no, dev->link->pmp);
parent_handle = host_handle;
parent = host_companion;
} else {
adr = dev->devno;
parent_handle = port_handle;
parent = port_companion;
}
acpi_preset_companion(&dev->tdev, parent_handle, adr);
acpi_preset_companion(&dev->tdev, parent, adr);
register_hotplug_dock_device(ata_dev_acpi_handle(dev),
&ata_acpi_dev_dock_ops, dev, NULL, NULL);

View File

@ -4,7 +4,7 @@ obj-y := core.o bus.o dd.o syscore.o \
driver.o class.o platform.o \
cpu.o firmware.o init.o map.o devres.o \
attribute_container.o transport_class.o \
topology.o
topology.o container.o
obj-$(CONFIG_DEVTMPFS) += devtmpfs.o
obj-$(CONFIG_DMA_CMA) += dma-contiguous.o
obj-y += power/

View File

@ -100,6 +100,7 @@ static inline int hypervisor_init(void) { return 0; }
#endif
extern int platform_bus_init(void);
extern void cpu_dev_init(void);
extern void container_dev_init(void);
struct kobject *virtual_device_parent(struct device *dev);

44
drivers/base/container.c Normal file
View File

@ -0,0 +1,44 @@
/*
* System bus type for containers.
*
* Copyright (C) 2013, Intel Corporation
* Author: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/container.h>
#include "base.h"
#define CONTAINER_BUS_NAME "container"
static int trivial_online(struct device *dev)
{
return 0;
}
static int container_offline(struct device *dev)
{
struct container_dev *cdev = to_container_dev(dev);
return cdev->offline ? cdev->offline(cdev) : 0;
}
struct bus_type container_subsys = {
.name = CONTAINER_BUS_NAME,
.dev_name = CONTAINER_BUS_NAME,
.online = trivial_online,
.offline = container_offline,
};
void __init container_dev_init(void)
{
int ret;
ret = subsys_system_register(&container_subsys, NULL);
if (ret)
pr_err("%s() failed: %d\n", __func__, ret);
}

View File

@ -33,4 +33,5 @@ void __init driver_init(void)
platform_bus_init();
cpu_dev_init();
memory_dev_init();
container_dev_init();
}

View File

@ -97,6 +97,17 @@ bool ide_port_acpi(ide_hwif_t *hwif)
return ide_noacpi == 0 && hwif->acpidata;
}
static acpi_handle acpi_get_child(acpi_handle handle, u64 addr)
{
struct acpi_device *adev;
if (!handle || acpi_bus_get_device(handle, &adev))
return NULL;
adev = acpi_find_child_device(adev, addr, false);
return adev ? adev->handle : NULL;
}
/**
* ide_get_dev_handle - finds acpi_handle and PCI device.function
* @dev: device to locate

View File

@ -308,7 +308,7 @@ static void sdio_acpi_set_handle(struct sdio_func *func)
struct mmc_host *host = func->card->host;
u64 addr = (host->slotno << 16) | func->num;
acpi_preset_companion(&func->dev, ACPI_HANDLE(host->parent), addr);
acpi_preset_companion(&func->dev, ACPI_COMPANION(host->parent), addr);
}
#else
static inline void sdio_acpi_set_handle(struct sdio_func *func) {}

View File

@ -491,7 +491,7 @@ static void acpiphp_bus_add(acpi_handle handle)
acpi_bus_scan(handle);
acpi_bus_get_device(handle, &adev);
if (adev)
if (acpi_device_enumerated(adev))
acpi_device_set_power(adev, ACPI_STATE_D0);
}

View File

@ -303,10 +303,10 @@ void acpi_pci_remove_bus(struct pci_bus *bus)
}
/* ACPI bus type */
static int acpi_pci_find_device(struct device *dev, acpi_handle *handle)
static struct acpi_device *acpi_pci_find_companion(struct device *dev)
{
struct pci_dev *pci_dev = to_pci_dev(dev);
bool is_bridge;
bool check_children;
u64 addr;
/*
@ -314,14 +314,12 @@ static int acpi_pci_find_device(struct device *dev, acpi_handle *handle)
* is set only after acpi_pci_find_device() has been called for the
* given device.
*/
is_bridge = pci_dev->hdr_type == PCI_HEADER_TYPE_BRIDGE
check_children = pci_dev->hdr_type == PCI_HEADER_TYPE_BRIDGE
|| pci_dev->hdr_type == PCI_HEADER_TYPE_CARDBUS;
/* Please ref to ACPI spec for the syntax of _ADR */
addr = (PCI_SLOT(pci_dev->devfn) << 16) | PCI_FUNC(pci_dev->devfn);
*handle = acpi_find_child(ACPI_HANDLE(dev->parent), addr, is_bridge);
if (!*handle)
return -ENODEV;
return 0;
return acpi_find_child_device(ACPI_COMPANION(dev->parent), addr,
check_children);
}
static void pci_acpi_setup(struct device *dev)
@ -364,7 +362,7 @@ static bool pci_acpi_bus_match(struct device *dev)
static struct acpi_bus_type acpi_pci_bus = {
.name = "PCI",
.match = pci_acpi_bus_match,
.find_device = acpi_pci_find_device,
.find_companion = acpi_pci_find_companion,
.setup = pci_acpi_setup,
.cleanup = pci_acpi_cleanup,
};

View File

@ -328,20 +328,15 @@ static int __init acpi_pnp_match(struct device *dev, void *_pnp)
&& compare_pnp_id(pnp->id, acpi_device_hid(acpi));
}
static int __init acpi_pnp_find_device(struct device *dev, acpi_handle * handle)
static struct acpi_device * __init acpi_pnp_find_companion(struct device *dev)
{
struct device *adev;
struct acpi_device *acpi;
dev = bus_find_device(&acpi_bus_type, NULL, to_pnp_dev(dev),
acpi_pnp_match);
if (!dev)
return NULL;
adev = bus_find_device(&acpi_bus_type, NULL,
to_pnp_dev(dev), acpi_pnp_match);
if (!adev)
return -ENODEV;
acpi = to_acpi_device(adev);
*handle = acpi->handle;
put_device(adev);
return 0;
put_device(dev);
return to_acpi_device(dev);
}
/* complete initialization of a PNPACPI device includes having
@ -355,7 +350,7 @@ static bool acpi_pnp_bus_match(struct device *dev)
static struct acpi_bus_type __initdata acpi_pnp_bus = {
.name = "PNP",
.match = acpi_pnp_bus_match,
.find_device = acpi_pnp_find_device,
.find_companion = acpi_pnp_find_companion,
};
int pnpacpi_disabled __initdata;

View File

@ -126,7 +126,7 @@ out:
return ret;
}
static int usb_acpi_find_device(struct device *dev, acpi_handle *handle)
static struct acpi_device *usb_acpi_find_companion(struct device *dev)
{
struct usb_device *udev;
acpi_handle *parent_handle;
@ -168,16 +168,15 @@ static int usb_acpi_find_device(struct device *dev, acpi_handle *handle)
break;
}
return -ENODEV;
return NULL;
}
/* root hub's parent is the usb hcd. */
parent_handle = ACPI_HANDLE(dev->parent);
*handle = acpi_get_child(parent_handle, udev->portnum);
if (!*handle)
return -ENODEV;
return 0;
return acpi_find_child_device(ACPI_COMPANION(dev->parent),
udev->portnum, false);
} else if (is_usb_port(dev)) {
struct acpi_device *adev = NULL;
sscanf(dev_name(dev), "port%d", &port_num);
/* Get the struct usb_device point of port's hub */
udev = to_usb_device(dev->parent->parent);
@ -193,26 +192,27 @@ static int usb_acpi_find_device(struct device *dev, acpi_handle *handle)
raw_port_num = usb_hcd_find_raw_port_number(hcd,
port_num);
*handle = acpi_get_child(ACPI_HANDLE(&udev->dev),
raw_port_num);
if (!*handle)
return -ENODEV;
adev = acpi_find_child_device(ACPI_COMPANION(&udev->dev),
raw_port_num, false);
if (!adev)
return NULL;
} else {
parent_handle =
usb_get_hub_port_acpi_handle(udev->parent,
udev->portnum);
if (!parent_handle)
return -ENODEV;
return NULL;
*handle = acpi_get_child(parent_handle, port_num);
if (!*handle)
return -ENODEV;
acpi_bus_get_device(parent_handle, &adev);
adev = acpi_find_child_device(adev, port_num, false);
if (!adev)
return NULL;
}
usb_acpi_check_port_connect_type(udev, adev->handle, port_num);
return adev;
}
usb_acpi_check_port_connect_type(udev, *handle, port_num);
} else
return -ENODEV;
return 0;
return NULL;
}
static bool usb_acpi_bus_match(struct device *dev)
@ -223,7 +223,7 @@ static bool usb_acpi_bus_match(struct device *dev)
static struct acpi_bus_type usb_acpi_bus = {
.name = "USB",
.match = usb_acpi_bus_match,
.find_device = usb_acpi_find_device,
.find_companion = usb_acpi_find_companion,
};
int usb_acpi_register(void)

View File

@ -266,7 +266,8 @@ static void acpi_processor_hotplug_notify(acpi_handle handle,
if (!is_processor_present(handle))
break;
if (!acpi_bus_get_device(handle, &device))
acpi_bus_get_device(handle, &device);
if (acpi_device_enumerated(device))
break;
result = acpi_bus_scan(handle);
@ -274,8 +275,9 @@ static void acpi_processor_hotplug_notify(acpi_handle handle,
pr_err(PREFIX "Unable to add the device\n");
break;
}
result = acpi_bus_get_device(handle, &device);
if (result) {
device = NULL;
acpi_bus_get_device(handle, &device);
if (!acpi_device_enumerated(device)) {
pr_err(PREFIX "Missing device object\n");
break;
}

View File

@ -168,7 +168,7 @@ static int acpi_memory_get_device(acpi_handle handle,
acpi_scan_lock_acquire();
acpi_bus_get_device(handle, &device);
if (device)
if (acpi_device_enumerated(device))
goto end;
/*
@ -181,8 +181,9 @@ static int acpi_memory_get_device(acpi_handle handle,
result = -EINVAL;
goto out;
}
result = acpi_bus_get_device(handle, &device);
if (result) {
device = NULL;
acpi_bus_get_device(handle, &device);
if (!acpi_device_enumerated(device)) {
pr_warn(PREFIX "Missing device object\n");
result = -EINVAL;
goto out;

View File

@ -89,17 +89,11 @@ struct acpi_device;
* -----------------
*/
enum acpi_hotplug_mode {
AHM_GENERIC = 0,
AHM_CONTAINER,
AHM_COUNT
};
struct acpi_hotplug_profile {
struct kobject kobj;
int (*scan_dependent)(struct acpi_device *adev);
bool enabled:1;
bool ignore:1;
enum acpi_hotplug_mode mode;
bool demand_offline:1;
};
static inline struct acpi_hotplug_profile *to_acpi_hotplug_profile(
@ -167,8 +161,10 @@ struct acpi_device_flags {
u32 ejectable:1;
u32 power_manageable:1;
u32 match_driver:1;
u32 initialized:1;
u32 visited:1;
u32 no_hotplug:1;
u32 reserved:26;
u32 reserved:24;
};
/* File System */
@ -298,6 +294,7 @@ struct acpi_device {
struct list_head children;
struct list_head node;
struct list_head wakeup_list;
struct list_head del_list;
struct acpi_device_status status;
struct acpi_device_flags flags;
struct acpi_device_pnp pnp;
@ -323,6 +320,11 @@ static inline void *acpi_driver_data(struct acpi_device *d)
#define to_acpi_device(d) container_of(d, struct acpi_device, dev)
#define to_acpi_driver(d) container_of(d, struct acpi_driver, drv)
static inline void acpi_set_device_status(struct acpi_device *adev, u32 sta)
{
*((u32 *)&adev->status) = sta;
}
/* acpi_device.dev.bus == &acpi_bus_type */
extern struct bus_type acpi_bus_type;
@ -385,6 +387,11 @@ int acpi_match_device_ids(struct acpi_device *device,
int acpi_create_dir(struct acpi_device *);
void acpi_remove_dir(struct acpi_device *);
static inline bool acpi_device_enumerated(struct acpi_device *adev)
{
return adev && adev->flags.initialized && adev->flags.visited;
}
typedef void (*acpi_hp_callback)(void *data, u32 src);
acpi_status acpi_hotplug_execute(acpi_hp_callback func, void *data, u32 src);
@ -408,7 +415,7 @@ struct acpi_bus_type {
struct list_head list;
const char *name;
bool (*match)(struct device *dev);
int (*find_device) (struct device *, acpi_handle *);
struct acpi_device * (*find_companion)(struct device *);
void (*setup)(struct device *);
void (*cleanup)(struct device *);
};
@ -427,12 +434,9 @@ struct acpi_pci_root {
};
/* helper */
acpi_handle acpi_find_child(acpi_handle, u64, bool);
static inline acpi_handle acpi_get_child(acpi_handle handle, u64 addr)
{
return acpi_find_child(handle, addr, false);
}
void acpi_preset_companion(struct device *dev, acpi_handle parent, u64 addr);
struct acpi_device *acpi_find_child_device(struct acpi_device *parent,
u64 address, bool check_children);
int acpi_is_root_bridge(acpi_handle);
struct acpi_pci_root *acpi_pci_find_root(acpi_handle handle);

View File

@ -54,6 +54,12 @@ static inline acpi_handle acpi_device_handle(struct acpi_device *adev)
#define ACPI_COMPANION_SET(dev, adev) ACPI_COMPANION(dev) = (adev)
#define ACPI_HANDLE(dev) acpi_device_handle(ACPI_COMPANION(dev))
static inline void acpi_preset_companion(struct device *dev,
struct acpi_device *parent, u64 addr)
{
ACPI_COMPANION_SET(dev, acpi_find_child_device(parent, addr, NULL));
}
static inline const char *acpi_dev_name(struct acpi_device *adev)
{
return dev_name(&adev->dev);

25
include/linux/container.h Normal file
View File

@ -0,0 +1,25 @@
/*
* Definitions for container bus type.
*
* Copyright (C) 2013, Intel Corporation
* Author: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/device.h>
/* drivers/base/power/container.c */
extern struct bus_type container_subsys;
struct container_dev {
struct device dev;
int (*offline)(struct container_dev *cdev);
};
static inline struct container_dev *to_container_dev(struct device *dev)
{
return container_of(dev, struct container_dev, dev);
}