ACPI and power management fixes for 3.17-rc7

- Revert of a recent hibernation core commit that introduced
    a NULL pointer dereference during resume for at least one user
    (Rafael J Wysocki).
 
  - Fix for the ACPI LPSS (Low-Power Subsystem) driver to disable
    asynchronous PM callback execution for LPSS devices during system
    suspend/resume (introduced in 3.16) which turns out to break
    ordering expectations on some systems.  From Fu Zhonghui.
 
  - cpufreq core fix related to the handling of sysfs nodes during
    system suspend/resume that has been broken for intel_pstate
    since 3.15 from Lan Tianyu.
 
  - Restore the generation of "online" uevents for ACPI container
    devices that was removed in 3.14, but some user space utilities
    turn out to need them (Rafael J Wysocki).
 
  - The cpufreq core fails to release a lock in an error code path
    after changes made in 3.14.  Fix from Prarit Bhargava.
 
  - ACPICA and ACPI/GPIO fixes to make the handling of ACPI GPIO
    operation regions (which means AML using GPIOs) work correctly
    in all cases from Bob Moore and Srinivas Pandruvada.
 
  - Fix for a wrong sign of the ACPI core's create_modalias() return
    value in case of an error from Mika Westerberg.
 
  - ACPI backlight blacklist entry for ThinkPad X201s from Aaron Lu.
 
 /
 -----BEGIN PGP SIGNATURE-----
 Version: GnuPG v2.0.22 (GNU/Linux)
 
 iQIcBAABCAAGBQJUJJGgAAoJEILEb/54YlRxt3kP/19OjVjGK/lFKJk4LCmQ77k5
 6DDF7/clNJmYBkKBXGdyqqRVdDUXjRuHS1Yd78zWMmwdLtdOcyI+wBjG1w0mMU7o
 vAYvXkIks9fCeKBRHSlqdtQROFf3+bxothKD8JGTONA5z4Fih40fqsnuSW8G7uJs
 iTEQQK7L2uPJ+w1OnltwN6eNgzN5KqfxgxI+L6DhEMRjWXRHuhfRZorVIjvz+ALV
 Fjm8shhjnhQKzS2zuv5PZ5gGM7zZBH7hy7kd4aDYsbppOLAB2pMOwVs0sgC1Xcbv
 teyWkyzmhix2Z1bX9wwia5FfMgbnY2leejJN7mukKzHz8CQ1vxS98Sji2uviIAej
 Ctp6GKjuemGvjryjbkstD6r3KYS8CuWAL++YwlamqSa0eWBuM+aD9YqGj4i6ntbU
 8BFT5KXauOIsA5U51zC8wNUDHoTgBcvoN99zNIM1jIF81M7wuQrXUzJLXBStuSlR
 /bDpExwxHt7I6MeUfRTjg37ApVNRAiStw32+DfsKAj4HLsqTkGs1879Paxf30T0f
 Z2SlYr5Jeusu5u9DNhk7MG21A+m46R0jjLd1OKBbf2mrtfQfdKCo6szGR7vjEMZC
 aGIlwtIA4iS4MN3UAyqOW3SxIPT2SxqPXzG/z27hRN5MUsGNWiClzcUsaaHoHmpp
 GlbY/BvDYfur4NBeCSli
 =SzQq
 -----END PGP SIGNATURE-----

Merge tag 'pm+acpi-3.17-rc7' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm

