Skip to content

block: expose blkcg io.stat to BPF - #8659

Open
kernel-patches-daemon-bpf-rc[bot] wants to merge 2 commits into
bpf-next_basefrom
series/1142393=>bpf-next
Open

block: expose blkcg io.stat to BPF#8659
kernel-patches-daemon-bpf-rc[bot] wants to merge 2 commits into
bpf-next_basefrom
series/1142393=>bpf-next

Conversation

@kernel-patches-daemon-bpf-rc

Copy link
Copy Markdown

Pull request for series with
subject: block: expose blkcg io.stat to BPF
version: 1
url: https://patchwork.kernel.org/project/netdevbpf/list/?series=1142393

@kernel-patches-daemon-bpf-rc

Copy link
Copy Markdown
Author

Upstream branch: 2b1f9f6
series: https://patchwork.kernel.org/project/netdevbpf/list/?series=1142393
version: 1

@kernel-patches-daemon-bpf-rc

Copy link
Copy Markdown
Author

Upstream branch: e1d9b82
series: https://patchwork.kernel.org/project/netdevbpf/list/?series=1142393
version: 1

@kernel-patches-daemon-bpf-rc

Copy link
Copy Markdown
Author

Upstream branch: 51476f6
series: https://patchwork.kernel.org/project/netdevbpf/list/?series=1142393
version: 1

@kernel-patches-daemon-bpf-rc

Copy link
Copy Markdown
Author

Upstream branch: 8b365b3
series: https://patchwork.kernel.org/project/netdevbpf/list/?series=1142393
version: 1

@kernel-patches-daemon-bpf-rc

Copy link
Copy Markdown
Author

Upstream branch: d114bb9
series: https://patchwork.kernel.org/project/netdevbpf/list/?series=1142393
version: 1

Ziyang Men added 2 commits August 8, 2026 03:18
Expose the block I/O controller's per-device statistics to BPF,
mirroring the memory controller kfuncs in mm/bpf_memcontrol.c.

A BPF program gets a blkcg from a cgroup's css with bpf_get_blkcg()
(or bpf_get_root_blkcg() for the root), flushes the stats with
bpf_blkcg_flush_stats(), then walks the cgroup's per-device blkgs with
the bpf_iter_blkg open-coded iterator and reads each device's counters
with bpf_blkg_iostat_bytes() and bpf_blkg_iostat_ios().  bpf_blkg_dev()
returns the device id for labelling.  The reference is released with
bpf_put_blkcg().

Unlike the memory controller, blkcg keeps one blkg (and one io.stat
line) per block device, so the reader kfuncs take a blkg and the
iterator yields them under RCU.  The counters are read under the same
u64_stats seqlock the io.stat file uses, so the kfuncs add no fast-path
cost: accounting stays in the per-cpu blkg iostat and is only folded on
flush.

bpf_blkcg_flush_stats() branches the way blkcg_print_stat() does.  A
non-root cgroup is flushed through rstat.  The root cgroup is not
accounted through rstat at all - blkcg_rstat_flush() returns early for
it and __blkcg_rstat_flush() stops propagating one level short - so its
per-device aggregates are refilled from the disks' own statistics
instead, by blkcg_fill_root_iostats(), which is no longer static for
that reason.  Without this a program reading the root cgroup would see
zeroes.  Those numbers cover every cgroup's I/O, exactly as the root
io.stat file reports them.

Two details are worth calling out:

bpf_iter_blkg_next() forgets the list head once the walk ends, not just
the position.  process_iter_next_call() in the verifier requires an
iterator to keep returning NULL once it has returned it, and stops
checking the loop for termination at that point; restarting the walk
would let such a loop spin forever.

The counter readers give up instead of retrying when called from NMI on
32-bit.  There the u64_stats read is a real seqcount loop, every writer
of blkg->iostat keeps interrupts off, and a perf event program can call
these kfuncs from NMI, where the loop would never end.  On 64-bit the
loop compiles away.

Signed-off-by: Ziyang Men <ziyang.meme@gmail.com>
Add cgroup_iter_io, a test_progs test for the block I/O controller BPF
kfuncs.  A SEC("iter.s/cgroup") program acquires the cgroup's blkcg,
flushes stats, iterates its blkgs and reads the io.stat counters for a
target device.

The userspace side attaches a loop device, generates O_DIRECT read and
write I/O charged to a test cgroup, and then:

  - checks the write and read byte/io counters are nonzero,
  - checks the reported device id,
  - compares every kfunc-read value against the cgroup's io.stat file
    for the same device and requires an exact match,
  - reads the same device through bpf_get_root_blkcg() and checks the
    root counters are at or above the test cgroup's.

The measured device is pinned to the loop device, which has no
asynchronous writeback, so the kfunc snapshot and the io.stat file
snapshot are identical rather than merely close.  The root cgroup's
numbers for a device come from the disk itself and so cover every
cgroup's I/O to it, which is why the root check is "at or above" rather
than an exact match.

CONFIG_BLK_CGROUP is added to the test config; CONFIG_BLK_DEV_LOOP is
already present.

Signed-off-by: Ziyang Men <ziyang.meme@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

0 participants