Skip to content

fix: read cgroupv2 cpu usage from the cgroup that owns cpu.max - #42

Draft
DanielleMaywood wants to merge 2 commits into
mainfrom
danielle/cgroupv2-cpu-usage-limit-owner
Draft

DanielleMaywood wants to merge 2 commits into
mainfrom
danielle/cgroupv2-cpu-usage-limit-owner

Conversation

@DanielleMaywood

Copy link
Copy Markdown
Collaborator

Fixes #41.

In cgroup v2, cpuTotal() found cpu.max by walking up to an ancestor, but cpuUsed() still read cpu.stat from the process's own cgroup. In docker-in-docker and devcontainer workspaces the agent sits in /init.scope while nested containers sit in sibling /docker/<id>, so usage was massively understated (0% reported vs 34% actual) even when the container was busy.

Now usage is read from the same cgroup that owns the quota, so used and total describe the same domain. When the process's own cgroup holds the quota, or there is no quota, behavior is unchanged. If the limit owner has no cpu.stat, it falls back to the old behavior.

This mirrors #39, which does the same for memory: cpuQuota() becomes cpuLimit() returning the owning cgroup, and cpuUsedUs() is split out so usage can be read from that owner.

Added CPU/UsageFromLimitOwner with an /init.scope + /docker/<id> fixture; it fails on main (0.1 cores) and passes with this change (2.0 cores).

Verification and decisions

Kernel behavior

  • cpu.stat includes descendants: cgroup_base_stat_flush() in kernel/cgroup/rstat.c (v6.17) adds each cgroup's CPU time into its parent's bstat, which is what cpu.stat prints. cpu.stat.local is the file that excludes descendants.
  • cpu.max applies to the cgroup and everything below it (cgroup v2 docs).

Live check (sysbox workspace, root cpu.max = 1200000 100000, agent in /init.scope, 4 busy loops in a nested container):

Build ContainerCPU()
main 0.017/12 cores (0%)
this PR 4.05/12 cores (34%)

Decisions

Checks

  • go test -race -count=3 ./... passes, skipping TestCgroupV2Detection and TestIsContainerized, which also fail on main when run inside a container.
  • golangci-lint v1.64.7 and gofumpt v0.6.0 are clean.

Generated by Coder Agents on behalf of @DanielleMaywood.

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.

bug: CPU usage misses other processes that share the same CPU limit (cgroup v2)

1 participant