Skip to content

Commit

Permalink
Merge branch 'acpi-general'
Browse files Browse the repository at this point in the history
* acpi-general: (38 commits)
  ACPI / thermal: _TMP and _CRT/_HOT/_PSV/_ACx dependency fix
  ACPI: drop unnecessary local variable from acpi_system_write_wakeup_device()
  ACPI: Fix logging when no pci_irq is allocated
  ACPI: Update Dock hotplug error messages
  ACPI: Update Container hotplug error messages
  ACPI: Update Memory hotplug error messages
  ACPI: Update CPU hotplug error messages
  ACPI: Add acpi_handle_<level>() interfaces
  ACPI: remove use of __devexit
  ACPI / PM: Add Sony Vaio VPCEB1S1E to nonvs blacklist.
  ACPI / battery: Correct battery capacity values on Thinkpads
  Revert "ACPI / x86: Add quirk for "CheckPoint P-20-00" to not use bridge _CRS_ info"
  ACPI: create _SUN sysfs file
  ACPI / memhotplug: bind the memory device when the driver is being loaded
  ACPI / memhotplug: don't allow to eject the memory device if it is being used
  ACPI / memhotplug: free memory device if acpi_memory_enable_device() failed
  ACPI / memhotplug: fix memory leak when memory device is unbound from acpi_memhotplug
  ACPI / memhotplug: deal with eject request in hotplug queue
  ACPI / memory-hotplug: add memory offline code to acpi_memory_device_remove()
  ACPI / memory-hotplug: call acpi_bus_trim() to remove memory device
  ...

Conflicts:
	include/linux/acpi.h (two additions at the end of the same file)
  • Loading branch information
Rafael J. Wysocki committed Nov 29, 2012
2 parents 08ab729 + 261cba2 commit d4c091f
Show file tree
Hide file tree
Showing 23 changed files with 566 additions and 261 deletions.
14 changes: 14 additions & 0 deletions Documentation/ABI/testing/sysfs-devices-sun
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
Whatt: /sys/devices/.../sun
Date: October 2012
Contact: Yasuaki Ishimatsu <isimatu.yasuaki@jp.fujitsu.com>
Description:
The file contains a Slot-unique ID which provided by the _SUN
method in the ACPI namespace. The value is written in Advanced
Configuration and Power Interface Specification as follows:

"The _SUN value is required to be unique among the slots of
the same type. It is also recommended that this number match
the slot number printed on the physical slot whenever possible."

So reading the sysfs file, we can identify a physical position
of the slot in the system.
2 changes: 2 additions & 0 deletions arch/x86/kernel/acpi/sleep.c
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,8 @@ static int __init acpi_sleep_setup(char *str)
#endif
if (strncmp(str, "nonvs", 5) == 0)
acpi_nvs_nosave();
if (strncmp(str, "nonvs_s3", 8) == 0)
acpi_nvs_nosave_s3();
if (strncmp(str, "old_ordering", 12) == 0)
acpi_old_suspend_ordering();
str = strchr(str, ',');
Expand Down
193 changes: 86 additions & 107 deletions drivers/acpi/acpi_memhotplug.c
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
#include <linux/types.h>
#include <linux/memory_hotplug.h>
#include <linux/slab.h>
#include <linux/acpi.h>
#include <acpi/acpi_drivers.h>

#define ACPI_MEMORY_DEVICE_CLASS "memory"
Expand Down Expand Up @@ -78,6 +79,7 @@ struct acpi_memory_info {
unsigned short caching; /* memory cache attribute */
unsigned short write_protect; /* memory read/write attribute */
unsigned int enabled:1;
unsigned int failed:1;
};

