arch/x86/entry/syscalls/syscall_64.tbl | 1 + include/linux/syscalls.h | 2 + kernel/sys.c | 138 +++++++++++++++++++++++++ 3 files changed, 141 insertions(+)
Add a new syscall that reports GPU presence and system-wide
energy information:
- mode 0: returns the number of display devices found on the PCI bus
- mode 1: returns an energy efficiency metric; prefers the real GPU
power reported through hwmon, falls back to runtime PM status
weights, plus the online CPU count
- mode 2: returns the real-time GPU power in milliwatts read from
the hwmon power1_input channel exposed by GPU drivers
(amdgpu/nouveau/radeon/nvidia); returns 0 if not reported
Signed-off-by: 林濬哲 <m18667909625@163.com>
Assisted-by: AI coding assistant (disclosed per kernel AI guidelines)
---
arch/x86/entry/syscalls/syscall_64.tbl | 1 +
include/linux/syscalls.h | 2 +
kernel/sys.c | 138 +++++++++++++++++++++++++
3 files changed, 141 insertions(+)
diff --git a/arch/x86/entry/syscalls/syscall_64.tbl b/arch/x86/entry/syscalls/syscall_64.tbl
index d5b6045b0090..f99649dfaa38 100644
--- a/arch/x86/entry/syscalls/syscall_64.tbl
+++ b/arch/x86/entry/syscalls/syscall_64.tbl
@@ -346,6 +346,7 @@
334 common rseq sys_rseq
335 common uretprobe sys_uretprobe
336 common uprobe sys_uprobe
+337 common system_energy_efficiency sys_system_energy_efficiency
# don't use numbers 387 through 423, add new calls after the last
# 'common' entry
424 common pidfd_send_signal sys_pidfd_send_signal
diff --git a/include/linux/syscalls.h b/include/linux/syscalls.h
index 8413b624ad47..dfa9d31c20cd 100644
--- a/include/linux/syscalls.h
+++ b/include/linux/syscalls.h
@@ -1017,6 +1017,8 @@ asmlinkage long sys_uretprobe(void);
asmlinkage long sys_uprobe(void);
+asmlinkage long sys_system_energy_efficiency(int __user *mode, int __user *value);
+
/* pciconfig: alpha, arm, arm64, ia64, sparc */
asmlinkage long sys_pciconfig_read(unsigned long bus, unsigned long dfn,
unsigned long off, unsigned long len,
diff --git a/kernel/sys.c b/kernel/sys.c
index 35b538ba843c..bb1106085d7e 100644
--- a/kernel/sys.c
+++ b/kernel/sys.c
@@ -22,6 +22,7 @@
#include <linux/workqueue.h>
#include <linux/capability.h>
#include <linux/device.h>
+#include <linux/pci.h>
#include <linux/key.h>
#include <linux/times.h>
#include <linux/posix-timers.h>
@@ -181,6 +182,143 @@ int fs_overflowgid = DEFAULT_FS_OVERFLOWGID;
EXPORT_SYMBOL(fs_overflowuid);
EXPORT_SYMBOL(fs_overflowgid);
+
+/*
+ * Read the real power consumption reported by GPU drivers via hwmon,
+ * in milliwatts. The hwmon ABI exposes power1_input in microwatts;
+ * amdgpu/nouveau/radeon/nvidia provide this channel on supported
+ * hardware. Returns 0 if no driver reports a power reading.
+ */
+static long gpu_hwmon_power_mw(void)
+{
+ static const char * const gpu_drivers[] = {
+ "amdgpu", "nouveau", "radeon", "nvidia",
+ };
+ char buf[32];
+ char path[64];
+ loff_t pos;
+ long total_uw = 0;
+ long val;
+ int i, j, ret;
+
+ for (i = 0; i < 32; i++) {
+ bool is_gpu = false;
+ struct file *fp;
+
+ snprintf(path, sizeof(path), "/sys/class/hwmon/hwmon%d/name", i);
+ fp = filp_open(path, O_RDONLY, 0);
+ if (IS_ERR(fp))
+ continue;
+
+ pos = 0;
+ ret = kernel_read(fp, buf, sizeof(buf) - 1, &pos);
+ filp_close(fp, NULL);
+ if (ret <= 0)
+ continue;
+ buf[ret] = '\0';
+
+ for (j = 0; j < ARRAY_SIZE(gpu_drivers); j++) {
+ if (!strncmp(buf, gpu_drivers[j], strlen(gpu_drivers[j]))) {
+ is_gpu = true;
+ break;
+ }
+ }
+ if (!is_gpu)
+ continue;
+
+ snprintf(path, sizeof(path), "/sys/class/hwmon/hwmon%d/power1_input", i);
+ fp = filp_open(path, O_RDONLY, 0);
+ if (IS_ERR(fp))
+ continue;
+
+ pos = 0;
+ ret = kernel_read(fp, buf, sizeof(buf) - 1, &pos);
+ filp_close(fp, NULL);
+ if (ret <= 0)
+ continue;
+ buf[ret] = '\0';
+
+ if (kstrtol(buf, 10, &val))
+ continue;
+
+ total_uw += val;
+ }
+
+ return total_uw / 1000; /* microwatts -> milliwatts */
+}
+
+static int gpu_energy_estimate(int *gpus)
+{
+ static const int class_list[] = {
+ 0x030000, /* VGA compatible controller */
+ 0x030200, /* 3D controller (modern NVIDIA/AMD GPUs) */
+ 0x030100, /* XGA */
+ 0x030080, /* other display controller */
+ };
+ struct pci_dev *pdev;
+ int i, count = 0, energy = 0;
+
+ for (i = 0; i < ARRAY_SIZE(class_list); i++) {
+ pdev = NULL;
+ while ((pdev = pci_get_class(class_list[i], pdev)) != NULL) {
+ count++;
+ if (pdev->dev.power.runtime_status == RPM_ACTIVE)
+ energy += 100;
+ else
+ energy += 5;
+ }
+ }
+
+ if (gpus)
+ *gpus = count;
+
+ return energy;
+}
+
+SYSCALL_DEFINE2(system_energy_efficiency, int __user *, mode, int __user *, value)
+{
+ int mode_v, ret;
+ int gpu_num, gpu_energy;
+ long gpu_power_mw;
+
+ if (get_user(mode_v, mode))
+ return -EFAULT;
+
+ if (mode_v < 0 || mode_v > 2)
+ return -EINVAL;
+
+ gpu_energy = gpu_energy_estimate(&gpu_num);
+ gpu_power_mw = gpu_hwmon_power_mw();
+
+ switch (mode_v) {
+ case 0:
+ /* number of GPUs, 0 if the system has none */
+ ret = gpu_num;
+ break;
+ case 1:
+ /*
+ * Energy efficiency metric: prefer the real GPU power
+ * (mW) reported by the driver, fall back to runtime PM
+ * status weights if no power channel exists, then add
+ * the CPU part (online CPUs x 10)
+ */
+ ret = (int)(gpu_power_mw ? gpu_power_mw : gpu_energy) +
+ num_online_cpus() * 10;
+ break;
+ case 2:
+ /* real-time GPU power in mW, 0 if not reported */
+ ret = (int)gpu_power_mw;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (put_user(ret, value))
+ return -EFAULT;
+
+ return 0;
+}
+
static const struct ctl_table overflow_sysctl_table[] = {
{
.procname = "overflowuid",
--
2.54.0.windows.1
Hi Greg, Thanks for the quick review! You are right: GPU power readings are already available from userspace via /sys/class/hwmon/*/power1_input, and display devices can be enumerated through /sys/bus/pci/devices. I do not have a use case that requires gathering this data from within the kernel, so please drop this patch. Thanks again for taking the time to review it. Best regards, 林濬哲
On Sun, Sep 20, 2026 at 06:07:13PM +0800, 林濬哲 wrote: > Add a new syscall that reports GPU presence and system-wide > energy information: > > - mode 0: returns the number of display devices found on the PCI bus > - mode 1: returns an energy efficiency metric; prefers the real GPU > power reported through hwmon, falls back to runtime PM status > weights, plus the online CPU count > - mode 2: returns the real-time GPU power in milliwatts read from > the hwmon power1_input channel exposed by GPU drivers > (amdgpu/nouveau/radeon/nvidia); returns 0 if not reported Cute, but you missed a bunch of other gpu drivers, AND all of this can be done today from userspace, no syscall is needed, right? thanks, greg k-h
© 2016 - 2026 Red Hat, Inc.