ACPI: PM: Fix NULL argument handling in acpi_device_get/set_power()
In principle, it should be valid to pass NULL as the ACPI device pointer to acpi_device_get_power() and acpi_device_set_power() and they both are expected to return -EINVAL in that case, but that has been broken recently by commit62fcb99bdf
("ACPI: Drop parent field from struct acpi_device") which has caused the ACPI device pointer to be dereferenced in these functions before the NULL check. Fix that and while at it make acpi_device_set_power() only use the parent field if the target ACPI device object's ignore_parent flag in not set. Fixes:62fcb99bdf
("ACPI: Drop parent field from struct acpi_device") Reported-by: Dan Carpenter <dan.carpenter@oracle.com> Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
This commit is contained in:
parent
62fcb99bdf
commit
d5008ef5b5
@ -74,13 +74,15 @@ static int acpi_dev_pm_explicit_get(struct acpi_device *device, int *state)
|
|||||||
*/
|
*/
|
||||||
int acpi_device_get_power(struct acpi_device *device, int *state)
|
int acpi_device_get_power(struct acpi_device *device, int *state)
|
||||||
{
|
{
|
||||||
struct acpi_device *parent = acpi_dev_parent(device);
|
|
||||||
int result = ACPI_STATE_UNKNOWN;
|
int result = ACPI_STATE_UNKNOWN;
|
||||||
|
struct acpi_device *parent;
|
||||||
int error;
|
int error;
|
||||||
|
|
||||||
if (!device || !state)
|
if (!device || !state)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
|
parent = acpi_dev_parent(device);
|
||||||
|
|
||||||
if (!device->flags.power_manageable) {
|
if (!device->flags.power_manageable) {
|
||||||
/* TBD: Non-recursive algorithm for walking up hierarchy. */
|
/* TBD: Non-recursive algorithm for walking up hierarchy. */
|
||||||
*state = parent ? parent->power.state : ACPI_STATE_D0;
|
*state = parent ? parent->power.state : ACPI_STATE_D0;
|
||||||
@ -159,7 +161,6 @@ static int acpi_dev_pm_explicit_set(struct acpi_device *adev, int state)
|
|||||||
*/
|
*/
|
||||||
int acpi_device_set_power(struct acpi_device *device, int state)
|
int acpi_device_set_power(struct acpi_device *device, int state)
|
||||||
{
|
{
|
||||||
struct acpi_device *parent = acpi_dev_parent(device);
|
|
||||||
int target_state = state;
|
int target_state = state;
|
||||||
int result = 0;
|
int result = 0;
|
||||||
|
|
||||||
@ -192,14 +193,18 @@ int acpi_device_set_power(struct acpi_device *device, int state)
|
|||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!device->power.flags.ignore_parent && parent &&
|
if (!device->power.flags.ignore_parent) {
|
||||||
state < parent->power.state) {
|
struct acpi_device *parent;
|
||||||
|
|
||||||
|
parent = acpi_dev_parent(device);
|
||||||
|
if (parent && state < parent->power.state) {
|
||||||
acpi_handle_debug(device->handle,
|
acpi_handle_debug(device->handle,
|
||||||
"Cannot transition to %s for parent in %s\n",
|
"Cannot transition to %s for parent in %s\n",
|
||||||
acpi_power_state_string(state),
|
acpi_power_state_string(state),
|
||||||
acpi_power_state_string(parent->power.state));
|
acpi_power_state_string(parent->power.state));
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Transition Power
|
* Transition Power
|
||||||
|
Loading…
Reference in New Issue
Block a user