Pull ACPI and power management fixes from Rafael Wysocki:
 "These are regression fixes (ACPI hotplug, cpufreq, hibernation, ACPI
  LPSS driver), fixes for stuff that never worked correctly (ACPI GPIO
  support in some cases and a wrong sign of an error code in the ACPI
  core in one place), and one blacklist item for ACPI backlight
  handling.

  Specifics:

   - Revert of a recent hibernation core commit that introduced a NULL
     pointer dereference during resume for at least one user (Rafael J
     Wysocki).

   - Fix for the ACPI LPSS (Low-Power Subsystem) driver to disable
     asynchronous PM callback execution for LPSS devices during system
     suspend/resume (introduced in 3.16) which turns out to break
     ordering expectations on some systems.  From Fu Zhonghui.

   - cpufreq core fix related to the handling of sysfs nodes during
     system suspend/resume that has been broken for intel_pstate since
     3.15 from Lan Tianyu.

   - Restore the generation of "online" uevents for ACPI container
     devices that was removed in 3.14, but some user space utilities
     turn out to need them (Rafael J Wysocki).

   - The cpufreq core fails to release a lock in an error code path
     after changes made in 3.14.  Fix from Prarit Bhargava.

   - ACPICA and ACPI/GPIO fixes to make the handling of ACPI GPIO
     operation regions (which means AML using GPIOs) work correctly in
     all cases from Bob Moore and Srinivas Pandruvada.

   - Fix for a wrong sign of the ACPI core's create_modalias() return
     value in case of an error from Mika Westerberg.

   - ACPI backlight blacklist entry for ThinkPad X201s from Aaron Lu"

* tag 'pm+acpi-3.17-rc7' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm:
  Revert "PM / Hibernate: Iterate over set bits instead of PFNs in swsusp_free()"
  gpio / ACPI: Use pin index and bit length
  ACPICA: Update to GPIO region handler interface.
  ACPI / platform / LPSS: disable async suspend/resume of LPSS devices
  cpufreq: release policy->rwsem on error
  cpufreq: fix cpufreq suspend/resume for intel_pstate
  ACPI / scan: Correct error return value of create_modalias()
  ACPI / video: disable native backlight for ThinkPad X201s
  ACPI / hotplug: Generate online uevents for ACPI containers
This commit is contained in:
Linus Torvalds 2014-09-25 15:25:52 -07:00
commit f4cb707e7a
14 changed files with 149 additions and 57 deletions

View file

