simple power draw

This commit is contained in:
Adeeb Shihadeh
2026-05-09 15:41:29 -07:00
parent 744b272c37
commit 8651afd778
2 changed files with 169 additions and 174 deletions
+168 -173
View File
@@ -1,12 +1,13 @@
/*
* power_burn_max - CPU/GPU/LED power burn for tici.
* power_burn_max - short CPU/LED-only power blip for boot.
*
* power_burn_max [seconds] [n_perf_cores]
* power_burn_max [seconds] [n_cores]
*
* seconds may be fractional. n_cores defaults to all 8 SDM845 cores; use 4 to
* burn only the big cluster.
*/
#define _GNU_SOURCE
#include <CL/cl.h>
#include <arm_neon.h>
#include <fcntl.h>
#include <pthread.h>
#include <sched.h>
@@ -30,205 +31,199 @@ static void stop_sig(int s) { exit_signum = s; running = 0; }
#define POWER_TEST_MAGIC 0x57505354u /* "WPST" */
static void write_magic(uint32_t val) {
int fd = open("/dev/mem", O_RDWR | O_SYNC);
if (fd < 0) { perror("open /dev/mem"); return; }
const uintptr_t page = POWER_TEST_ADDR & ~0xFFFull;
const uintptr_t off = POWER_TEST_ADDR & 0xFFFull;
void *p = mmap(NULL, 0x1000, PROT_READ | PROT_WRITE, MAP_SHARED, fd, (off_t)page);
if (p == MAP_FAILED) { perror("mmap /dev/mem"); close(fd); return; }
*(volatile uint32_t *)((char *)p + off) = val;
msync(p, 0x1000, MS_SYNC);
munmap(p, 0x1000);
close(fd);
int fd = open("/dev/mem", O_RDWR | O_SYNC);
if (fd < 0) return;
const uintptr_t page = POWER_TEST_ADDR & ~0xFFFull;
const uintptr_t off = POWER_TEST_ADDR & 0xFFFull;
void *p = mmap(NULL, 0x1000, PROT_READ | PROT_WRITE, MAP_SHARED, fd, (off_t)page);
if (p == MAP_FAILED) { close(fd); return; }
*(volatile uint32_t *)((char *)p + off) = val;
msync(p, 0x1000, MS_SYNC);
munmap(p, 0x1000);
close(fd);
}
#define MAX_SAVED 96
static struct { char path[192]; char val[48]; } saved[MAX_SAVED];
#define MAX_SAVED 128
static struct { char path[192]; char val[64]; } saved[MAX_SAVED];
static int nsaved;
static int sysfs_read(const char *p, char *b, int sz) {
FILE *f = fopen(p, "r");
if (!f) return -1;
if (!fgets(b, sz, f)) { fclose(f); return -1; }
fclose(f);
b[strcspn(b, "\n")] = 0;
return 0;
FILE *f = fopen(p, "r");
if (!f) return -1;
if (!fgets(b, sz, f)) { fclose(f); return -1; }
fclose(f);
b[strcspn(b, "\n")] = 0;
return 0;
}
static void sysfs_write(const char *p, const char *v) {
FILE *f = fopen(p, "w");
if (!f) return;
fputs(v, f);
fclose(f);
FILE *f = fopen(p, "w");
if (!f) return;
fputs(v, f);
fclose(f);
}
static void save_and_write(const char *p, const char *v) {
if (nsaved < MAX_SAVED && sysfs_read(p, saved[nsaved].val, sizeof(saved[0].val)) == 0) {
strncpy(saved[nsaved].path, p, sizeof(saved[0].path) - 1);
nsaved++;
}
sysfs_write(p, v);
if (nsaved < MAX_SAVED && sysfs_read(p, saved[nsaved].val, sizeof(saved[0].val)) == 0) {
strncpy(saved[nsaved].path, p, sizeof(saved[0].path) - 1);
saved[nsaved].path[sizeof(saved[0].path) - 1] = 0;
nsaved++;
}
sysfs_write(p, v);
}
static void restore_all(void) {
for (int i = nsaved - 1; i >= 0; i--) sysfs_write(saved[i].path, saved[i].val);
for (int i = nsaved - 1; i >= 0; i--) sysfs_write(saved[i].path, saved[i].val);
}
static void fatal_sig(int s) {
const char *m = "[power_burn_max] fault: signal\n";
switch (s) {
case SIGABRT: m = "[power_burn_max] fault: SIGABRT\n"; break;
case SIGSEGV: m = "[power_burn_max] fault: SIGSEGV\n"; break;
case SIGBUS: m = "[power_burn_max] fault: SIGBUS\n"; break;
}
(void)!write(2, m, strlen(m));
write_magic(0);
restore_all();
_exit(128 + s);
write_magic(0);
restore_all();
_exit(128 + s);
}
static void leds_on(void) {
save_and_write("/sys/class/leds/led:torch_0/brightness", "500");
save_and_write("/sys/class/leds/led:torch_1/brightness", "500");
save_and_write("/sys/class/leds/led:torch_2/brightness", "300");
save_and_write("/sys/class/leds/led:switch_0/brightness", "255");
save_and_write("/sys/class/leds/led:switch_1/brightness", "255");
save_and_write("/sys/class/leds/led:switch_2/brightness", "255");
save_and_write("/sys/class/leds/led:torch_0/brightness", "500");
save_and_write("/sys/class/leds/led:torch_1/brightness", "500");
save_and_write("/sys/class/leds/led:torch_2/brightness", "300");
save_and_write("/sys/class/leds/led:switch_0/brightness", "255");
save_and_write("/sys/class/leds/led:switch_1/brightness", "255");
save_and_write("/sys/class/leds/led:switch_2/brightness", "255");
}
static void freq_max(void) {
/* max_pwrlevel=0 unlocks the 710MHz step that Adreno gates off by default
* even under governor=performance. */
save_and_write("/sys/class/kgsl/kgsl-3d0/devfreq/governor", "performance");
save_and_write("/sys/class/kgsl/kgsl-3d0/max_pwrlevel", "0");
save_and_write("/sys/class/kgsl/kgsl-3d0/force_bus_on", "1");
save_and_write("/sys/class/kgsl/kgsl-3d0/force_clk_on", "1");
save_and_write("/sys/class/kgsl/kgsl-3d0/force_rail_on", "1");
save_and_write("/sys/class/kgsl/kgsl-3d0/force_no_nap", "1");
save_and_write("/sys/class/devfreq/soc:qcom,gpubw/governor", "performance");
save_and_write("/sys/class/devfreq/soc:qcom,cpubw/governor", "performance");
save_and_write("/sys/class/devfreq/soc:qcom,llccbw/governor", "performance");
save_and_write("/sys/class/devfreq/soc:qcom,l3-cpu0/governor","performance");
save_and_write("/sys/class/devfreq/soc:qcom,l3-cpu4/governor","performance");
save_and_write("/sys/class/devfreq/soc:qcom,l3-cdsp/governor","performance");
static void cpu_bus_freq_max(void) {
save_and_write("/sys/class/devfreq/soc:qcom,cpubw/governor", "performance");
save_and_write("/sys/class/devfreq/soc:qcom,llccbw/governor", "performance");
save_and_write("/sys/class/devfreq/soc:qcom,l3-cpu0/governor", "performance");
save_and_write("/sys/class/devfreq/soc:qcom,l3-cpu4/governor", "performance");
}
static void pin_perf_core(int cpu) {
char path[128];
snprintf(path, sizeof(path), "/sys/devices/system/cpu/cpu%d/online", cpu);
save_and_write(path, "1");
snprintf(path, sizeof(path), "/sys/devices/system/cpu/cpu%d/cpufreq/scaling_governor", cpu);
save_and_write(path, "performance");
static void pin_core(int cpu) {
char path[160];
char max_freq[32];
snprintf(path, sizeof(path), "/sys/devices/system/cpu/cpu%d/cpufreq/cpuinfo_max_freq", cpu);
if (sysfs_read(path, max_freq, sizeof(max_freq)) != 0)
max_freq[0] = 0;
snprintf(path, sizeof(path), "/sys/devices/system/cpu/cpu%d/online", cpu);
save_and_write(path, "1");
if (max_freq[0]) {
snprintf(path, sizeof(path), "/sys/devices/system/cpu/cpu%d/cpufreq/scaling_max_freq", cpu);
save_and_write(path, max_freq);
snprintf(path, sizeof(path), "/sys/devices/system/cpu/cpu%d/cpufreq/scaling_min_freq", cpu);
save_and_write(path, max_freq);
}
snprintf(path, sizeof(path), "/sys/devices/system/cpu/cpu%d/cpufreq/scaling_governor", cpu);
save_and_write(path, "performance");
}
static void *cpu_burn(void *arg) {
int core = (int)(intptr_t)arg;
cpu_set_t set; CPU_ZERO(&set); CPU_SET(core, &set);
pthread_setaffinity_np(pthread_self(), sizeof(set), &set);
int core = (int)(intptr_t)arg;
cpu_set_t set; CPU_ZERO(&set); CPU_SET(core, &set);
pthread_setaffinity_np(pthread_self(), sizeof(set), &set);
/* Contraction map x := 0.5*x + 0.25 — fixed point at 0.5, keeps values
* bounded away from subnormal/NaN paths that short-circuit the FPU. */
float32x4_t v0 = vdupq_n_f32(0.50f + core * 0.01f);
float32x4_t v1 = vdupq_n_f32(0.25f);
float32x4_t v2 = vdupq_n_f32(0.75f);
float32x4_t v3 = vdupq_n_f32(0.125f);
float32x4_t v4 = vdupq_n_f32(0.875f);
float32x4_t v5 = vdupq_n_f32(0.625f);
float32x4_t v6 = vdupq_n_f32(0.375f);
float32x4_t v7 = vdupq_n_f32(0.1875f);
const float32x4_t half = vdupq_n_f32(0.5f);
const float32x4_t quarter = vdupq_n_f32(0.25f);
while (running) {
for (int i = 0; i < 10000; i++) {
v0 = vfmaq_f32(quarter, v0, half);
v1 = vfmaq_f32(quarter, v1, half);
v2 = vfmaq_f32(quarter, v2, half);
v3 = vfmaq_f32(quarter, v3, half);
v4 = vfmaq_f32(quarter, v4, half);
v5 = vfmaq_f32(quarter, v5, half);
v6 = vfmaq_f32(quarter, v6, half);
v7 = vfmaq_f32(quarter, v7, half);
}
}
volatile float sink = vgetq_lane_f32(v0,0) + vgetq_lane_f32(v1,0) + vgetq_lane_f32(v2,0) +
vgetq_lane_f32(v3,0) + vgetq_lane_f32(v4,0) + vgetq_lane_f32(v5,0) +
vgetq_lane_f32(v6,0) + vgetq_lane_f32(v7,0);
(void)sink;
void *mem = NULL;
if (posix_memalign(&mem, 64, 4096) != 0)
return NULL;
memset(mem, core, 4096);
const uint32_t a = 0x3f000000u + (uint32_t)core;
const uint32_t b = 0x3e800000u + (uint32_t)core;
const uint32_t c = 0x3f4ccccdu + (uint32_t)core;
while (running) {
asm volatile(
"dup v0.4s, %w[a]\n"
"dup v1.4s, %w[b]\n"
"dup v2.4s, %w[c]\n"
"dup v3.4s, %w[a]\n"
"dup v4.4s, %w[b]\n"
"dup v5.4s, %w[c]\n"
"dup v6.4s, %w[a]\n"
"dup v7.4s, %w[b]\n"
"dup v16.4s, %w[c]\n"
"dup v17.4s, %w[a]\n"
"dup v18.4s, %w[b]\n"
"dup v19.4s, %w[c]\n"
"mov x9, #10000\n"
"1:\n"
"ldp q20, q21, [%[p], #0]\n"
"fmla v0.4s, v16.4s, v17.4s\n"
"fmla v1.4s, v17.4s, v18.4s\n"
"fmla v2.4s, v18.4s, v19.4s\n"
"fmla v3.4s, v19.4s, v16.4s\n"
"ldp q22, q23, [%[p], #32]\n"
"fmla v4.4s, v16.4s, v18.4s\n"
"fmla v5.4s, v17.4s, v19.4s\n"
"fmla v6.4s, v18.4s, v16.4s\n"
"fmla v7.4s, v19.4s, v17.4s\n"
"eor v20.16b, v20.16b, v22.16b\n"
"fmla v8.4s, v0.4s, v17.4s\n"
"fmla v9.4s, v1.4s, v18.4s\n"
"fmla v10.4s, v2.4s, v19.4s\n"
"fmla v11.4s, v3.4s, v16.4s\n"
"eor v21.16b, v21.16b, v23.16b\n"
"fmla v12.4s, v4.4s, v18.4s\n"
"fmla v13.4s, v5.4s, v19.4s\n"
"fmla v14.4s, v6.4s, v16.4s\n"
"fmla v15.4s, v7.4s, v17.4s\n"
"stp q20, q21, [%[p], #0]\n"
"stp q22, q23, [%[p], #32]\n"
"subs x9, x9, #1\n"
"b.ne 1b\n"
:
: [p] "r" (mem), [a] "r" (a), [b] "r" (b), [c] "r" (c)
: "x9", "cc", "memory",
"v0", "v1", "v2", "v3", "v4", "v5", "v6", "v7",
"v8", "v9", "v10", "v11", "v12", "v13", "v14", "v15",
"v16", "v17", "v18", "v19", "v20", "v21", "v22", "v23");
}
free(mem);
return NULL;
}
static const char *GPU_KSRC =
"__kernel void burn(__global float *buf, int n) {"
" int id = get_global_id(0);"
" float4 a = (float4)(0.6f,0.7f,0.8f,0.9f) + (float)(id&15)*0.001f;"
" float4 b=a*0.5f, c=a*0.75f, d=a*0.8f, e=a*0.9f, f=a*0.55f, g=a*0.65f, h=a*0.85f;"
" for (int i = 0; i < n; i++) {"
" a = mad(a,(float4)(0.5f),(float4)(0.25f));"
" b = mad(b,(float4)(0.5f),(float4)(0.25f));"
" c = mad(c,(float4)(0.5f),(float4)(0.25f));"
" d = mad(d,(float4)(0.5f),(float4)(0.25f));"
" e = mad(e,(float4)(0.5f),(float4)(0.25f));"
" f = mad(f,(float4)(0.5f),(float4)(0.25f));"
" g = mad(g,(float4)(0.5f),(float4)(0.25f));"
" h = mad(h,(float4)(0.5f),(float4)(0.25f));"
" }"
" if ((id&1023)==0) buf[id/1024] = a.x+b.x+c.x+d.x+e.x+f.x+g.x+h.x;"
"}";
static uint64_t mono_ns(void) {
struct timespec ts;
clock_gettime(CLOCK_MONOTONIC, &ts);
return (uint64_t)ts.tv_sec * 1000000000ull + ts.tv_nsec;
}
int main(int argc, char **argv) {
int duration = 1, n_perf = 4;
if (argc > 1) duration = atoi(argv[1]);
if (argc > 2) n_perf = atoi(argv[2]);
if (n_perf < 0) n_perf = 0; if (n_perf > 4) n_perf = 4;
double duration_s = argc > 1 ? strtod(argv[1], NULL) : 1.0;
int n_cores = argc > 2 ? atoi(argv[2]) : 8;
if (n_cores < 0) n_cores = 0;
if (n_cores > 8) n_cores = 8;
int first_cpu = n_cores > 4 ? 0 : 4;
signal(SIGTERM, stop_sig);
signal(SIGINT, stop_sig);
signal(SIGABRT, fatal_sig);
signal(SIGSEGV, fatal_sig);
signal(SIGBUS, fatal_sig);
signal(SIGTERM, stop_sig);
signal(SIGINT, stop_sig);
signal(SIGABRT, fatal_sig);
signal(SIGSEGV, fatal_sig);
signal(SIGBUS, fatal_sig);
freq_max();
for (int i = 0; i < n_perf; i++) pin_perf_core(4 + i);
cpu_bus_freq_max();
for (int i = 0; i < n_cores; i++) pin_core(first_cpu + i);
write_magic(POWER_TEST_MAGIC);
leds_on();
cl_platform_id plat; cl_device_id dev; cl_int err;
clGetPlatformIDs(1, &plat, NULL);
clGetDeviceIDs(plat, CL_DEVICE_TYPE_GPU, 1, &dev, NULL);
cl_context ctx = clCreateContext(NULL, 1, &dev, NULL, NULL, &err);
cl_command_queue q = clCreateCommandQueue(ctx, dev, 0, &err);
cl_program prog = clCreateProgramWithSource(ctx, 1, &GPU_KSRC, NULL, &err);
clBuildProgram(prog, 1, &dev, "-cl-fast-relaxed-math -cl-mad-enable", NULL, NULL);
cl_kernel kern = clCreateKernel(prog, "burn", &err);
cl_mem buf = clCreateBuffer(ctx, CL_MEM_WRITE_ONLY, 4096 * sizeof(float), NULL, &err);
int n_inner = 200;
clSetKernelArg(kern, 0, sizeof(cl_mem), &buf);
clSetKernelArg(kern, 1, sizeof(int), &n_inner);
const size_t gs = 65536;
pthread_t th[8];
for (int i = 0; i < n_cores; i++)
pthread_create(&th[i], NULL, cpu_burn, (void *)(intptr_t)(first_cpu + i));
write_magic(POWER_TEST_MAGIC);
uint64_t end_ns = duration_s > 0.0 ? mono_ns() + (uint64_t)(duration_s * 1000000000.0) : 0;
while (running) {
if (end_ns && mono_ns() >= end_ns) break;
usleep(1000);
}
running = 0;
pthread_t cpu_th[4];
for (int i = 0; i < n_perf; i++)
pthread_create(&cpu_th[i], NULL, cpu_burn, (void *)(intptr_t)(4 + i));
leds_on();
struct timespec ts;
clock_gettime(CLOCK_MONOTONIC, &ts);
uint64_t t_end_ns = (uint64_t)ts.tv_sec * 1000000000ull + ts.tv_nsec + (uint64_t)duration * 1000000000ull;
while (running) {
clEnqueueNDRangeKernel(q, kern, 1, NULL, &gs, NULL, 0, NULL, NULL);
clFinish(q);
if (duration > 0) {
clock_gettime(CLOCK_MONOTONIC, &ts);
if ((uint64_t)ts.tv_sec * 1000000000ull + ts.tv_nsec >= t_end_ns) break;
}
}
running = 0;
for (int i = 0; i < n_perf; i++) pthread_join(cpu_th[i], NULL);
clReleaseMemObject(buf);
clReleaseKernel(kern);
clReleaseProgram(prog);
clReleaseCommandQueue(q);
clReleaseContext(ctx);
restore_all();
write_magic(0);
if (exit_signum) fprintf(stderr, "[power_burn_max] interrupted by signal %d\n", (int)exit_signum);
return 0;
for (int i = 0; i < n_cores; i++) pthread_join(th[i], NULL);
restore_all();
write_magic(0);
if (exit_signum) fprintf(stderr, "[power_burn_max] interrupted by signal %d\n", (int)exit_signum);
return 0;
}
+1 -1
View File
@@ -10,7 +10,7 @@ RESET_TRIGGER="/data/__system_reset__"
# blip power to ~10W to see if the PSU is stable
if grep -q mici /sys/firmware/devicetree/base/model; then
sudo timeout --kill-after=2 5 /home/comma/power_burn_max
sudo timeout --kill-after=2 5 /home/comma/power_burn_max 0.3 8
fi
# use max freq to boot up quickly, then limit