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:
Guenter Roeck 2026-10-31 09:08:12 -07:00
parent 321dcb9ae9
commit 847ec34ec2

View File

@ -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);