Skip to content

sysfs: don't hang on cpufreq files that return EAGAIN - #879

Open
blovett wants to merge 1 commit into
prometheus:masterfrom
blovett:sysfs-cpufreq-eagain
Open

blovett wants to merge 1 commit into
prometheus:masterfrom
blovett:sysfs-cpufreq-eagain

Conversation

@blovett

@blovett blovett commented Oct 8, 2026

Copy link
Copy Markdown

Fixes prometheus/node_exporter#3791.

On arm64, a read of cpuinfo_avg_freq can return EAGAIN. This occurs when all CPUs in the policy are idle. #829 added this file to SystemCpufreq.

os.ReadFile adds the file to the Go netpoller. After EAGAIN, it waits for a readiness event. kernfs does not send this event. Because of this, the read does not complete and SystemCpufreq does not return. For each scrape, node_exporter 1.12 does not release one goroutine and one file descriptor.

SysReadFile prevents this problem. parseCpufreqCpuinfo uses it for the string attributes. This change also uses SysReadUintFromFile for the uint attributes. If the read returns EAGAIN, the function does not use the value. This is the same as for a missing file.

Testing

make test and golangci-lint pass. The test fixtures cannot return EAGAIN. For this reason, this change does not add a unit test. If you want a unit test, I can add one.

I built node_exporter 1.12.1 with procfs master, and with procfs master and this change. I scraped each binary 20 times on a Radxa Orion O6 (arm64, cppc_cpufreq):

Successful scrapes Goroutines File descriptors
master 8/20 4 → 86 7 → 53
this change 20/20 4 → 4 7 → 7

The end-to-end test output is the same for the two binaries.

#861 is a different solution. It sets a 5 s deadline for each read. This also stops the leak. But on an idle system, most calls took 5 s.

@SuperQ

Generated with assistance by Claude Code.

On arm64, reading cpuinfo_avg_freq returns EAGAIN when the CPU has not
ticked recently and every other CPU in the policy is idle
(arch_freq_get_on_cpu: "No alternative to pull info from").

os.ReadFile registers sysfs fds with the netpoller, and on EAGAIN waits
for a readiness edge that kernfs never sends, so the read never returns.
SystemCpufreq waits on every per-CPU read, so the whole call hangs, and
node_exporter's cpufreq collector leaks a goroutine and an fd on every
scrape.

Read the uint attributes with SysReadUintFromFile, which already exists
for this failure mode and is already used for the string attributes, and
skip the value on EAGAIN like a missing or unreadable file.

Fixes prometheus/node_exporter#3791

Signed-off-by: Ben Lovett <64740+blovett@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

cpufreq collector hangs forever on arm64 with cppc_cpufreq, leaking a goroutine and an fd per scrape

1 participant