@ -419,7 +419,6 @@ static int acpi_lpss_create_device(struct acpi_device *adev,
adev->driver_data = pdata;
pdev = acpi_create_platform_device(adev);
if (!IS_ERR_OR_NULL(pdev)) {
device_enable_async_suspend(&pdev->dev);
return 1;
}

View file

@ -254,6 +254,7 @@ struct acpi_create_field_info {
u32 field_bit_position;
u32 field_bit_length;
u16 resource_length;
u16 pin_number_index;
u8 field_flags;
u8 attribute;
u8 field_type;

View file

@ -264,6 +264,7 @@ struct acpi_object_region_field {
ACPI_OBJECT_COMMON_HEADER ACPI_COMMON_FIELD_INFO u16 resource_length;
union acpi_operand_object *region_obj; /* Containing op_region object */
u8 *resource_buffer; /* resource_template for serial regions/fields */
u16 pin_number_index; /* Index relative to previous Connection/Template */
};
struct acpi_object_bank_field {

View file

@ -360,6 +360,7 @@ acpi_ds_get_field_names(struct acpi_create_field_info *info,
*/
info->resource_buffer = NULL;
info->connection_node = NULL;
info->pin_number_index = 0;
/*
* A Connection() is either an actual resource descriptor (buffer)
@ -437,6 +438,7 @@ acpi_ds_get_field_names(struct acpi_create_field_info *info,
}
info->field_bit_position += info->field_bit_length;
info->pin_number_index++; /* Index relative to previous Connection() */
break;
default:

View file

@ -142,6 +142,7 @@ acpi_ev_address_space_dispatch(union acpi_operand_object *region_obj,
union acpi_operand_object *region_obj2;
void *region_context = NULL;
struct acpi_connection_info *context;
acpi_physical_address address;
ACPI_FUNCTION_TRACE(ev_address_space_dispatch);
@ -231,25 +232,23 @@ acpi_ev_address_space_dispatch(union acpi_operand_object *region_obj,
/* We have everything we need, we can invoke the address space handler */
handler = handler_desc->address_space.handler;
ACPI_DEBUG_PRINT((ACPI_DB_OPREGION,
"Handler %p (@%p) Address %8.8X%8.8X [%s]\n",
&region_obj->region.handler->address_space, handler,
ACPI_FORMAT_NATIVE_UINT(region_obj->region.address +
region_offset),
acpi_ut_get_region_name(region_obj->region.
space_id)));
address = (region_obj->region.address + region_offset);
/*
* Special handling for generic_serial_bus and general_purpose_io:
* There are three extra parameters that must be passed to the
* handler via the context:
* 1) Connection buffer, a resource template from Connection() op.
* 2) Length of the above buffer.
* 3) Actual access length from the access_as() op.
* 1) Connection buffer, a resource template from Connection() op
* 2) Length of the above buffer
* 3) Actual access length from the access_as() op
*
* In addition, for general_purpose_io, the Address and bit_width fields
* are defined as follows:
* 1) Address is the pin number index of the field (bit offset from
* the previous Connection)
* 2) bit_width is the actual bit length of the field (number of pins)
*/
if (((region_obj->region.space_id == ACPI_ADR_SPACE_GSBUS) ||
(region_obj->region.space_id == ACPI_ADR_SPACE_GPIO)) &&
if ((region_obj->region.space_id == ACPI_ADR_SPACE_GSBUS) &&
context && field_obj) {
/* Get the Connection (resource_template) buffer */
@ -258,6 +257,24 @@ acpi_ev_address_space_dispatch(union acpi_operand_object *region_obj,
context->length = field_obj->field.resource_length;
context->access_length = field_obj->field.access_length;
}
if ((region_obj->region.space_id == ACPI_ADR_SPACE_GPIO) &&
context && field_obj) {
/* Get the Connection (resource_template) buffer */
context->connection = field_obj->field.resource_buffer;
context->length = field_obj->field.resource_length;
context->access_length = field_obj->field.access_length;
address = field_obj->field.pin_number_index;
bit_width = field_obj->field.bit_length;
}
ACPI_DEBUG_PRINT((ACPI_DB_OPREGION,
"Handler %p (@%p) Address %8.8X%8.8X [%s]\n",
&region_obj->region.handler->address_space, handler,
ACPI_FORMAT_NATIVE_UINT(address),
acpi_ut_get_region_name(region_obj->region.
space_id)));
if (!(handler_desc->address_space.handler_flags &
ACPI_ADDR_HANDLER_DEFAULT_INSTALLED)) {
@ -271,9 +288,7 @@ acpi_ev_address_space_dispatch(union acpi_operand_object *region_obj,
/* Call the handler */
status = handler(function,
(region_obj->region.address + region_offset),
bit_width, value, context,
status = handler(function, address, bit_width, value, context,
region_obj2->extra.region_context);
if (ACPI_FAILURE(status)) {

View file

@ -253,6 +253,37 @@ acpi_ex_read_data_from_field(struct acpi_walk_state * walk_state,
buffer = &buffer_desc->integer.value;
}
if ((obj_desc->common.type == ACPI_TYPE_LOCAL_REGION_FIELD) &&
(obj_desc->field.region_obj->region.space_id ==
ACPI_ADR_SPACE_GPIO)) {
/*
* For GPIO (general_purpose_io), the Address will be the bit offset
* from the previous Connection() operator, making it effectively a
* pin number index. The bit_length is the length of the field, which
* is thus the number of pins.
*/
ACPI_DEBUG_PRINT((ACPI_DB_BFIELD,
"GPIO FieldRead [FROM]: Pin %u Bits %u\n",
obj_desc->field.pin_number_index,
obj_desc->field.bit_length));
/* Lock entire transaction if requested */
acpi_ex_acquire_global_lock(obj_desc->common_field.field_flags);
/* Perform the write */
status = acpi_ex_access_region(obj_desc, 0,
(u64 *)buffer, ACPI_READ);
acpi_ex_release_global_lock(obj_desc->common_field.field_flags);
if (ACPI_FAILURE(status)) {
acpi_ut_remove_reference(buffer_desc);
} else {
*ret_buffer_desc = buffer_desc;
}
return_ACPI_STATUS(status);
}
ACPI_DEBUG_PRINT((ACPI_DB_BFIELD,
"FieldRead [TO]: Obj %p, Type %X, Buf %p, ByteLen %X\n",
obj_desc, obj_desc->common.type, buffer,
@ -413,6 +444,42 @@ acpi_ex_write_data_to_field(union acpi_operand_object *source_desc,
*result_desc = buffer_desc;
return_ACPI_STATUS(status);
} else if ((obj_desc->common.type == ACPI_TYPE_LOCAL_REGION_FIELD) &&
(obj_desc->field.region_obj->region.space_id ==
ACPI_ADR_SPACE_GPIO)) {
/*
* For GPIO (general_purpose_io), we will bypass the entire field
* mechanism and handoff the bit address and bit width directly to
* the handler. The Address will be the bit offset
* from the previous Connection() operator, making it effectively a
* pin number index. The bit_length is the length of the field, which
* is thus the number of pins.
*/
if (source_desc->common.type != ACPI_TYPE_INTEGER) {
return_ACPI_STATUS(AE_AML_OPERAND_TYPE);
}
ACPI_DEBUG_PRINT((ACPI_DB_BFIELD,
"GPIO FieldWrite [FROM]: (%s:%X), Val %.8X [TO]: Pin %u Bits %u\n",
acpi_ut_get_type_name(source_desc->common.
type),
source_desc->common.type,
(u32)source_desc->integer.value,
obj_desc->field.pin_number_index,
obj_desc->field.bit_length));
buffer = &source_desc->integer.value;
/* Lock entire transaction if requested */
acpi_ex_acquire_global_lock(obj_desc->common_field.field_flags);
/* Perform the write */
status = acpi_ex_access_region(obj_desc, 0,
(u64 *)buffer, ACPI_WRITE);
acpi_ex_release_global_lock(obj_desc->common_field.field_flags);
return_ACPI_STATUS(status);
}
/* Get a pointer to the data to be written */

View file

@ -484,6 +484,8 @@ acpi_status acpi_ex_prep_field_value(struct acpi_create_field_info *info)
obj_desc->field.resource_length = info->resource_length;
}
obj_desc->field.pin_number_index = info->pin_number_index;
/* Allow full data read from EC address space */
if ((obj_desc->field.region_obj->region.space_id ==

View file

@ -99,6 +99,13 @@ static void container_device_detach(struct acpi_device *adev)
device_unregister(dev);
}
static void container_device_online(struct acpi_device *adev)
{
struct device *dev = acpi_driver_data(adev);
kobject_uevent(&dev->kobj, KOBJ_ONLINE);
}
static struct acpi_scan_handler container_handler = {
.ids = container_device_ids,
.attach = container_device_attach,
@ -106,6 +113,7 @@ static struct acpi_scan_handler container_handler = {
.hotplug = {
.enabled = true,
.demand_offline = true,
.notify_online = container_device_online,
},
};

View file

@ -130,7 +130,7 @@ static int create_modalias(struct acpi_device *acpi_dev, char *modalias,
list_for_each_entry(id, &acpi_dev->pnp.ids, list) {
count = snprintf(&modalias[len], size, "%s:", id->id);
if (count < 0)
return EINVAL;
return -EINVAL;
if (count >= size)
return -ENOMEM;
len += count;
@ -2189,6 +2189,9 @@ static void acpi_bus_attach(struct acpi_device *device)
ok:
list_for_each_entry(child, &device->children, node)
acpi_bus_attach(child);
if (device->handler && device->handler->hotplug.notify_online)
device->handler->hotplug.notify_online(device);
}
/**

View file

@ -750,6 +750,14 @@ static struct dmi_system_id video_dmi_table[] __initdata = {
DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad T520"),
},
},
{
.callback = video_disable_native_backlight,
.ident = "ThinkPad X201s",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad X201s"),
},
},
/* The native backlight controls do not work on some older machines */
{

View file

@ -1289,6 +1289,8 @@ static int __cpufreq_add_dev(struct device *dev, struct subsys_interface *sif)
per_cpu(cpufreq_cpu_data, j) = NULL;
write_unlock_irqrestore(&cpufreq_driver_lock, flags);
up_write(&policy->rwsem);
if (cpufreq_driver->exit)
cpufreq_driver->exit(policy);
err_set_policy_cpu:
@ -1656,6 +1658,8 @@ void cpufreq_suspend(void)
if (!cpufreq_driver)
return;
cpufreq_suspended = true;
if (!has_target())
return;
@ -1670,8 +1674,6 @@ void cpufreq_suspend(void)
pr_err("%s: Failed to suspend driver: %p\n", __func__,
policy);
}
cpufreq_suspended = true;
}
/**
@ -1687,13 +1689,13 @@ void cpufreq_resume(void)
if (!cpufreq_driver)
return;
cpufreq_suspended = false;
if (!has_target())
return;
pr_debug("%s: Resuming Governors\n", __func__);
cpufreq_suspended = false;
list_for_each_entry(policy, &cpufreq_policy_list, policy_list) {
if (cpufreq_driver->resume && cpufreq_driver->resume(policy))
pr_err("%s: Failed to resume driver: %p\n", __func__,

View file

@ -377,8 +377,10 @@ acpi_gpio_adr_space_handler(u32 function, acpi_physical_address address,
struct gpio_chip *chip = achip->chip;
struct acpi_resource_gpio *agpio;
struct acpi_resource *ares;
int pin_index = (int)address;
acpi_status status;
bool pull_up;
int length;
int i;
status = acpi_buffer_to_resource(achip->conn_info.connection,
@ -400,7 +402,8 @@ acpi_gpio_adr_space_handler(u32 function, acpi_physical_address address,
return AE_BAD_PARAMETER;
}
for (i = 0; i < agpio->pin_table_length; i++) {
length = min(agpio->pin_table_length, (u16)(pin_index + bits));
for (i = pin_index; i < length; ++i) {
unsigned pin = agpio->pin_table[i];
struct acpi_gpio_connection *conn;
struct gpio_desc *desc;

View file

@ -118,6 +118,7 @@ struct acpi_device;
struct acpi_hotplug_profile {
struct kobject kobj;
int (*scan_dependent)(struct acpi_device *adev);
void (*notify_online)(struct acpi_device *adev);
bool enabled:1;
bool demand_offline:1;
};

View file

@ -725,14 +725,6 @@ static void memory_bm_clear_bit(struct memory_bitmap *bm, unsigned long pfn)
clear_bit(bit, addr);
}
static void memory_bm_clear_current(struct memory_bitmap *bm)
{
int bit;
bit = max(bm->cur.node_bit - 1, 0);
clear_bit(bit, bm->cur.node->data);
}
static int memory_bm_test_bit(struct memory_bitmap *bm, unsigned long pfn)
{
void *addr;
@ -1341,35 +1333,23 @@ static struct memory_bitmap copy_bm;
void swsusp_free(void)
{
unsigned long fb_pfn, fr_pfn;
struct zone *zone;
unsigned long pfn, max_zone_pfn;
memory_bm_position_reset(forbidden_pages_map);
memory_bm_position_reset(free_pages_map);
for_each_populated_zone(zone) {
max_zone_pfn = zone_end_pfn(zone);
for (pfn = zone->zone_start_pfn; pfn < max_zone_pfn; pfn++)
if (pfn_valid(pfn)) {
struct page *page = pfn_to_page(pfn);
loop:
fr_pfn = memory_bm_next_pfn(free_pages_map);
fb_pfn = memory_bm_next_pfn(forbidden_pages_map);
/*
* Find the next bit set in both bitmaps. This is guaranteed to
* terminate when fb_pfn == fr_pfn == BM_END_OF_MAP.
*/
do {
if (fb_pfn < fr_pfn)
fb_pfn = memory_bm_next_pfn(forbidden_pages_map);
if (fr_pfn < fb_pfn)
fr_pfn = memory_bm_next_pfn(free_pages_map);
} while (fb_pfn != fr_pfn);
if (fr_pfn != BM_END_OF_MAP && pfn_valid(fr_pfn)) {
struct page *page = pfn_to_page(fr_pfn);
memory_bm_clear_current(forbidden_pages_map);
memory_bm_clear_current(free_pages_map);
__free_page(page);
goto loop;
if (swsusp_page_is_forbidden(page) &&
swsusp_page_is_free(page)) {
swsusp_unset_page_forbidden(page);
swsusp_unset_page_free(page);
__free_page(page);
}
}
}
nr_copy_pages = 0;
nr_meta_pages = 0;
restore_pblist = NULL;