Files
moby/daemon/update_linux.go
Alexis Couvreur ca30e0d8ec daemon: support per-device blkio throttle in ContainerUpdate
The POST /containers/{id}/update API accepts BlkioWeightDevice,
BlkioDeviceReadBps, BlkioDeviceWriteBps, BlkioDeviceReadIOps, and
BlkioDeviceWriteIOps in its Resources body, but these five fields were
silently ignored when updating a running container.

The root cause was in toContainerdResources (daemon/update_linux.go):
only BlkioWeight was mapped into specs.LinuxBlockIO; the per-device
fields were never converted, so tsk.UpdateResources never wrote to
cgroupv2 io.max or the cgroupv1 blkio throttle files.

Fix by calling the existing getBlkioWeightDevices and
getBlkioThrottleDevices helpers (already used in oci_linux.go for
container creation) to populate all five fields. The function signature
is extended to return an error so that stat(2) failures on invalid
device paths are surfaced to the caller instead of being silently
dropped.

The API makes distinction between nil and zero-length slices while
doing. A nil per-device blkio field means the caller did not request an
update for that setting, while a non-nil empty slice means the caller
explicitly requested the setting to be cleared.

The Windows stub is updated to match the new signature.

Signed-off-by: Alexis Couvreur <alexiscouvreur.pro@gmail.com>
Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>
2026-06-12 15:52:14 +02:00

119 lines
2.7 KiB
Go

package daemon
import (
"time"
"github.com/moby/moby/api/types/container"
libcontainerdtypes "github.com/moby/moby/v2/daemon/internal/libcontainerd/types"
"github.com/opencontainers/runtime-spec/specs-go"
)
func toContainerdResources(resources container.Resources) (*libcontainerdtypes.Resources, error) {
var r libcontainerdtypes.Resources
// little helper to lazily initialize the BlockIO struct only if needed
blockIO := func() *specs.LinuxBlockIO {
if r.BlockIO == nil {
r.BlockIO = &specs.LinuxBlockIO{}
}
return r.BlockIO
}
weightDevices, err := getBlkioWeightDevices(resources)
if err != nil {
return nil, err
}
if resources.BlkioWeightDevice != nil {
blockIO().WeightDevice = weightDevices
}
readBpsDevices, err := getBlkioThrottleDevices(resources.BlkioDeviceReadBps)
if err != nil {
return nil, err
}
if resources.BlkioDeviceReadBps != nil {
blockIO().ThrottleReadBpsDevice = readBpsDevices
}
writeBpsDevices, err := getBlkioThrottleDevices(resources.BlkioDeviceWriteBps)
if err != nil {
return nil, err
}
if resources.BlkioDeviceWriteBps != nil {
blockIO().ThrottleWriteBpsDevice = writeBpsDevices
}
readIOpsDevices, err := getBlkioThrottleDevices(resources.BlkioDeviceReadIOps)
if err != nil {
return nil, err
}
if resources.BlkioDeviceReadIOps != nil {
blockIO().ThrottleReadIOPSDevice = readIOpsDevices
}
writeIOpsDevices, err := getBlkioThrottleDevices(resources.BlkioDeviceWriteIOps)
if err != nil {
return nil, err
}
if resources.BlkioDeviceWriteIOps != nil {
blockIO().ThrottleWriteIOPSDevice = writeIOpsDevices
}
if resources.BlkioWeight != 0 {
blockIO().Weight = &resources.BlkioWeight
}
cpu := specs.LinuxCPU{
Cpus: resources.CpusetCpus,
Mems: resources.CpusetMems,
}
if resources.CPUShares != 0 {
shares := uint64(resources.CPUShares)
cpu.Shares = &shares
}
var (
period uint64
quota int64
)
if resources.NanoCPUs != 0 {
period = uint64(100 * time.Millisecond / time.Microsecond)
quota = resources.NanoCPUs * int64(period) / 1e9
}
if quota == 0 && resources.CPUQuota != 0 {
quota = resources.CPUQuota
}
if period == 0 && resources.CPUPeriod != 0 {
period = uint64(resources.CPUPeriod)
}
if period != 0 {
cpu.Period = &period
}
if quota != 0 {
cpu.Quota = &quota
}
if cpu != (specs.LinuxCPU{}) {
r.CPU = &cpu
}
var memory specs.LinuxMemory
if resources.Memory != 0 {
memory.Limit = &resources.Memory
}
if resources.MemoryReservation != 0 {
memory.Reservation = &resources.MemoryReservation
}
if resources.MemorySwap > 0 {
memory.Swap = &resources.MemorySwap
}
if memory != (specs.LinuxMemory{}) {
r.Memory = &memory
}
r.Pids = getPidsLimit(resources)
return &r, nil
}