Skip to content

Commit e04ded9

Browse files
committed
runc update: support per-device weight and iops
This support was missing from runc, and thus the example from the podman-update wasn't working. To fix, introduce a function to either update or insert new weights and iops. Add integration tests. Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com>
1 parent b04031d commit e04ded9

File tree

4 files changed

+156
-6
lines changed

4 files changed

+156
-6
lines changed

tests/integration/cgroups.bats

Lines changed: 29 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -174,17 +174,40 @@ function setup() {
174174
runc run -d --console-socket "$CONSOLE_SOCKET" test_dev_weight
175175
[ "$status" -eq 0 ]
176176

177-
# The loop device itself is no longer needed.
178-
losetup -d "$dev"
179-
180177
if [ -v CGROUP_V2 ]; then
181178
file="io.bfq.weight"
182179
else
183180
file="blkio.bfq.weight_device"
184181
fi
185-
weights=$(get_cgroup_value $file)
186-
[[ "$weights" == *"default 333"* ]]
187-
[[ "$weights" == *"$major:$minor 444"* ]]
182+
weights1=$(get_cgroup_value $file)
183+
184+
# Check that runc update works.
185+
runc update -r - test_dev_weight <<EOF
186+
{
187+
"blockIO": {
188+
"weight": 111,
189+
"weightDevice": [
190+
{
191+
"major": $major,
192+
"minor": $minor,
193+
"weight": 222
194+
}
195+
]
196+
}
197+
}
198+
EOF
199+
weights2=$(get_cgroup_value $file)
200+
201+
# The loop device itself is no longer needed.
202+
losetup -d "$dev"
203+
204+
# Check original values.
205+
grep '^default 333$' <<<"$weights1"
206+
grep "^$major:$minor 444$" <<<"$weights1"
207+
# Check updated values.
208+
grep '^default 111$' <<<"$weights2"
209+
grep "^$major:$minor 222$" <<<"$weights2"
210+
188211
}
189212

190213
@test "runc run (per-device multiple iops via unified)" {

tests/integration/helpers.bash

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -368,6 +368,22 @@ function check_cpu_weight() {
368368
check_systemd_value "CPUWeight" "$weight"
369369
}
370370

371+
function check_cgroup_dev_iops() {
372+
local dev=$1 rbps=$2 wbps=$3 riops=$4 wiops=$5
373+
374+
if [ -v CGROUP_V2 ]; then
375+
iops=$(get_cgroup_value "io.max")
376+
printf "== io.max ==\n%s\n" "$iops"
377+
grep "^$dev rbps=$rbps wbps=$wbps riops=$riops wiops=$wiops$" <<<"$iops"
378+
return
379+
fi
380+
381+
grep "^$dev ${rbps}$" <<<"$(get_cgroup_value blkio.throttle.read_bps_device)"
382+
grep "^$dev ${wbps}$" <<<"$(get_cgroup_value blkio.throttle.write_bps_device)"
383+
grep "^$dev ${riops}$" <<<"$(get_cgroup_value blkio.throttle.read_iops_device)"
384+
grep "^$dev ${wiops}$" <<<"$(get_cgroup_value blkio.throttle.write_iops_device)"
385+
}
386+
371387
# Helper function to set a resources limit
372388
function set_resources_limit() {
373389
update_config '.linux.resources.pids.limit |= 100'

tests/integration/update.bats

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -892,3 +892,57 @@ EOF
892892
runc update test_update --memory 1024
893893
wait_for_container 10 1 test_update stopped
894894
}
895+
896+
@test "update per-device iops/bps values" {
897+
[ $EUID -ne 0 ] && requires rootless_cgroup
898+
899+
# We need a major number of any disk device. Usually those are partitioned,
900+
# with the device itself having minor of 0, and partitions are 1, 2...
901+
major=$(awk '$2 == 0 {print $1; exit}' /proc/partitions)
902+
if [ "$major" = "0" ] || [ "$major" = "" ]; then
903+
echo "=== /proc/partitions ==="
904+
cat /proc/partitions
905+
echo "==="
906+
skip "can't get device major number from /proc/partitions (got $major)"
907+
fi
908+
909+
runc run -d --console-socket "$CONSOLE_SOCKET" test_update
910+
[ "$status" -eq 0 ]
911+
912+
runc update -r - test_update <<EOF
913+
{
914+
"blockIO": {
915+
"throttleReadBpsDevice": [
916+
{
917+
"major": $major,
918+
"minor": 0,
919+
"rate": 10485760
920+
}
921+
],
922+
"throttleWriteBpsDevice": [
923+
{
924+
"major": $major,
925+
"minor": 0,
926+
"rate": 9437184
927+
}
928+
],
929+
"throttleReadIOPSDevice": [
930+
{
931+
"major": $major,
932+
"minor": 0,
933+
"rate": 1000
934+
}
935+
],
936+
"throttleWriteIOPSDevice": [
937+
{
938+
"major": $major,
939+
"minor": 0,
940+
"rate": 900
941+
}
942+
]
943+
}
944+
}
945+
EOF
946+
[ "$status" -eq 0 ]
947+
check_cgroup_dev_iops "$major:0" 10485760 9437184 1000 900
948+
}

update.go

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -273,6 +273,25 @@ other options are ignored.
273273
if r.BlockIO.Weight != nil {
274274
config.Cgroups.Resources.BlkioWeight = *r.BlockIO.Weight
275275
}
276+
if r.BlockIO.LeafWeight != nil {
277+
config.Cgroups.Resources.BlkioLeafWeight = *r.BlockIO.LeafWeight
278+
}
279+
// For devices, we either update an existing one, or insert a new one.
280+
for _, wd := range r.BlockIO.WeightDevice {
281+
config.Cgroups.Resources.BlkioWeightDevice = upsertWeightDevice(config.Cgroups.Resources.BlkioWeightDevice, wd)
282+
}
283+
for _, td := range r.BlockIO.ThrottleReadBpsDevice {
284+
config.Cgroups.Resources.BlkioThrottleReadBpsDevice = upsertThrottleDevice(config.Cgroups.Resources.BlkioThrottleReadBpsDevice, td)
285+
}
286+
for _, td := range r.BlockIO.ThrottleWriteBpsDevice {
287+
config.Cgroups.Resources.BlkioThrottleWriteBpsDevice = upsertThrottleDevice(config.Cgroups.Resources.BlkioThrottleWriteBpsDevice, td)
288+
}
289+
for _, td := range r.BlockIO.ThrottleReadIOPSDevice {
290+
config.Cgroups.Resources.BlkioThrottleReadIOPSDevice = upsertThrottleDevice(config.Cgroups.Resources.BlkioThrottleReadIOPSDevice, td)
291+
}
292+
for _, td := range r.BlockIO.ThrottleWriteIOPSDevice {
293+
config.Cgroups.Resources.BlkioThrottleWriteIOPSDevice = upsertThrottleDevice(config.Cgroups.Resources.BlkioThrottleWriteIOPSDevice, td)
294+
}
276295

277296
// Setting CPU quota and period independently does not make much sense,
278297
// but historically runc allowed it and this needs to be supported
@@ -381,3 +400,41 @@ other options are ignored.
381400
return container.Set(config)
382401
},
383402
}
403+
404+
func upsertWeightDevice(devices []*cgroups.WeightDevice, wd specs.LinuxWeightDevice) []*cgroups.WeightDevice {
405+
for i, dev := range devices {
406+
if dev.Major != wd.Major || dev.Minor != wd.Minor {
407+
continue
408+
}
409+
// Update weights for existing device.
410+
if wd.Weight != nil {
411+
devices[i].Weight = *wd.Weight
412+
}
413+
if wd.LeafWeight != nil {
414+
devices[i].LeafWeight = *wd.LeafWeight
415+
}
416+
return devices
417+
}
418+
419+
// New device -- append it.
420+
var weight, leafWeight uint16
421+
if wd.Weight != nil {
422+
weight = *wd.Weight
423+
}
424+
if wd.LeafWeight != nil {
425+
leafWeight = *wd.LeafWeight
426+
}
427+
428+
return append(devices, cgroups.NewWeightDevice(wd.Major, wd.Minor, weight, leafWeight))
429+
}
430+
431+
func upsertThrottleDevice(devices []*cgroups.ThrottleDevice, td specs.LinuxThrottleDevice) []*cgroups.ThrottleDevice {
432+
for i, dev := range devices {
433+
if dev.Major == td.Major && dev.Minor == td.Minor {
434+
devices[i].Rate = td.Rate
435+
return devices
436+
}
437+
}
438+
439+
return append(devices, cgroups.NewThrottleDevice(td.Major, td.Minor, td.Rate))
440+
}

0 commit comments

Comments
 (0)