mirror of
https://github.com/torvalds/linux.git
synced 2026-11-07 06:17:02 +01:00
hwmon: (acpi_power_meter) Prevent spurious power alarm when power is unknown
According to the ACPI specification (Section 10.4.3 _PMM), if an error occurs while obtaining the power meter reading or if the value is not available, _PMM returns an integer with all bits set (U32_MAX or U64_MAX). In get_power_alarm_state(), update_meter() stores this unknown sentinel value in resource->power and returns 0. Unlike the hwmon_power_average handler in power_meter_read(), get_power_alarm_state() does not check resource->power against UNKNOWN_POWER_32 or UNKNOWN_POWER_64 before comparing resource->power > resource->cap. Because the sentinel is a large unsigned integer, the comparison evaluates to true for any valid cap, falsely reporting an active power alarm to userspace. Furthermore, get_power_alarm_state() writes resource->power > resource->cap back into resource->power_alarm, re-latching polled software comparisons as if a hardware METER_NOTIFY_CAPPING event had occurred and causing the next power1_alarm read to falsely report an alarm even after power drops below cap or becomes unknown. In addition, alarm attributes should not return error codes when the alarm status cannot be determined; an inconclusive alarm state should report 0 (no alarm, no error). Convert get_power_alarm_state() to return a boolean alarm state. Consume and clear any latched hardware capping notification in resource->power_alarm without re-latching software comparisons, and return false (0, no alarm) when no notification was latched and resource->power is unknown or updating the meter or cap reading fails. Reported-by: Sashiko <sashiko-bot@kernel.org> Fixes:de584afa5e("hwmon driver for ACPI 4.0 power meters") Fixes:7532e68f5d("hwmon: (acpi_power_meter) Fix uninitialized variables") Signed-off-by: Guenter Roeck <linux@roeck-us.net>
This commit is contained in:
parent
321dcb9ae9
commit
847ec34ec2
|
|
@ -377,28 +377,29 @@ static int set_avg_interval(struct acpi_power_meter_resource *resource,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int get_power_alarm_state(struct acpi_power_meter_resource *resource,
|
||||
long *val)
|
||||
static bool get_power_alarm_state(struct acpi_power_meter_resource *resource)
|
||||
{
|
||||
bool alarm = false;
|
||||
int ret;
|
||||
|
||||
if (resource->caps.flags & POWER_METER_CAN_NOTIFY)
|
||||
alarm = resource->power_alarm;
|
||||
resource->power_alarm = false;
|
||||
|
||||
ret = update_meter(resource);
|
||||
if (ret)
|
||||
return ret;
|
||||
if (ret ||
|
||||
resource->power == UNKNOWN_POWER_32 ||
|
||||
resource->power == UNKNOWN_POWER_64)
|
||||
return alarm;
|
||||
|
||||
/* need to update cap if not to support the notification. */
|
||||
if (!(resource->caps.flags & POWER_METER_CAN_NOTIFY)) {
|
||||
ret = update_cap(resource);
|
||||
if (ret)
|
||||
return ret;
|
||||
resource->power_alarm = resource->power > resource->cap;
|
||||
*val = resource->power_alarm;
|
||||
} else {
|
||||
*val = resource->power_alarm || resource->power > resource->cap;
|
||||
resource->power_alarm = resource->power > resource->cap;
|
||||
return false;
|
||||
}
|
||||
|
||||
return 0;
|
||||
return alarm || resource->power > resource->cap;
|
||||
}
|
||||
|
||||
static umode_t power_meter_is_visible(const void *data,
|
||||
|
|
@ -481,9 +482,7 @@ static int power_meter_read(struct device *dev, enum hwmon_sensor_types type,
|
|||
*val = res->caps.max_cap * 1000;
|
||||
break;
|
||||
case hwmon_power_alarm:
|
||||
ret = get_power_alarm_state(res, val);
|
||||
if (ret)
|
||||
return ret;
|
||||
*val = get_power_alarm_state(res);
|
||||
break;
|
||||
case hwmon_power_cap:
|
||||
ret = update_cap(res);
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user