ACPI fix for 5.13-rc4.

Fix a recent ACPI power management regression causing boot issues
 to occur on some systems due to attempts to turn off ACPI power
 resources that are already off (which should work according to the
 ACPI specification).
 -----BEGIN PGP SIGNATURE-----
 
 iQJGBAABCAAwFiEE4fcc61cGeeHD/fCwgsRv/nhiVHEFAmCv5H0SHHJqd0Byand5
 c29ja2kubmV0AAoJEILEb/54YlRx11wP/1j7DA8TOOeJjdxYOw3Ky3XtntjpVphe
 xeI2M9w8zDhiZl0JeyWNjLbiPL50j6xi9+YuglG/pIjnhUoMtewr/7+4+X6/iz7U
 VMZ+C+Hd/0/vdn1vWKxMmCIHASi5Vz+RMon58GweBLAcMiedJkVX1qtv+ZAUmhBY
 KtCjQVpSDuhmzrw6LPADjTjobHIsQPUnu+SE/faplFoEGHv9CACrypaHuNO+ACDm
 v0eT09Zjt+qGEhkCy33xmd47+JcktOmSuHabg4yn/93+z7HHwNDhIbInFUTdvPoW
 CnBV6wUGerj57nN6kzjCEb4nowhYwbmfJf8ufROfay+eR/arnueo9rNX5G/iGxRS
 VpIAG+Fu3mm9RfOwwWrl/v8ofBPjRGhCShAYnBPtFr5ZQX4pnRstZcWmxxQK5/x5
 2RN9cQlBmm0wzKTwJ9XYpscZqf+yNVQSzYz1vM5jLNh8DggQ4ChqUG7QJcUYKrwL
 Gs6eoiMRgF/CqaNgx8cd96YRv7KiUZtYN8fQRSWmCmmTlkKJk1FoHbQXQ78MUZ9Y
 w7KI9yXCA5TgcUAwgrBuJIrcEMIGYlukrdSDFFwJfmUKsd2X/mRoIdr8Z8yQicQg
 ZMzyKKjMCqKDDZLYx8ZiKygap7Hg/W+bii75PyFOD+UWg6LXn6ARdmaWiggLw1bE
 10Z6UnsV7zZ2
 =z6Ba
 -----END PGP SIGNATURE-----

Merge tag 'acpi-5.13-rc4' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm

Pull ACPI fix from Rafael Wysocki:
 "Fix a recent ACPI power management regression causing boot issues to
  occur on some systems due to attempts to turn off ACPI power resources
  that are already off (which should work according to the ACPI
  specification)"

* tag 'acpi-5.13-rc4' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm:
  ACPI: power: Refine turning off unused power resources
This commit is contained in:
Linus Torvalds 2021-05-27 08:39:05 -10:00
commit 3224374f7e
4 changed files with 50 additions and 17 deletions

View File

