Skip to content

Commit

Permalink
ACPI / hotplug: Introduce user space interface for hotplug profiles
Browse files Browse the repository at this point in the history
Introduce user space interface for manipulating hotplug profiles
associated with ACPI scan handlers.

The interface consists of sysfs directories under
/sys/firmware/acpi/hotplug/, one for each hotplug profile, containing
an attribute allowing user space to manipulate the enabled field of
the corresponding profile.  Namely, switching the enabled attribute
from '0' to '1' will cause the common hotplug notify handler to be
installed for all ACPI namespace objects representing devices matching
the scan handler associated with the given hotplug profile (and
analogously for the converse switch).

Drivers willing to use the new user space interface should add their
ACPI scan handlers with the help of new funtion
acpi_scan_add_handler_with_hotplug().

Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
Acked-by: Toshi Kani <toshi.kani@hp.com>
Tested-by: Toshi Kani <toshi.kani@hp.com>
  • Loading branch information
Rafael J. Wysocki committed Mar 4, 2013
1 parent 4b59cc1 commit 3f8055c
Show file tree
Hide file tree
Showing 5 changed files with 164 additions and 0 deletions.
26 changes: 26 additions & 0 deletions Documentation/ABI/testing/sysfs-firmware-acpi
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,32 @@ Description:
yoffset: The number of pixels between the top of the screen
and the top edge of the image.

What: /sys/firmware/acpi/hotplug/
Date: February 2013
Contact: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
Description:
There are separate hotplug profiles for different classes of
devices supported by ACPI, such as containers, memory modules,
processors, PCI root bridges etc. A hotplug profile for a given
class of devices is a collection of settings defining the way
that class of devices will be handled by the ACPI core hotplug
code. Those profiles are represented in sysfs as subdirectories
of /sys/firmware/acpi/hotplug/.

The following setting is available to user space for each
hotplug profile:

enabled: If set, the ACPI core will handle notifications of
hotplug events associated with the given class of
devices and will allow those devices to be ejected with
the help of the _EJ0 control method. Unsetting it
effectively disables hotplug for the correspoinding
class of devices.

The value of the above attribute is an integer number: 1 (set)
or 0 (unset). Attempts to write any other values to it will
cause -EINVAL to be returned.

What: /sys/firmware/acpi/interrupts/
Date: February 2008
Contact: Len Brown <lenb@kernel.org>
Expand Down
6 changes: 6 additions & 0 deletions drivers/acpi/internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,12 @@ void acpi_container_init(void);
static inline void acpi_container_init(void) {}
#endif

void acpi_sysfs_add_hotplug_profile(struct acpi_hotplug_profile *hotplug,
const char *name);
int acpi_scan_add_handler_with_hotplug(struct acpi_scan_handler *handler,
const char *hotplug_profile_name);
void acpi_scan_hotplug_enabled(struct acpi_hotplug_profile *hotplug, bool val);

#ifdef CONFIG_DEBUG_FS
extern struct dentry *acpi_debugfs_dir;
int acpi_debugfs_init(void);
Expand Down
59 changes: 59 additions & 0 deletions drivers/acpi/scan.c
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,19 @@ int acpi_scan_add_handler(struct acpi_scan_handler *handler)
return 0;
}

int acpi_scan_add_handler_with_hotplug(struct acpi_scan_handler *handler,
const char *hotplug_profile_name)
{
int error;

error = acpi_scan_add_handler(handler);
if (error)
return error;

acpi_sysfs_add_hotplug_profile(&handler->hotplug, hotplug_profile_name);
return 0;
}

/*
* Creates hid/cid(s) string needed for modalias and uevent
* e.g. on a device with hid:IBM0001 and cid:ACPI0001 you get:
Expand Down Expand Up @@ -1690,6 +1703,52 @@ static bool acpi_scan_handler_matching(struct acpi_scan_handler *handler,
return false;
}

static acpi_status acpi_scan_hotplug_modify(acpi_handle handle,
u32 lvl_not_used, void *data,
void **ret_not_used)
{
struct acpi_scan_handler *handler = data;
struct acpi_device_info *info;
bool match = false;

if (ACPI_FAILURE(acpi_get_object_info(handle, &info)))
return AE_OK;

if (info->valid & ACPI_VALID_HID) {
char *idstr = info->hardware_id.string;
match = acpi_scan_handler_matching(handler, idstr, NULL);
}
kfree(info);
if (!match)
return AE_OK;

if (handler->hotplug.enabled)
acpi_install_notify_handler(handle, ACPI_SYSTEM_NOTIFY,
acpi_hotplug_notify_cb, NULL);
else
acpi_remove_notify_handler(handle, ACPI_SYSTEM_NOTIFY,
acpi_hotplug_notify_cb);

return AE_OK;
}

void acpi_scan_hotplug_enabled(struct acpi_hotplug_profile *hotplug, bool val)
{
struct acpi_scan_handler *handler;

if (!!hotplug->enabled == !!val)
return;

mutex_lock(&acpi_scan_lock);

hotplug->enabled = val;
handler = container_of(hotplug, struct acpi_scan_handler, hotplug);
acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT, ACPI_UINT32_MAX,
acpi_scan_hotplug_modify, NULL, handler, NULL);

mutex_unlock(&acpi_scan_lock);
}

static struct acpi_scan_handler *acpi_scan_match_handler(char *idstr,
const struct acpi_device_id **matchid)
{
Expand Down
66 changes: 66 additions & 0 deletions drivers/acpi/sysfs.c
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
#include <linux/moduleparam.h>
#include <acpi/acpi_drivers.h>

#include "internal.h"

#define _COMPONENT ACPI_SYSTEM_COMPONENT
ACPI_MODULE_NAME("sysfs");

Expand Down Expand Up @@ -249,6 +251,7 @@ module_param_call(acpica_version, NULL, param_get_acpica_version, NULL, 0444);
static LIST_HEAD(acpi_table_attr_list);
static struct kobject *tables_kobj;
static struct kobject *dynamic_tables_kobj;
static struct kobject *hotplug_kobj;

struct acpi_table_attr {
struct bin_attribute attr;
Expand Down Expand Up @@ -716,13 +719,76 @@ acpi_show_profile(struct device *dev, struct device_attribute *attr,
static const struct device_attribute pm_profile_attr =
__ATTR(pm_profile, S_IRUGO, acpi_show_profile, NULL);

static ssize_t hotplug_enabled_show(struct kobject *kobj,
struct kobj_attribute *attr, char *buf)
{
struct acpi_hotplug_profile *hotplug = to_acpi_hotplug_profile(kobj);

return sprintf(buf, "%d\n", hotplug->enabled);
}

static ssize_t hotplug_enabled_store(struct kobject *kobj,
struct kobj_attribute *attr,
const char *buf, size_t size)
{
struct acpi_hotplug_profile *hotplug = to_acpi_hotplug_profile(kobj);
unsigned int val;

if (kstrtouint(buf, 10, &val) || val > 1)
return -EINVAL;

acpi_scan_hotplug_enabled(hotplug, val);
return size;
}

static struct kobj_attribute hotplug_enabled_attr =
__ATTR(enabled, S_IRUGO | S_IWUSR, hotplug_enabled_show,
hotplug_enabled_store);

static struct attribute *hotplug_profile_attrs[] = {
&hotplug_enabled_attr.attr,
NULL
};

struct kobj_type acpi_hotplug_profile_ktype = {
.sysfs_ops = &kobj_sysfs_ops,
.default_attrs = hotplug_profile_attrs,
};

void acpi_sysfs_add_hotplug_profile(struct acpi_hotplug_profile *hotplug,
const char *name)
{
int error;

if (!hotplug_kobj)
goto err_out;

kobject_init(&hotplug->kobj, &acpi_hotplug_profile_ktype);
error = kobject_set_name(&hotplug->kobj, "%s", name);
if (error)
goto err_out;

hotplug->kobj.parent = hotplug_kobj;
error = kobject_add(&hotplug->kobj, hotplug_kobj, NULL);
if (error)
goto err_out;

kobject_uevent(&hotplug->kobj, KOBJ_ADD);
return;

err_out:
pr_err(PREFIX "Unable to add hotplug profile '%s'\n", name);
}

int __init acpi_sysfs_init(void)
{
int result;

result = acpi_tables_sysfs_init();
if (result)
return result;

hotplug_kobj = kobject_create_and_add("hotplug", acpi_kobj);
result = sysfs_create_file(acpi_kobj, &pm_profile_attr.attr);
return result;
}
7 changes: 7 additions & 0 deletions include/acpi/acpi_bus.h
Original file line number Diff line number Diff line change
Expand Up @@ -95,10 +95,17 @@ enum acpi_hotplug_mode {
};

struct acpi_hotplug_profile {
struct kobject kobj;
bool enabled:1;
enum acpi_hotplug_mode mode;
};

static inline struct acpi_hotplug_profile *to_acpi_hotplug_profile(
struct kobject *kobj)
{
return container_of(kobj, struct acpi_hotplug_profile, kobj);
}

struct acpi_scan_handler {
const struct acpi_device_id *ids;
struct list_head list_node;
Expand Down

0 comments on commit 3f8055c

Please sign in to comment.