feat: HWP scaling, C1E, closed-loop thermal governor for Intel CPUs
This commit is contained in:
@@ -200,6 +200,9 @@ namespace montauk::abi {
|
||||
static constexpr uint64_t SYS_SDR_SETPARAM = 147; // (handle, param, value)
|
||||
static constexpr uint64_t SYS_SDR_GETPARAM = 148; // (handle, param) -> value
|
||||
|
||||
// CPU power/thermal status
|
||||
static constexpr uint64_t SYS_POWERINFO = 149; // (PowerInfo*) -> 0, -1 unsupported
|
||||
|
||||
// Tunable parameters (for SYS_SDR_SETPARAM / SYS_SDR_GETPARAM).
|
||||
static constexpr int SDR_PARAM_FREQ = 0; // center frequency, Hz
|
||||
static constexpr int SDR_PARAM_SAMPLE_RATE = 1; // sample rate, Hz
|
||||
@@ -493,6 +496,22 @@ namespace montauk::abi {
|
||||
uint32_t _pad;
|
||||
};
|
||||
|
||||
// CPU power/thermal snapshot (returned by SYS_POWERINFO)
|
||||
struct PowerInfo {
|
||||
uint8_t hwpActive; // hardware P-state scaling enabled
|
||||
uint8_t throttling; // thermal governor currently limiting frequency
|
||||
uint8_t tempC; // package temperature, degrees C (0 = unknown)
|
||||
uint8_t tjMaxC; // hardware throttle temperature
|
||||
uint8_t highestPerf; // HWP performance range (ratio units)
|
||||
uint8_t lowestPerf;
|
||||
uint8_t curMaxPerf; // thermal governor's current ceiling
|
||||
uint8_t epp; // energy/perf preference (0=perf, 255=power)
|
||||
uint32_t baseMHz; // nominal base frequency (0 = unknown)
|
||||
uint32_t maxMHz; // max turbo frequency
|
||||
uint32_t effMHz; // measured average active frequency
|
||||
uint32_t apIdleHint; // MWAIT hint used for AP deep idle
|
||||
};
|
||||
|
||||
struct ProcInfo {
|
||||
int32_t pid;
|
||||
int32_t parentPid;
|
||||
|
||||
@@ -172,6 +172,7 @@ extern "C" {
|
||||
#define MTK_SYS_SDR_READ 146
|
||||
#define MTK_SYS_SDR_SETPARAM 147
|
||||
#define MTK_SYS_SDR_GETPARAM 148
|
||||
#define MTK_SYS_POWERINFO 149
|
||||
/* @SYSCALLS-END */
|
||||
|
||||
#define MTK_SOCK_TCP 1
|
||||
|
||||
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,152 @@
|
||||
/*
|
||||
* main.cpp
|
||||
* power - CPU power/thermal status tool.
|
||||
*
|
||||
* Reads the kernel's power management snapshot (SYS_POWERINFO): package
|
||||
* temperature, HWP frequency-scaling state, the thermal governor's
|
||||
* current frequency ceiling, and the measured average active frequency.
|
||||
* Useful for verifying that HWP and the thermal governor are doing their
|
||||
* job on real hardware.
|
||||
*
|
||||
* Usage:
|
||||
* power print the current snapshot
|
||||
* power watch sample once per second until 60 s elapse
|
||||
* power watch <secs> sample once per second for <secs> seconds
|
||||
* Copyright (c) 2026 Daniel Hammer
|
||||
*/
|
||||
|
||||
#include <montauk/syscall.h>
|
||||
|
||||
using namespace montauk;
|
||||
|
||||
static void put_u64(uint64_t n) {
|
||||
char buf[24];
|
||||
int i = 0;
|
||||
if (n == 0) { putchar('0'); return; }
|
||||
while (n) { buf[i++] = (char)('0' + (n % 10)); n /= 10; }
|
||||
while (i) putchar(buf[--i]);
|
||||
}
|
||||
|
||||
static void put_hex8(uint8_t v) {
|
||||
static const char* digits = "0123456789ABCDEF";
|
||||
print("0x");
|
||||
putchar(digits[(v >> 4) & 0xF]);
|
||||
putchar(digits[v & 0xF]);
|
||||
}
|
||||
|
||||
static bool read_info(montauk::abi::PowerInfo& info) {
|
||||
return syscall1(montauk::abi::SYS_POWERINFO, (uint64_t)&info) == 0;
|
||||
}
|
||||
|
||||
static void print_snapshot(const montauk::abi::PowerInfo& info) {
|
||||
print("CPU power/thermal status:\n");
|
||||
|
||||
print(" HWP: ");
|
||||
if (info.hwpActive) {
|
||||
print("active (EPP ");
|
||||
put_hex8(info.epp);
|
||||
print(", perf range ");
|
||||
put_u64(info.lowestPerf);
|
||||
print("-");
|
||||
put_u64(info.highestPerf);
|
||||
print(")\n");
|
||||
} else {
|
||||
print("not active\n");
|
||||
}
|
||||
|
||||
print(" Frequency: base ");
|
||||
put_u64(info.baseMHz);
|
||||
print(" MHz, turbo ");
|
||||
put_u64(info.maxMHz);
|
||||
print(" MHz, avg active ");
|
||||
put_u64(info.effMHz);
|
||||
print(" MHz\n");
|
||||
|
||||
print(" Package: ");
|
||||
if (info.tempC != 0) {
|
||||
put_u64(info.tempC);
|
||||
print(" C (hardware throttles at ");
|
||||
put_u64(info.tjMaxC);
|
||||
print(" C)\n");
|
||||
} else {
|
||||
print("temperature unavailable\n");
|
||||
}
|
||||
|
||||
print(" Governor: ");
|
||||
if (!info.hwpActive) {
|
||||
print("inactive\n");
|
||||
} else if (info.throttling) {
|
||||
print("THROTTLING - ceiling ");
|
||||
put_u64(info.curMaxPerf);
|
||||
print(" of ");
|
||||
put_u64(info.highestPerf);
|
||||
print("\n");
|
||||
} else {
|
||||
print("not throttling (ceiling ");
|
||||
put_u64(info.curMaxPerf);
|
||||
print(")\n");
|
||||
}
|
||||
|
||||
print(" AP idle: MWAIT hint ");
|
||||
put_hex8((uint8_t)info.apIdleHint);
|
||||
if (info.apIdleHint == 0) {
|
||||
print(" (C1 only)");
|
||||
}
|
||||
print("\n");
|
||||
}
|
||||
|
||||
static void print_watch_line(const montauk::abi::PowerInfo& info, uint64_t t) {
|
||||
put_u64(t);
|
||||
print("s ");
|
||||
put_u64(info.tempC);
|
||||
print(" C avg ");
|
||||
put_u64(info.effMHz);
|
||||
print(" MHz ceiling ");
|
||||
put_u64(info.curMaxPerf);
|
||||
print("/");
|
||||
put_u64(info.highestPerf);
|
||||
print(info.throttling ? " THROTTLING\n" : "\n");
|
||||
}
|
||||
|
||||
static uint64_t parse_u64(const char* s, uint64_t fallback) {
|
||||
uint64_t v = 0;
|
||||
bool any = false;
|
||||
while (*s == ' ') s++;
|
||||
while (*s >= '0' && *s <= '9') { v = v * 10 + (uint64_t)(*s - '0'); s++; any = true; }
|
||||
return any ? v : fallback;
|
||||
}
|
||||
|
||||
extern "C" void _start() {
|
||||
char args[64];
|
||||
int alen = montauk::getargs(args, sizeof(args));
|
||||
const char* rest = (alen > 0) ? args : "";
|
||||
|
||||
montauk::abi::PowerInfo info{};
|
||||
if (!read_info(info)) {
|
||||
print("power: no CPU power management available\n");
|
||||
print("(non-Intel CPU or running under emulation)\n");
|
||||
montauk::exit(1);
|
||||
}
|
||||
|
||||
bool watch = rest[0] == 'w';
|
||||
if (!watch) {
|
||||
print_snapshot(info);
|
||||
montauk::exit(0);
|
||||
}
|
||||
|
||||
const char* p = rest;
|
||||
while (*p && *p != ' ') p++;
|
||||
uint64_t seconds = parse_u64(p, 60);
|
||||
if (seconds == 0) seconds = 60;
|
||||
|
||||
print("time temp freq ceiling (1 sample/s, ");
|
||||
put_u64(seconds);
|
||||
print(" s)\n");
|
||||
|
||||
for (uint64_t t = 0; t < seconds; t++) {
|
||||
if (!read_info(info)) break;
|
||||
print_watch_line(info, t);
|
||||
montauk::sleep_ms(1000);
|
||||
}
|
||||
montauk::exit(0);
|
||||
}
|
||||
Reference in New Issue
Block a user