Description
Symptom
runsc fails on a kernel with
- cgroup v1, and
CONFIG_BLK_CGROUP disabled
Root Cause
992a840889 intends to make cgroup blkio optional. But it skips iff the BlockIO struct is nil.
Docker always populates that struct, even when no I/O limit was requested.
Inspect Container Config
docker run --rm -v /run:/rc:ro alpine sh -c 'grep -o "\"blockIO\":{[^}]*}" $(find /rc -path "*/io.containerd.runtime.v2.task/moby/$(hostname)*/config.json")'
Output
// Docker v24.0.2
"blockIO":{"weight":0}
// Newer Docker
"blockIO":{}
Proposal
In addition to nil check, blockIO.skip() checks whether all fields of LinuxBlockIO are default.
func (*blockIO) skip(spec *specs.LinuxResources) error {
if spec == nil || spec.BlockIO == nil {
return nil
}
b := spec.BlockIO
if (b.Weight != nil && *b.Weight != 0) ||
(b.LeafWeight != nil && *b.LeafWeight != 0) ||
len(b.WeightDevice) > 0 ||
len(b.ThrottleReadBpsDevice) > 0 ||
len(b.ThrottleWriteBpsDevice) > 0 ||
len(b.ThrottleReadIOPSDevice) > 0 ||
len(b.ThrottleWriteIOPSDevice) > 0 {
return fmt.Errorf("blkio controller is missing but limits are set in OCI spec")
}
return nil
}
Steps to reproduce
Docker Runtime Failure
Requirements (if infeasible, try Unit Test Failure instead)
- cgroup v1
CONFIG_BLK_CGROUP disabled. i.e. build the kernel with CONFIG_BLK_CGROUP=n
Command
docker run --rm --runtime runsc alpine true
Error
docker: Error response from daemon: failed to create task for container: failed to create shim task: OCI runtime create failed: creating container: cannot set up cgroup for root: configuring cgroup: stat /sys/fs/cgroup/blkio: no such file or directory: unknown.
Unit Test Failure
Add extraValid to TestOptional
{
name: "blkio",
ctrlr: &blockIO{},
extraValid: []*specs.LinuxResources{
{BlockIO: &specs.LinuxBlockIO{}}, // Docker 29
{BlockIO: &specs.LinuxBlockIO{Weight: uint16Ptr(0)}}, // Docker ≤ 24
},
invalid: []struct {
name string
spec *specs.LinuxResources
err string
}{
{
name: "weight",
spec: &specs.LinuxResources{BlockIO: &specs.LinuxBlockIO{Weight: uint16Ptr(1)}},
err: "blkio controller is missing but limits are set in OCI spec",
},
},
},
The test fails with the current skip().
The proposal fixes it.
Combined with Inspect Container Config, this shows that the current implementation does not skip BlockIO as expected.
runsc version
runsc version VERSION_MISSING
spec: 1.2.1
Built from source at release-20260817.0-43-g80336ad54
docker version (if using docker)
Client:
Version: 24.0.2
API version: 1.43
Go version: go1.20.4
Git commit: 610b8d0
Built: Thu Sep 25 10:08:01 2025
OS/Arch: linux/amd64
Context: default
Server:
Engine:
Version: 24.0.2
API version: 1.43 (minimum version 1.12)
Go version: go1.20.4
Git commit: bd0a34d
Built: Thu Sep 25 10:09:08 2025
OS/Arch: linux/amd64
Experimental: false
containerd:
Version: v1.7.1
GitCommit: fea6458b8abe502f6228eab2e5a6678fcb5c3fb0
runc:
Version: v1.1.7
GitCommit: 0320c58
docker-init:
Version: 0.19.0
GitCommit: ed96d00
uname
Linux 5.10.55+ SMP x86_64 GNU/Linux
kubectl (if using Kubernetes)
repo state (if built from source)
fatal: No names found, cannot describe anything.
runsc debug logs (if available)
Not applicable: the failure happens in runsc create before the sandbox starts
Description
Symptom
runsc fails on a kernel with
CONFIG_BLK_CGROUPdisabledRoot Cause
992a840889 intends to make cgroup blkio optional. But it skips iff the
BlockIOstruct is nil.Docker always populates that struct, even when no I/O limit was requested.
"blockIO":{"weight":0}"blockIO":{}Inspect Container Config
docker run --rm -v /run:/rc:ro alpine sh -c 'grep -o "\"blockIO\":{[^}]*}" $(find /rc -path "*/io.containerd.runtime.v2.task/moby/$(hostname)*/config.json")'Output
Proposal
In addition to nil check, blockIO.skip() checks whether all fields of LinuxBlockIO are default.
Steps to reproduce
Docker Runtime Failure
Requirements (if infeasible, try
Unit Test Failureinstead)CONFIG_BLK_CGROUPdisabled. i.e. build the kernel withCONFIG_BLK_CGROUP=nCommand
docker run --rm --runtime runsc alpine trueError
Unit Test Failure
Add
extraValidto TestOptionalThe test fails with the current skip().
The proposal fixes it.
Combined with
Inspect Container Config, this shows that the current implementation does not skipBlockIOas expected.runsc version
runsc version VERSION_MISSING spec: 1.2.1 Built from source at release-20260817.0-43-g80336ad54docker version (if using docker)
Client: Version: 24.0.2 API version: 1.43 Go version: go1.20.4 Git commit: 610b8d0 Built: Thu Sep 25 10:08:01 2025 OS/Arch: linux/amd64 Context: default Server: Engine: Version: 24.0.2 API version: 1.43 (minimum version 1.12) Go version: go1.20.4 Git commit: bd0a34d Built: Thu Sep 25 10:09:08 2025 OS/Arch: linux/amd64 Experimental: false containerd: Version: v1.7.1 GitCommit: fea6458b8abe502f6228eab2e5a6678fcb5c3fb0 runc: Version: v1.1.7 GitCommit: 0320c58 docker-init: Version: 0.19.0 GitCommit: ed96d00uname
Linux 5.10.55+ SMP x86_64 GNU/Linux
kubectl (if using Kubernetes)
repo state (if built from source)
fatal: No names found, cannot describe anything.
runsc debug logs (if available)
Not applicable: the failure happens in runsc create before the sandbox starts