Linux Hardware Monitor development
 help / color / mirror / Atom feed
From: Guenter Roeck <linux@roeck-us.net>
To: linux-hwmon@vger.kernel.org
Cc: Guenter Roeck <linux@roeck-us.net>, Sashiko <sashiko-bot@kernel.org>
Subject: [PATCH 3/3] hwmon: (acpi_power_meter) Check for 32-bit and 64-bit unknown ACPI values
Date: Sat, 31 Oct 2026 09:54:47 -0700	[thread overview]
Message-ID: <20261031165447.1771473-4-linux@roeck-us.net> (raw)
In-Reply-To: <20261031165447.1771473-1-linux@roeck-us.net>

According to the ACPI specification (Section 10.4), ACPI Power Meter
objects (_PMC capability fields, _PMM, _GHL, _PAI) report unknown or
unavailable integer values using an integer with all bits set, which can
be either U32_MAX (0xffffffff) or U64_MAX (0xffffffffffffffff) depending
on the DSDT revision.

Currently, only hwmon_power_average checks both 32-bit and 64-bit
unknown sentinels:
- power1_cap_hyst_show() only compares res->caps.hysteresis against the
  32-bit UNKNOWN_HYSTERESIS (0xffffffff), so a 64-bit U64_MAX sentinel
  bypasses the check, overflows when multiplied by 1000, and emits a
  bogus number instead of "unknown".
- power1_accuracy_show() does not check for unknown sentinels at all,
  formatting 0xffffffff as "4294967.295%".
- power_meter_read() does not check min_avg_interval, max_avg_interval,
  avg_interval, min_cap, max_cap, or cap for unknown sentinels, exposing
  scaled or overflowed values (or negative values when cast to signed
  long) to userspace instead of returning -ENODATA.
- get_power_alarm_state() does not check whether resource->cap is
  unknown before comparing resource->power > resource->cap.
- update_meter() passes an unknown caps.sampling_time directly to
  msecs_to_jiffies(), caching sensor readings for weeks on 64-bit builds.

Introduce acpi_power_meter_is_unknown() to check for both U32_MAX and
U64_MAX, and use it across sysfs attribute handlers and meter updates:
- Return -ENODATA in power_meter_read() when numeric power, interval, or
  cap attributes report an unknown value.
- Emit "unknown" in power1_cap_hyst_show() and power1_accuracy_show()
  when hysteresis or accuracy is unknown.
- Avoid comparing against an unknown cap in get_power_alarm_state(),
  returning 0 (no alarm, no error) unless a hardware capping
  notification was latched.
- Skip the sampling_time cache window in update_meter() when
  sampling_time is unknown.

Reported-by: Sashiko <sashiko-bot@kernel.org>
Fixes: de584afa5e18 ("hwmon driver for ACPI 4.0 power meters")
Signed-off-by: Guenter Roeck <linux@roeck-us.net>
---
 drivers/hwmon/acpi_power_meter.c | 49 ++++++++++++++++++++++----------
 1 file changed, 34 insertions(+), 15 deletions(-)

diff --git a/drivers/hwmon/acpi_power_meter.c b/drivers/hwmon/acpi_power_meter.c
index 1f1d58995693..b28ca6acf630 100644
--- a/drivers/hwmon/acpi_power_meter.c
+++ b/drivers/hwmon/acpi_power_meter.c
@@ -30,13 +30,6 @@
 #define POWER_METER_CAN_CAP	(1 << 2)
 #define POWER_METER_CAN_NOTIFY	(1 << 3)
 #define POWER_METER_IS_BATTERY	(1 << 8)
-#define UNKNOWN_HYSTERESIS	0xFFFFFFFF
-/*
- * ACPI integers can either be 32-bit or 64-bit wide, so
- * we have to check for both values.
- */
-#define UNKNOWN_POWER_32	U32_MAX
-#define UNKNOWN_POWER_64	U64_MAX
 
 #define METER_NOTIFY_CONFIG	0x80
 #define METER_NOTIFY_TRIP	0x81
@@ -54,6 +47,15 @@ static bool force_cap_on;
 
 static DEFINE_MUTEX(acpi_notify_lock);
 