struct acpi_memory_device {
Expand All @@ -86,8 +88,6 @@ struct acpi_memory_device {
struct list_head res_list;
};

static int acpi_hotmem_initialized;

static acpi_status
acpi_memory_get_resource(struct acpi_resource *resource, void *context)
{
Expand Down Expand Up @@ -125,22 +125,28 @@ acpi_memory_get_resource(struct acpi_resource *resource, void *context)
return AE_OK;
}

static void
acpi_memory_free_device_resources(struct acpi_memory_device *mem_device)
{
struct acpi_memory_info *info, *n;

list_for_each_entry_safe(info, n, &mem_device->res_list, list)
kfree(info);
INIT_LIST_HEAD(&mem_device->res_list);
}

static int
acpi_memory_get_device_resources(struct acpi_memory_device *mem_device)
{
acpi_status status;
struct acpi_memory_info *info, *n;


if (!list_empty(&mem_device->res_list))
return 0;

status = acpi_walk_resources(mem_device->device->handle, METHOD_NAME__CRS,
acpi_memory_get_resource, mem_device);
if (ACPI_FAILURE(status)) {
list_for_each_entry_safe(info, n, &mem_device->res_list, list)
kfree(info);
INIT_LIST_HEAD(&mem_device->res_list);
acpi_memory_free_device_resources(mem_device);
return -EINVAL;
}

Expand Down Expand Up @@ -170,7 +176,7 @@ acpi_memory_get_device(acpi_handle handle,
/* Get the parent device */
result = acpi_bus_get_device(phandle, &pdevice);
if (result) {
printk(KERN_WARNING PREFIX "Cannot get acpi bus device");
acpi_handle_warn(phandle, "Cannot get acpi bus device\n");
return -EINVAL;
}

Expand All @@ -180,14 +186,14 @@ acpi_memory_get_device(acpi_handle handle,
*/
result = acpi_bus_add(&device, pdevice, handle, ACPI_BUS_TYPE_DEVICE);
if (result) {
printk(KERN_WARNING PREFIX "Cannot add acpi bus");
acpi_handle_warn(handle, "Cannot add acpi bus\n");
return -EINVAL;
}

end:
*mem_device = acpi_driver_data(device);
if (!(*mem_device)) {
printk(KERN_ERR "\n driver data not found");
dev_err(&device->dev, "driver data not found\n");
return -ENODEV;
}

Expand Down Expand Up @@ -224,7 +230,8 @@ static int acpi_memory_enable_device(struct acpi_memory_device *mem_device)
/* Get the range from the _CRS */
result = acpi_memory_get_device_resources(mem_device);
if (result) {
printk(KERN_ERR PREFIX "get_device_resources failed\n");
dev_err(&mem_device->device->dev,
"get_device_resources failed\n");
mem_device->state = MEMORY_INVALID_STATE;
return result;
}
Expand All @@ -251,13 +258,27 @@ static int acpi_memory_enable_device(struct acpi_memory_device *mem_device)
node = memory_add_physaddr_to_nid(info->start_addr);

result = add_memory(node, info->start_addr, info->length);
if (result)

/*
* If the memory block has been used by the kernel, add_memory()
* returns -EEXIST. If add_memory() returns the other error, it
* means that this memory block is not used by the kernel.
*/
if (result && result != -EEXIST) {
info->failed = 1;
continue;
info->enabled = 1;
}

if (!result)
info->enabled = 1;
/*
* Add num_enable even if add_memory() returns -EEXIST, so the
* device is bound to this driver.
*/
num_enabled++;
}
if (!num_enabled) {
printk(KERN_ERR PREFIX "add_memory failed\n");
dev_err(&mem_device->device->dev, "add_memory failed\n");
mem_device->state = MEMORY_INVALID_STATE;
return -EINVAL;
}
Expand All @@ -272,75 +293,39 @@ static int acpi_memory_enable_device(struct acpi_memory_device *mem_device)
return 0;
}

static int acpi_memory_powerdown_device(struct acpi_memory_device *mem_device)
static int acpi_memory_remove_memory(struct acpi_memory_device *mem_device)
{
acpi_status status;
struct acpi_object_list arg_list;
union acpi_object arg;
unsigned long long current_status;


/* Issue the _EJ0 command */
arg_list.count = 1;
arg_list.pointer = &arg;
arg.type = ACPI_TYPE_INTEGER;
arg.integer.value = 1;
status = acpi_evaluate_object(mem_device->device->handle,
"_EJ0", &arg_list, NULL);
/* Return on _EJ0 failure */
if (ACPI_FAILURE(status)) {
ACPI_EXCEPTION((AE_INFO, status, "_EJ0 failed"));
return -ENODEV;
}

/* Evalute _STA to check if the device is disabled */
status = acpi_evaluate_integer(mem_device->device->handle, "_STA",
NULL, &current_status);
if (ACPI_FAILURE(status))
return -ENODEV;

/* Check for device status. Device should be disabled */
if (current_status & ACPI_STA_DEVICE_ENABLED)
return -EINVAL;
int result = 0;
struct acpi_memory_info *info, *n;

return 0;
}
list_for_each_entry_safe(info, n, &mem_device->res_list, list) {
if (info->failed)
/* The kernel does not use this memory block */
continue;

static int acpi_memory_disable_device(struct acpi_memory_device *mem_device)
{
int result;
struct acpi_memory_info *info, *n;
if (!info->enabled)
/*
* The kernel uses this memory block, but it may be not
* managed by us.
*/
return -EBUSY;

result = remove_memory(info->start_addr, info->length);
if (result)
return result;

/*
* Ask the VM to offline this memory range.
* Note: Assume that this function returns zero on success
*/
list_for_each_entry_safe(info, n, &mem_device->res_list, list) {
if (info->enabled) {
result = remove_memory(info->start_addr, info->length);
if (result)
return result;
}
list_del(&info->list);
kfree(info);
}

/* Power-off and eject the device */
result = acpi_memory_powerdown_device(mem_device);
if (result) {
/* Set the status of the device to invalid */
mem_device->state = MEMORY_INVALID_STATE;
return result;
}

mem_device->state = MEMORY_POWER_OFF_STATE;
return result;
}

static void acpi_memory_device_notify(acpi_handle handle, u32 event, void *data)
{
struct acpi_memory_device *mem_device;
struct acpi_device *device;
struct acpi_eject_event *ej_event = NULL;
u32 ost_code = ACPI_OST_SC_NON_SPECIFIC_FAILURE; /* default */

switch (event) {
Expand All @@ -353,15 +338,15 @@ static void acpi_memory_device_notify(acpi_handle handle, u32 event, void *data)
ACPI_DEBUG_PRINT((ACPI_DB_INFO,
"\nReceived DEVICE CHECK notification for device\n"));
if (acpi_memory_get_device(handle, &mem_device)) {
printk(KERN_ERR PREFIX "Cannot find driver data\n");
acpi_handle_err(handle, "Cannot find driver data\n");
break;
}

if (acpi_memory_check_device(mem_device))
break;

if (acpi_memory_enable_device(mem_device)) {
printk(KERN_ERR PREFIX "Cannot enable memory device\n");
acpi_handle_err(handle,"Cannot enable memory device\n");
break;
}

Expand All @@ -373,40 +358,28 @@ static void acpi_memory_device_notify(acpi_handle handle, u32 event, void *data)
"\nReceived EJECT REQUEST notification for device\n"));

if (acpi_bus_get_device(handle, &device)) {
printk(KERN_ERR PREFIX "Device doesn't exist\n");
acpi_handle_err(handle, "Device doesn't exist\n");
break;
}
mem_device = acpi_driver_data(device);
if (!mem_device) {
printk(KERN_ERR PREFIX "Driver Data is NULL\n");
acpi_handle_err(handle, "Driver Data is NULL\n");
break;
}

/*
* Currently disabling memory device from kernel mode
* TBD: Can also be disabled from user mode scripts
* TBD: Can also be disabled by Callback registration
* with generic sysfs driver
*/
if (acpi_memory_disable_device(mem_device)) {
printk(KERN_ERR PREFIX "Disable memory device\n");
/*
* If _EJ0 was called but failed, _OST is not
* necessary.
*/
if (mem_device->state == MEMORY_INVALID_STATE)
return;

ej_event = kmalloc(sizeof(*ej_event), GFP_KERNEL);
if (!ej_event) {
pr_err(PREFIX "No memory, dropping EJECT\n");
break;
}

/*
* TBD: Invoke acpi_bus_remove to cleanup data structures
*/
ej_event->handle = handle;
ej_event->event = ACPI_NOTIFY_EJECT_REQUEST;
acpi_os_hotplug_execute(acpi_bus_hot_remove_device,
(void *)ej_event);

/* _EJ0 succeeded; _OST is not necessary */
/* eject is performed asynchronously */
return;

default:
ACPI_DEBUG_PRINT((ACPI_DB_INFO,
"Unsupported event [0x%x]\n", event));
Expand All @@ -420,6 +393,15 @@ static void acpi_memory_device_notify(acpi_handle handle, u32 event, void *data)
return;
}

static void acpi_memory_device_free(struct acpi_memory_device *mem_device)
{
if (!mem_device)
return;

acpi_memory_free_device_resources(mem_device);
kfree(mem_device);
}

static int acpi_memory_device_add(struct acpi_device *device)
{
int result;
Expand Down Expand Up @@ -449,37 +431,35 @@ static int acpi_memory_device_add(struct acpi_device *device)
/* Set the device state */
mem_device->state = MEMORY_POWER_ON_STATE;

printk(KERN_DEBUG "%s \n", acpi_device_name(device));

/*
* Early boot code has recognized memory area by EFI/E820.
* If DSDT shows these memory devices on boot, hotplug is not necessary
* for them. So, it just returns until completion of this driver's
* start up.
*/
if (!acpi_hotmem_initialized)
return 0;
pr_debug("%s\n", acpi_device_name(device));

if (!acpi_memory_check_device(mem_device)) {
/* call add_memory func */
result = acpi_memory_enable_device(mem_device);
if (result)
printk(KERN_ERR PREFIX
if (result) {
dev_err(&device->dev,
"Error in acpi_memory_enable_device\n");
acpi_memory_device_free(mem_device);
}
}
return result;
}

static int acpi_memory_device_remove(struct acpi_device *device, int type)
{
struct acpi_memory_device *mem_device = NULL;

int result;

if (!device || !acpi_driver_data(device))
return -EINVAL;

mem_device = acpi_driver_data(device);
kfree(mem_device);

result = acpi_memory_remove_memory(mem_device);
if (result)
return result;

acpi_memory_device_free(mem_device);

return 0;
}
Expand Down Expand Up @@ -568,7 +548,6 @@ static int __init acpi_memory_device_init(void)
return -ENODEV;
}

acpi_hotmem_initialized = 1;
return 0;
}

Expand Down
Loading

0 comments on commit d4c091f

Please sign in to comment.