@ -134,7 +134,7 @@ int acpi_power_init(void);
void acpi_power_resources_list_free(struct list_head *list);
int acpi_extract_power_resources(union acpi_object *package, unsigned int start,
struct list_head *list);
int acpi_add_power_resource(acpi_handle handle);
struct acpi_device *acpi_add_power_resource(acpi_handle handle);
void acpi_power_add_remove_device(struct acpi_device *adev, bool add);
int acpi_power_wakeup_list_init(struct list_head *list, int *system_level);
int acpi_device_sleep_wake(struct acpi_device *dev,
@ -142,7 +142,7 @@ int acpi_device_sleep_wake(struct acpi_device *dev,
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);
void acpi_turn_off_unused_power_resources(void);
void acpi_turn_off_unused_power_resources(bool init);
/* --------------------------------------------------------------------------
Device Power Management

View File

@ -52,6 +52,7 @@ struct acpi_power_resource {
u32 system_level;
u32 order;
unsigned int ref_count;
unsigned int users;
bool wakeup_enabled;
struct mutex resource_lock;
struct list_head dependents;
@ -147,6 +148,7 @@ int acpi_extract_power_resources(union acpi_object *package, unsigned int start,
for (i = start; i < package->package.count; i++) {
union acpi_object *element = &package->package.elements[i];
struct acpi_device *rdev;
acpi_handle rhandle;
if (element->type != ACPI_TYPE_LOCAL_REFERENCE) {
@ -163,13 +165,16 @@ int acpi_extract_power_resources(union acpi_object *package, unsigned int start,
if (acpi_power_resource_is_dup(package, start, i))
continue;
err = acpi_add_power_resource(rhandle);
if (err)
rdev = acpi_add_power_resource(rhandle);
if (!rdev) {
err = -ENODEV;
break;
}
err = acpi_power_resources_list_add(rhandle, list);
if (err)
break;
to_power_resource(rdev)->users++;
}
if (err)
acpi_power_resources_list_free(list);
@ -907,7 +912,7 @@ static void acpi_power_add_resource_to_list(struct acpi_power_resource *resource
mutex_unlock(&power_resource_list_lock);
}
int acpi_add_power_resource(acpi_handle handle)
struct acpi_device *acpi_add_power_resource(acpi_handle handle)
{
struct acpi_power_resource *resource;
struct acpi_device *device = NULL;
@ -918,11 +923,11 @@ int acpi_add_power_resource(acpi_handle handle)
acpi_bus_get_device(handle, &device);
if (device)
return 0;
return device;
resource = kzalloc(sizeof(*resource), GFP_KERNEL);
if (!resource)
return -ENOMEM;
return NULL;
device = &resource->device;
acpi_init_device_object(device, handle, ACPI_BUS_TYPE_POWER);
@ -959,11 +964,11 @@ int acpi_add_power_resource(acpi_handle handle)
acpi_power_add_resource_to_list(resource);
acpi_device_add_finalize(device);
return 0;
return device;
err:
acpi_release_power_resource(&device->dev);
return result;
return NULL;
}
#ifdef CONFIG_ACPI_SLEEP
@ -997,7 +1002,38 @@ void acpi_resume_power_resources(void)
}
#endif
void acpi_turn_off_unused_power_resources(void)
static void acpi_power_turn_off_if_unused(struct acpi_power_resource *resource,
bool init)
{
if (resource->ref_count > 0)
return;
if (init) {
if (resource->users > 0)
return;
} else {
int result, state;
result = acpi_power_get_state(resource->device.handle, &state);
if (result || state == ACPI_POWER_RESOURCE_STATE_OFF)
return;
}
dev_info(&resource->device.dev, "Turning OFF\n");
__acpi_power_off(resource);
}
/**
* acpi_turn_off_unused_power_resources - Turn off power resources not in use.
* @init: Control switch.
*
* If @ainit is set, unconditionally turn off all of the ACPI power resources
* without any users.
*
* Otherwise, turn off all ACPI power resources without active references (that
* is, the ones that should be "off" at the moment) that are "on".
*/
void acpi_turn_off_unused_power_resources(bool init)
{
struct acpi_power_resource *resource;
@ -1006,10 +1042,7 @@ void acpi_turn_off_unused_power_resources(void)
list_for_each_entry_reverse(resource, &acpi_power_resource_list, list_node) {
mutex_lock(&resource->resource_lock);
if (!resource->ref_count) {
dev_info(&resource->device.dev, "Turning OFF\n");
__acpi_power_off(resource);
}
acpi_power_turn_off_if_unused(resource, init);
mutex_unlock(&resource->resource_lock);
}

View File

@ -2360,7 +2360,7 @@ int __init acpi_scan_init(void)
}
}
acpi_turn_off_unused_power_resources();
acpi_turn_off_unused_power_resources(true);
acpi_scan_initialized = true;

View File

@ -504,7 +504,7 @@ static void acpi_pm_start(u32 acpi_state)
*/
static void acpi_pm_end(void)
{
acpi_turn_off_unused_power_resources();
acpi_turn_off_unused_power_resources(false);
acpi_scan_lock_release();
/*
* This is necessary in case acpi_pm_finish() is not called during a