+/*
+ * ACPI integers can either be 32-bit or 64-bit wide, so
+ * we have to check for both values.
+ */
+static bool acpi_power_meter_is_unknown(u64 val)
+{
+	return val == U32_MAX || val == U64_MAX;
+}
+
 static int can_cap_in_hardware(void)
 {
 	return force_cap_on || cap_in_hardware;
@@ -179,9 +181,10 @@ static int update_meter(struct acpi_power_meter_resource *resource)
 	acpi_status status;
 	unsigned long local_jiffies = jiffies;
 
-	if (time_before(local_jiffies, resource->sensors_last_updated +
+	if (!acpi_power_meter_is_unknown(resource->caps.sampling_time) &&
+	    time_before(local_jiffies, resource->sensors_last_updated +
 			msecs_to_jiffies(resource->caps.sampling_time)) &&
-			resource->sensors_valid)
+	    resource->sensors_valid)
 		return 0;
 
 	status = acpi_evaluate_integer(resource->acpi_dev->handle, "_PMM",
@@ -387,9 +390,7 @@ static bool get_power_alarm_state(struct acpi_power_meter_resource *resource)
 	resource->power_alarm = false;
 
 	ret = update_meter(resource);
-	if (ret ||
-	    resource->power == UNKNOWN_POWER_32 ||
-	    resource->power == UNKNOWN_POWER_64)
+	if (ret || acpi_power_meter_is_unknown(resource->power))
 		return alarm;
 
 	/* need to update cap if not to support the notification. */
@@ -399,6 +400,9 @@ static bool get_power_alarm_state(struct acpi_power_meter_resource *resource)
 			return false;
 	}
 
+	if (acpi_power_meter_is_unknown(resource->cap))
+		return alarm;
+
 	resource->power_alarm = resource->power > resource->cap;
 	return alarm || resource->power_alarm;
 }
@@ -460,26 +464,36 @@ static int power_meter_read(struct device *dev, enum hwmon_sensor_types type,
 		ret = update_meter(res);
 		if (ret)
 			return ret;
-		if (res->power == UNKNOWN_POWER_32 || res->power == UNKNOWN_POWER_64)
+		if (acpi_power_meter_is_unknown(res->power))
 			return -ENODATA;
 		*val = res->power * 1000;
 		break;
 	case hwmon_power_average_interval_min:
+		if (acpi_power_meter_is_unknown(res->caps.min_avg_interval))
+			return -ENODATA;
 		*val = res->caps.min_avg_interval;
 		break;
 	case hwmon_power_average_interval_max:
+		if (acpi_power_meter_is_unknown(res->caps.max_avg_interval))
+			return -ENODATA;
 		*val = res->caps.max_avg_interval;
 		break;
 	case hwmon_power_average_interval:
 		ret = update_avg_interval(res);
 		if (ret)
 			return ret;
-		*val = (res)->avg_interval;
+		if (acpi_power_meter_is_unknown(res->avg_interval))
+			return -ENODATA;
+		*val = res->avg_interval;
 		break;
 	case hwmon_power_cap_min:
+		if (acpi_power_meter_is_unknown(res->caps.min_cap))
+			return -ENODATA;
 		*val = res->caps.min_cap * 1000;
 		break;
 	case hwmon_power_cap_max:
+		if (acpi_power_meter_is_unknown(res->caps.max_cap))
+			return -ENODATA;
 		*val = res->caps.max_cap * 1000;
 		break;
 	case hwmon_power_alarm:
@@ -489,6 +503,8 @@ static int power_meter_read(struct device *dev, enum hwmon_sensor_types type,
 		ret = update_cap(res);
 		if (ret)
 			return ret;
+		if (acpi_power_meter_is_unknown(res->cap))
+			return -ENODATA;
 		*val = res->cap * 1000;
 		break;
 	default:
@@ -614,7 +630,7 @@ static ssize_t power1_cap_hyst_show(struct device *dev,
 
 	guard(mutex)(&res->lock);
 
-	if (res->caps.hysteresis == UNKNOWN_HYSTERESIS)
+	if (acpi_power_meter_is_unknown(res->caps.hysteresis))
 		return sysfs_emit(buf, "unknown\n");
 
 	return sysfs_emit(buf, "%llu\n", res->caps.hysteresis * 1000);
@@ -629,6 +645,9 @@ static ssize_t power1_accuracy_show(struct device *dev,
 
 	guard(mutex)(&res->lock);
 
+	if (acpi_power_meter_is_unknown(res->caps.accuracy))
+		return sysfs_emit(buf, "unknown\n");
+
 	acc = res->caps.accuracy;
 	return sysfs_emit(buf, "%u.%u%%\n", acc / 1000, acc % 1000);
 }
-- 
2.45.2


  parent reply	other threads:[~2026-10-31 16:54 UTC|newest]

Thread overview: 7+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-10-31 16:54 [PATCH 0/3] hwmon: (acpi_power_meter) Fix locking and unknown value handling Guenter Roeck
2026-10-31 16:54 ` [PATCH 1/3] hwmon: (acpi_power_meter) Fix missing locking in sysfs show callbacks and resume Guenter Roeck
2026-10-31 16:58   ` sashiko-bot
2026-10-31 16:54 ` [PATCH 2/3] hwmon: (acpi_power_meter) Prevent spurious power alarm when power is unknown Guenter Roeck
2026-10-31 17:02   ` sashiko-bot
2026-10-31 16:54 ` Guenter Roeck [this message]
2026-10-31 17:02   ` [PATCH 3/3] hwmon: (acpi_power_meter) Check for 32-bit and 64-bit unknown ACPI values sashiko-bot

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20261031165447.1771473-4-linux@roeck-us.net \
    --to=linux@roeck-us.net \
    --cc=linux-hwmon@vger.kernel.org \
    --cc=sashiko-bot@kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox