From aa60df2ee8035f315bfb5d8dafce3144c7d05552 Mon Sep 17 00:00:00 2001 From: Yu Zou Date: Sat, 27 Aug 2016 17:30:37 +0800 Subject: [PATCH] add more parameters of blkio subsystem to command line. Signed-off-by: Yu Zou --- contrib/completions/bash/runc | 6 ++ libcontainer/cgroups/fs/blkio_test.go | 39 +++++++ man/runc-update.8.md | 38 ++++--- tests/integration/update.bats | 9 +- update.go | 149 +++++++++++++++++++++++++- 5 files changed, 223 insertions(+), 18 deletions(-) diff --git a/contrib/completions/bash/runc b/contrib/completions/bash/runc index f19adf1f840..71c33ab8da6 100644 --- a/contrib/completions/bash/runc +++ b/contrib/completions/bash/runc @@ -701,6 +701,12 @@ _runc_update() { local options_with_args=" --blkio-weight + --blkio-leaf-weight + --blkio-weight-device + --blkio-throttle-readbps-device + --blkio-throttle-writebps-device + --blkio-throttle-readiops-device + --blkio-throttle-writeiops-device --cpu-period --cpu-quota --cpu-share diff --git a/libcontainer/cgroups/fs/blkio_test.go b/libcontainer/cgroups/fs/blkio_test.go index 6957392048d..0bd701967c2 100644 --- a/libcontainer/cgroups/fs/blkio_test.go +++ b/libcontainer/cgroups/fs/blkio_test.go @@ -173,6 +173,45 @@ func TestBlkioSetMultipleWeightDevice(t *testing.T) { } } +func TestBlkioSetThrottleDevice(t *testing.T) { + helper := NewCgroupTestUtil("blkio", t) + defer helper.cleanup() + + const ( + throttleDeviceBefore = "8:0 400" + ) + deviceSet := map[string]string{"blkio.throttle.read_bps_device": throttleDeviceBefore, + "blkio.throttle.write_bps_device": throttleDeviceBefore, + "blkio.throttle.read_iops_device": throttleDeviceBefore, + "blkio.throttle.write_iops_device": throttleDeviceBefore, + } + + td := configs.NewThrottleDevice(8, 0, 500) + throttleDeviceAfter := td.String() + + helper.writeFileContents(deviceSet) + + helper.CgroupData.config.Resources.BlkioThrottleReadBpsDevice = []*configs.ThrottleDevice{td} + helper.CgroupData.config.Resources.BlkioThrottleWriteBpsDevice = []*configs.ThrottleDevice{td} + helper.CgroupData.config.Resources.BlkioThrottleReadIOPSDevice = []*configs.ThrottleDevice{td} + helper.CgroupData.config.Resources.BlkioThrottleWriteIOPSDevice = []*configs.ThrottleDevice{td} + blkio := &BlkioGroup{} + if err := blkio.Set(helper.CgroupPath, helper.CgroupData.config); err != nil { + t.Fatal(err) + } + + for d := range deviceSet { + value, err := getCgroupParamString(helper.CgroupPath, d) + if err != nil { + t.Fatalf("Failed to parse %s - %s", d, err) + } + + if value != throttleDeviceAfter { + t.Fatalf("Got the wrong value, set %s failed.", d) + } + } +} + func TestBlkioStats(t *testing.T) { helper := NewCgroupTestUtil("blkio", t) defer helper.cleanup() diff --git a/man/runc-update.8.md b/man/runc-update.8.md index 5f74026d70f..91fdec10160 100644 --- a/man/runc-update.8.md +++ b/man/runc-update.8.md @@ -24,7 +24,13 @@ accepted format is as follow (unchanged values can be omitted): "mems": "" }, "blockIO": { - "blkioWeight": 0 + "blkioWeight": 0, + "blkioLeafWeight": 0, + "blkioWeightDevice": "", + "blkioThrottleReadBpsDevice": "", + "blkioThrottleWriteBpsDevice": "", + "blkioThrottleReadIOPSDevice": "", + "blkioThrottleWriteIOPSDevice": "" }, } @@ -32,15 +38,21 @@ Note: if data is to be read from a file or the standard input, all other options are ignored. # OPTIONS - --resources value, -r value path to the file containing the resources to update or '-' to read from the standard input - --blkio-weight value Specifies per cgroup weight, range is from 10 to 1000 (default: 0) - --cpu-period value CPU period to be used for hardcapping (in usecs). 0 to use system default - --cpu-quota value CPU hardcap limit (in usecs). Allowed cpu time in a given period - --cpu-share value CPU shares (relative weight vs. other containers) - --cpuset-cpus value CPU(s) to use - --cpuset-mems value Memory node(s) to use - --kernel-memory value Kernel memory limit (in bytes) - --kernel-memory-tcp value Kernel memory limit (in bytes) for tcp buffer - --memory value Memory limit (in bytes) - --memory-reservation value Memory reservation or soft_limit (in bytes) - --memory-swap value Total memory usage (memory + swap); set '-1' to enable unlimited swap + --resources value, -r value path to the file containing the resources to update or '-' to read from the standard input + --blkio-weight value Specifies per cgroup weight + --blkio-leaf-weight value Specifies tasks' weight in the given cgroup while competing with the cgroup's child cgroups, cfq scheduler only + --blkio-weight-device value Weight per cgroup per device, can override blkio-weight. Argument must be of the form ": [LEAF_WEIGHT]" + --blkio-throttle-readbps-device value IO read rate limit per cgroup per device, bytes per second. Argument must be of the form ": " + --blkio-throttle-writebps-device value IO write rate limit per cgroup per divice, bytes per second. Argument must be of the form ": " + --blkio-throttle-readiops-device value IO read rate limit per cgroup per device, IO per second. Argument must be of the form ": " + --blkio-throttle-writeiops-device value IO write rate limit per cgroup per device, IO per second. Argument must be of the form ": " + --cpu-period value CPU period to be used for hardcapping (in usecs). 0 to use system default + --cpu-quota value CPU hardcap limit (in usecs). Allowed cpu time in a given period + --cpu-share value CPU shares (relative weight vs. other containers) + --cpuset-cpus value CPU(s) to use + --cpuset-mems value Memory node(s) to use + --kernel-memory value Kernel memory limit (in bytes) + --kernel-memory-tcp value Kernel memory limit (in bytes) for tcp buffer + --memory value Memory limit (in bytes) + --memory-reservation value Memory reservation or soft_limit (in bytes) + --memory-swap value Total memory usage (memory + swap); set '-1' to enable unlimited swap diff --git a/tests/integration/update.bats b/tests/integration/update.bats index cba56cb8a3e..1c0e64aae4e 100644 --- a/tests/integration/update.bats +++ b/tests/integration/update.bats @@ -30,7 +30,8 @@ function setup() { "cpus": "0" }, "blockio": { - "blkioWeight": 1000 + "blkioWeight": 1000, + "blkioLeafWeight": 1000 }, EOF ) @@ -62,6 +63,7 @@ function check_cgroup_value() { # check that initial values were properly set check_cgroup_value $CGROUP_BLKIO "blkio.weight" 1000 + check_cgroup_value $CGROUP_BLKIO "blkio.leaf_weight" 1000 check_cgroup_value $CGROUP_CPU "cpu.cfs_period_us" 1000000 check_cgroup_value $CGROUP_CPU "cpu.cfs_quota_us" 500000 check_cgroup_value $CGROUP_CPU "cpu.shares" 100 @@ -76,6 +78,10 @@ function check_cgroup_value() { [ "$status" -eq 0 ] check_cgroup_value $CGROUP_BLKIO "blkio.weight" 500 + # update blkio-leaf-weight + runc update test_update --blkio-leaf-weight 500 + check_cgroup_value $CGROUP_BLKIO "blkio.leaf_weight" 500 + # update cpu-period runc update test_update --cpu-period 900000 [ "$status" -eq 0 ] @@ -102,6 +108,7 @@ function check_cgroup_value() { # update memory limit runc update test_update --memory 67108864 [ "$status" -eq 0 ] + check_cgroup_value $CGROUP_MEMORY "memory.limit_in_bytes" 67108864 runc update test_update --memory 50M diff --git a/update.go b/update.go index 058211f0e2a..b9e77658bb0 100644 --- a/update.go +++ b/update.go @@ -6,16 +6,21 @@ import ( "encoding/json" "fmt" "os" + "regexp" "strconv" "github.com/docker/go-units" - "github.com/opencontainers/runtime-spec/specs-go" + "github.com/opencontainers/runc/libcontainer/configs" + specs "github.com/opencontainers/runtime-spec/specs-go" "github.com/urfave/cli" ) func u64Ptr(i uint64) *uint64 { return &i } func u16Ptr(i uint16) *uint16 { return &i } +var regBlkioWeightDevice = regexp.MustCompile(`([0-9]+):([0-9]+) ([0-9]+)(?: ([0-9]+))?`) +var regBlkioThrottleDevice = regexp.MustCompile(`([0-9]+):([0-9]+) ([0-9]+)`) + var updateCommand = cli.Command{ Name: "update", Usage: "update container resource constraints", @@ -44,7 +49,13 @@ The accepted format is as follow (unchanged values can be omitted): "mems": "" }, "blockIO": { - "blkioWeight": 0 + "blkioWeight": 0, + "blkioLeafWeight": 0, + "blkioWeightDevice": "", + "blkioThrottleReadBpsDevice": "", + "blkioThrottleWriteBpsDevice": "", + "blkioThrottleReadIOPSDevice": "", + "blkioThrottleWriteIOPSDevice": "" }, } @@ -55,7 +66,31 @@ other options are ignored. cli.IntFlag{ Name: "blkio-weight", - Usage: "Specifies per cgroup weight, range is from 10 to 1000", + Usage: "Specifies per cgroup weight", + }, + cli.IntFlag{ + Name: "blkio-leaf-weight", + Usage: "Specifies tasks' weight in the given cgroup while competing with the cgroup's child cgroups, cfq scheduler only", + }, + cli.StringSliceFlag{ + Name: "blkio-weight-device", + Usage: "Weight per cgroup per device, can override blkio-weight. Argument must be of the form \": [LEAF_WEIGHT]\"", + }, + cli.StringSliceFlag{ + Name: "blkio-throttle-readbps-device", + Usage: "IO read rate limit per cgroup per device, bytes per second. Argument must be of the form \": \"", + }, + cli.StringSliceFlag{ + Name: "blkio-throttle-writebps-device", + Usage: "IO write rate limit per cgroup per divice, bytes per second. Argument must be of the form \": \"", + }, + cli.StringSliceFlag{ + Name: "blkio-throttle-readiops-device", + Usage: "IO read rate limit per cgroup per device, IO per second. Argument must be of the form \": \"", + }, + cli.StringSliceFlag{ + Name: "blkio-throttle-writeiops-device", + Usage: "IO write rate limit per cgroup per device, IO per second. Argument must be of the form \": \"", }, cli.StringFlag{ Name: "cpu-period", @@ -120,7 +155,8 @@ other options are ignored. Mems: sPtr(""), }, BlockIO: &specs.BlockIO{ - Weight: u16Ptr(0), + Weight: u16Ptr(0), + LeafWeight: u16Ptr(0), }, } @@ -148,6 +184,26 @@ other options are ignored. if val := context.Int("blkio-weight"); val != 0 { r.BlockIO.Weight = u16Ptr(uint16(val)) } + if val := context.Int("blkio-leaf-weight"); val != 0 { + r.BlockIO.LeafWeight = u16Ptr(uint16(val)) + } + if val := context.StringSlice("blkio-weight-device"); val != nil { + if r.BlockIO.WeightDevice, err = getBlkioWeightDeviceFromString(val); err != nil { + return fmt.Errorf("invalid value for blkio-weight-device: %v", err) + } + } + for opt, dest := range map[string]*[]specs.ThrottleDevice{ + "blkio-throttle-readbps-device": &r.BlockIO.ThrottleReadBpsDevice, + "blkio-throttle-writebps-device": &r.BlockIO.ThrottleWriteBpsDevice, + "blkio-throttle-readiops-device": &r.BlockIO.ThrottleReadIOPSDevice, + "blkio-throttle-writeiops-device": &r.BlockIO.ThrottleWriteIOPSDevice, + } { + if val := context.StringSlice(opt); val != nil { + if *dest, err = getBlkioThrottleDeviceFromString(val); err != nil { + return fmt.Errorf("invalid value for %s: %v", opt, err) + } + } + } if val := context.String("cpuset-cpus"); val != "" { r.CPU.Cpus = &val } @@ -188,6 +244,12 @@ other options are ignored. // Update the value config.Cgroups.Resources.BlkioWeight = *r.BlockIO.Weight + config.Cgroups.Resources.BlkioLeafWeight = *r.BlockIO.LeafWeight + config.Cgroups.Resources.BlkioWeightDevice = convertSpecWeightDevices(r.BlockIO.WeightDevice) + config.Cgroups.Resources.BlkioThrottleReadBpsDevice = convertSpecThrottleDevices(r.BlockIO.ThrottleReadBpsDevice) + config.Cgroups.Resources.BlkioThrottleWriteBpsDevice = convertSpecThrottleDevices(r.BlockIO.ThrottleWriteBpsDevice) + config.Cgroups.Resources.BlkioThrottleReadIOPSDevice = convertSpecThrottleDevices(r.BlockIO.ThrottleReadIOPSDevice) + config.Cgroups.Resources.BlkioThrottleWriteIOPSDevice = convertSpecThrottleDevices(r.BlockIO.ThrottleWriteIOPSDevice) config.Cgroups.Resources.CpuPeriod = int64(*r.CPU.Period) config.Cgroups.Resources.CpuQuota = int64(*r.CPU.Quota) config.Cgroups.Resources.CpuShares = int64(*r.CPU.Shares) @@ -205,3 +267,82 @@ other options are ignored. return nil }, } + +func getBlkioWeightDeviceFromString(deviceStr []string) ([]specs.WeightDevice, error) { + var devs []specs.WeightDevice + for _, s := range deviceStr { + elems := regBlkioWeightDevice.FindStringSubmatch(s) + if elems == nil || len(elems) < 5 { + return nil, fmt.Errorf("invalid value for %s", s) + } + var dev specs.WeightDevice + var err error + var weight uint64 + if dev.Major, err = strconv.ParseInt(elems[1], 10, 64); err != nil { + return nil, fmt.Errorf("invalid value for %s, err: %v", elems[1], err) + } + if dev.Minor, err = strconv.ParseInt(elems[2], 10, 64); err != nil { + return nil, fmt.Errorf("invalid value for %s, err: %v", elems[2], err) + } + if weight, err = strconv.ParseUint(elems[3], 10, 16); err != nil { + return nil, fmt.Errorf("invalid value for %s, err: %v", elems[3], err) + } + dev.Weight = u16Ptr(uint16(weight)) + if elems[4] != "" { + if weight, err = strconv.ParseUint(elems[4], 10, 16); err != nil { + return nil, fmt.Errorf("invalid value for %s, err: %v", elems[4], err) + } + dev.LeafWeight = u16Ptr(uint16(weight)) + } + devs = append(devs, dev) + } + + return devs, nil +} + +func getBlkioThrottleDeviceFromString(deviceStr []string) ([]specs.ThrottleDevice, error) { + var devs []specs.ThrottleDevice + for _, s := range deviceStr { + elems := regBlkioThrottleDevice.FindStringSubmatch(s) + if elems == nil || len(elems) < 4 { + return nil, fmt.Errorf("invalid value for %s", s) + } + var dev specs.ThrottleDevice + var err error + var rate uint64 + if dev.Major, err = strconv.ParseInt(elems[1], 10, 64); err != nil { + return nil, fmt.Errorf("invalid value for %s, err: %v", elems[1], err) + } + if dev.Minor, err = strconv.ParseInt(elems[2], 10, 64); err != nil { + return nil, fmt.Errorf("invalid value for %s, err: %v", elems[2], err) + } + if rate, err = strconv.ParseUint(elems[3], 10, 64); err != nil { + return nil, fmt.Errorf("invalid value for %s, err: %v", elems[3], err) + } + dev.Rate = u64Ptr(rate) + devs = append(devs, dev) + } + return devs, nil +} + +func convertSpecWeightDevices(devices []specs.WeightDevice) []*configs.WeightDevice { + var cwds []*configs.WeightDevice + for _, d := range devices { + leafWeight := uint16(0) + if d.LeafWeight != nil { + leafWeight = *d.LeafWeight + } + wd := configs.NewWeightDevice(d.Major, d.Minor, *d.Weight, leafWeight) + cwds = append(cwds, wd) + } + return cwds +} + +func convertSpecThrottleDevices(devices []specs.ThrottleDevice) []*configs.ThrottleDevice { + var ctds []*configs.ThrottleDevice + for _, d := range devices { + td := configs.NewThrottleDevice(d.Major, d.Minor, *d.Rate) + ctds = append(ctds, td) + } + return ctds +}