The following pull request was submitted through Github. It can be accessed and reviewed at: https://github.com/lxc/lxd/pull/6575
This e-mail was sent by the LXC bot, direct replies will not reach the author unless they happen to be subscribed to this list. === Description (from pull-request) === Initial abstraction to support different properties and controllers for https://github.com/lxc/lxd/issues/6305.
From 8b76c03441f915acf68e8e16434e457ae82d41b3 Mon Sep 17 00:00:00 2001 From: Shreyas Sood <shreyass...@gmail.com> Date: Sun, 8 Dec 2019 19:49:39 -0600 Subject: [PATCH] lxd/cgroup: Initial abstraction for cgroupsv2 with pids support --- lxd/cgroup/cgroup.go | 69 ++++++++++++++++++ lxd/container_lxc.go | 108 ++++++++++++++++++++--------- lxd/devices.go | 4 +- lxd/instance/instance_interface.go | 7 +- lxd/instance/qemu/vm_qemu.go | 17 ++++- 5 files changed, 165 insertions(+), 40 deletions(-) create mode 100644 lxd/cgroup/cgroup.go diff --git a/lxd/cgroup/cgroup.go b/lxd/cgroup/cgroup.go new file mode 100644 index 0000000000..cc3ecba6f4 --- /dev/null +++ b/lxd/cgroup/cgroup.go @@ -0,0 +1,69 @@ +package cgroup + +import ( + "fmt" + "gopkg.in/lxc/go-lxc.v2" +) + +type Property int +const ( + PidsCurrent Property = iota + PidsMax +) + +type configItem struct { + Key string + Value string +} + +// Get finds property values on a lxcContainer +func Get(c *lxc.Container, property Property) ([]string, error) { + switch property { + + // Properties which have the same functionality for both v1 and v2 + case PidsCurrent: + return c.CgroupItem("pids.current"), nil + } + + return nil, fmt.Errorf("CGroup Property not supported for Get") +} + +// Set sets a property on a lxcContainer +func Set(c *lxc.Container, property Property, value string) error { + + configs, e := SetConfigMap(property, value) + if e != nil { + return e + } + + for _, rule := range configs { + err := c.SetCgroupItem(rule.Key, rule.Value) + + if err != nil { + return fmt.Errorf("Failure while trying to set property: %s", err) + } + } + + return nil +} + +// SetConfigMap returns different cgroup configs to set a particular property +func SetConfigMap(property Property, value string) ([]configItem, error) { + + switch property { + + // Properties which have the same functionality for both v1 and v2 + case PidsCurrent: + return []configItem{ + {Key: "pids.current", Value: value}, + }, nil + + case PidsMax: + return []configItem{ + {Key: "pids.max", Value: value}, + }, nil + + } + + return nil, fmt.Errorf("CGroup Property not supported for Set") +} \ No newline at end of file diff --git a/lxd/container_lxc.go b/lxd/container_lxc.go index 4c6bb2999d..c9a39492c5 100644 --- a/lxd/container_lxc.go +++ b/lxd/container_lxc.go @@ -1405,7 +1405,7 @@ func (c *containerLXC) deviceAddCgroupRules(cgroups []deviceConfig.RunConfigItem } // Add the new device cgroup rule. - err := c.CGroupSet(rule.Key, rule.Value) + err := c.CGroupSetV1(rule.Key, rule.Value) if err != nil { return fmt.Errorf("Failed to add cgroup rule for device") } @@ -2592,7 +2592,7 @@ func (c *containerLXC) Stop(stateful bool) error { // Fork-bomb mitigation, prevent forking from this point on if c.state.OS.CGroupPidsController { // Attempt to disable forking new processes - c.CGroupSet("pids.max", "0") + c.CGroupSet(cgroup.PidsMax, "0") } else if c.state.OS.CGroupFreezerController { // Attempt to freeze the container freezer := make(chan bool, 1) @@ -3769,7 +3769,28 @@ func (c *containerLXC) Rename(newName string) error { return nil } -func (c *containerLXC) CGroupGet(key string) (string, error) { +// CGroupGet gets the value of a supported property on a hybrid v1/v2 controller system +func (c *containerLXC) CGroupGet(property cgroup.Property) (string, error) { + // Load the go-lxc struct + err := c.initLXC(false) + if err != nil { + return "", err + } + + // Make sure the container is running + if !c.IsRunning() { + return "", fmt.Errorf("Can't get cgroups on a stopped container") + } + + value, err := cgroup.Get(c.c, property) + + if err != nil { + return "", err + } + return strings.Join(value, "\n"), nil +} + +func (c *containerLXC) CGroupGetV1(key string) (string, error) { // Load the go-lxc struct err := c.initLXC(false) if err != nil { @@ -3785,7 +3806,28 @@ func (c *containerLXC) CGroupGet(key string) (string, error) { return strings.Join(value, "\n"), nil } -func (c *containerLXC) CGroupSet(key string, value string) error { +// CGroupSet sets a supported property on a hybrid v1/v2 controller system +func (c *containerLXC) CGroupSet(property cgroup.Property, value string) error { + // Load the go-lxc struct + err := c.initLXC(false) + if err != nil { + return err + } + + // Make sure the container is running + if !c.IsRunning() { + return fmt.Errorf("Can't set cgroups on a stopped container") + } + + err = cgroup.Set(c.c, property, value) + if err != nil { + return fmt.Errorf("Failed to set cgroup item %d=\"%s\": %s", property, value, err) + } + + return nil +} + +func (c *containerLXC) CGroupSetV1(key string, value string) error { // Load the go-lxc struct err := c.initLXC(false) if err != nil { @@ -4208,7 +4250,7 @@ func (c *containerLXC) Update(args db.InstanceArgs, userRequested bool) error { priority = 10 } - err = c.CGroupSet("blkio.weight", fmt.Sprintf("%d", priority)) + err = c.CGroupSetV1("blkio.weight", fmt.Sprintf("%d", priority)) if err != nil { return err } @@ -4249,52 +4291,52 @@ func (c *containerLXC) Update(args db.InstanceArgs, userRequested bool) error { // Store the old values for revert oldMemswLimit := "" if c.state.OS.CGroupSwapAccounting { - oldMemswLimit, err = c.CGroupGet("memory.memsw.limit_in_bytes") + oldMemswLimit, err = c.CGroupGetV1("memory.memsw.limit_in_bytes") if err != nil { oldMemswLimit = "" } } - oldLimit, err := c.CGroupGet("memory.limit_in_bytes") + oldLimit, err := c.CGroupGetV1("memory.limit_in_bytes") if err != nil { oldLimit = "" } - oldSoftLimit, err := c.CGroupGet("memory.soft_limit_in_bytes") + oldSoftLimit, err := c.CGroupGetV1("memory.soft_limit_in_bytes") if err != nil { oldSoftLimit = "" } revertMemory := func() { if oldSoftLimit != "" { - c.CGroupSet("memory.soft_limit_in_bytes", oldSoftLimit) + c.CGroupSetV1("memory.soft_limit_in_bytes", oldSoftLimit) } if oldLimit != "" { - c.CGroupSet("memory.limit_in_bytes", oldLimit) + c.CGroupSetV1("memory.limit_in_bytes", oldLimit) } if oldMemswLimit != "" { - c.CGroupSet("memory.memsw.limit_in_bytes", oldMemswLimit) + c.CGroupSetV1("memory.memsw.limit_in_bytes", oldMemswLimit) } } // Reset everything if c.state.OS.CGroupSwapAccounting { - err = c.CGroupSet("memory.memsw.limit_in_bytes", "-1") + err = c.CGroupSetV1("memory.memsw.limit_in_bytes", "-1") if err != nil { revertMemory() return err } } - err = c.CGroupSet("memory.limit_in_bytes", "-1") + err = c.CGroupSetV1("memory.limit_in_bytes", "-1") if err != nil { revertMemory() return err } - err = c.CGroupSet("memory.soft_limit_in_bytes", "-1") + err = c.CGroupSetV1("memory.soft_limit_in_bytes", "-1") if err != nil { revertMemory() return err @@ -4303,26 +4345,26 @@ func (c *containerLXC) Update(args db.InstanceArgs, userRequested bool) error { // Set the new values if memoryEnforce == "soft" { // Set new limit - err = c.CGroupSet("memory.soft_limit_in_bytes", memory) + err = c.CGroupSetV1("memory.soft_limit_in_bytes", memory) if err != nil { revertMemory() return err } } else { if c.state.OS.CGroupSwapAccounting && (memorySwap == "" || shared.IsTrue(memorySwap)) { - err = c.CGroupSet("memory.limit_in_bytes", memory) + err = c.CGroupSetV1("memory.limit_in_bytes", memory) if err != nil { revertMemory() return err } - err = c.CGroupSet("memory.memsw.limit_in_bytes", memory) + err = c.CGroupSetV1("memory.memsw.limit_in_bytes", memory) if err != nil { revertMemory() return err } } else { - err = c.CGroupSet("memory.limit_in_bytes", memory) + err = c.CGroupSetV1("memory.limit_in_bytes", memory) if err != nil { revertMemory() return err @@ -4336,7 +4378,7 @@ func (c *containerLXC) Update(args db.InstanceArgs, userRequested bool) error { return err } - err = c.CGroupSet("memory.soft_limit_in_bytes", fmt.Sprintf("%.0f", float64(valueInt)*0.9)) + err = c.CGroupSetV1("memory.soft_limit_in_bytes", fmt.Sprintf("%.0f", float64(valueInt)*0.9)) if err != nil { revertMemory() return err @@ -4348,7 +4390,7 @@ func (c *containerLXC) Update(args db.InstanceArgs, userRequested bool) error { memorySwap := c.expandedConfig["limits.memory.swap"] memorySwapPriority := c.expandedConfig["limits.memory.swap.priority"] if memorySwap != "" && !shared.IsTrue(memorySwap) { - err = c.CGroupSet("memory.swappiness", "0") + err = c.CGroupSetV1("memory.swappiness", "0") if err != nil { return err } @@ -4361,7 +4403,7 @@ func (c *containerLXC) Update(args db.InstanceArgs, userRequested bool) error { } } - err = c.CGroupSet("memory.swappiness", fmt.Sprintf("%d", 60-10+priority)) + err = c.CGroupSetV1("memory.swappiness", fmt.Sprintf("%d", 60-10+priority)) if err != nil { return err } @@ -4387,17 +4429,17 @@ func (c *containerLXC) Update(args db.InstanceArgs, userRequested bool) error { return err } - err = c.CGroupSet("cpu.shares", cpuShares) + err = c.CGroupSetV1("cpu.shares", cpuShares) if err != nil { return err } - err = c.CGroupSet("cpu.cfs_period_us", cpuCfsPeriod) + err = c.CGroupSetV1("cpu.cfs_period_us", cpuCfsPeriod) if err != nil { return err } - err = c.CGroupSet("cpu.cfs_quota_us", cpuCfsQuota) + err = c.CGroupSetV1("cpu.cfs_quota_us", cpuCfsQuota) if err != nil { return err } @@ -4407,7 +4449,7 @@ func (c *containerLXC) Update(args db.InstanceArgs, userRequested bool) error { } if value == "" { - err = c.CGroupSet("pids.max", "max") + err = c.CGroupSet(cgroup.PidsCurrent, "max") if err != nil { return err } @@ -4417,7 +4459,7 @@ func (c *containerLXC) Update(args db.InstanceArgs, userRequested bool) error { return err } - err = c.CGroupSet("pids.max", fmt.Sprintf("%d", valueInt)) + err = c.CGroupSet(cgroup.PidsMax, fmt.Sprintf("%d", valueInt)) if err != nil { return err } @@ -5732,7 +5774,7 @@ func (c *containerLXC) cpuState() api.InstanceStateCPU { } // CPU usage in seconds - value, err := c.CGroupGet("cpuacct.usage") + value, err := c.CGroupGetV1("cpuacct.usage") if err != nil { cpu.Usage = -1 return cpu @@ -5803,14 +5845,14 @@ func (c *containerLXC) memoryState() api.InstanceStateMemory { } // Memory in bytes - value, err := c.CGroupGet("memory.usage_in_bytes") + value, err := c.CGroupGetV1("memory.usage_in_bytes") valueInt, err1 := strconv.ParseInt(value, 10, 64) if err == nil && err1 == nil { memory.Usage = valueInt } // Memory peak in bytes - value, err = c.CGroupGet("memory.max_usage_in_bytes") + value, err = c.CGroupGetV1("memory.max_usage_in_bytes") valueInt, err1 = strconv.ParseInt(value, 10, 64) if err == nil && err1 == nil { memory.UsagePeak = valueInt @@ -5819,7 +5861,7 @@ func (c *containerLXC) memoryState() api.InstanceStateMemory { if c.state.OS.CGroupSwapAccounting { // Swap in bytes if memory.Usage > 0 { - value, err := c.CGroupGet("memory.memsw.usage_in_bytes") + value, err := c.CGroupGetV1("memory.memsw.usage_in_bytes") valueInt, err1 := strconv.ParseInt(value, 10, 64) if err == nil && err1 == nil { memory.SwapUsage = valueInt - memory.Usage @@ -5828,7 +5870,7 @@ func (c *containerLXC) memoryState() api.InstanceStateMemory { // Swap peak in bytes if memory.UsagePeak > 0 { - value, err = c.CGroupGet("memory.memsw.max_usage_in_bytes") + value, err = c.CGroupGetV1("memory.memsw.max_usage_in_bytes") valueInt, err1 = strconv.ParseInt(value, 10, 64) if err == nil && err1 == nil { memory.SwapUsagePeak = valueInt - memory.UsagePeak @@ -5905,7 +5947,7 @@ func (c *containerLXC) processesState() int64 { } if c.state.OS.CGroupPidsController { - value, err := c.CGroupGet("pids.current") + value, err := c.CGroupGet(cgroup.PidsCurrent) if err != nil { return -1 } @@ -6527,7 +6569,7 @@ func (c *containerLXC) setNetworkPriority() error { success := false var last_error error for _, netif := range netifs { - err = c.CGroupSet("net_prio.ifpriomap", fmt.Sprintf("%s %d", netif.Name, networkInt)) + err = c.CGroupSetV1("net_prio.ifpriomap", fmt.Sprintf("%s %d", netif.Name, networkInt)) if err == nil { success = true } else { diff --git a/lxd/devices.go b/lxd/devices.go index fb8a84f733..28d4d3a34d 100644 --- a/lxd/devices.go +++ b/lxd/devices.go @@ -400,7 +400,7 @@ func deviceTaskBalance(s *state.State) { } sort.Strings(set) - err := ctn.CGroupSet("cpuset.cpus", strings.Join(set, ",")) + err := ctn.CGroupSetV1("cpuset.cpus", strings.Join(set, ",")) if err != nil { logger.Error("balance: Unable to set cpuset", log.Ctx{"name": ctn.Name(), "err": err, "value": strings.Join(set, ",")}) } @@ -431,7 +431,7 @@ func deviceNetworkPriority(s *state.State, netif string) { } // Set the value for the new interface - c.CGroupSet("net_prio.ifpriomap", fmt.Sprintf("%s %d", netif, networkInt)) + c.CGroupSetV1("net_prio.ifpriomap", fmt.Sprintf("%s %d", netif, networkInt)) } return diff --git a/lxd/instance/instance_interface.go b/lxd/instance/instance_interface.go index 9c56b0ddfe..22facc7540 100644 --- a/lxd/instance/instance_interface.go +++ b/lxd/instance/instance_interface.go @@ -6,6 +6,7 @@ import ( "time" "github.com/lxc/lxd/lxd/backup" + "github.com/lxc/lxd/lxd/cgroup" "github.com/lxc/lxd/lxd/db" deviceConfig "github.com/lxc/lxd/lxd/device/config" "github.com/lxc/lxd/lxd/instance/instancetype" @@ -40,8 +41,10 @@ type Instance interface { Export(w io.Writer, properties map[string]string) error // Live configuration - CGroupGet(key string) (string, error) - CGroupSet(key string, value string) error + CGroupGet(property cgroup.Property) (string, error) + CGroupGetV1(key string) (string, error) + CGroupSet(property cgroup.Property, value string) error + CGroupSetV1(key string, value string) error VolatileSet(changes map[string]string) error // File handling diff --git a/lxd/instance/qemu/vm_qemu.go b/lxd/instance/qemu/vm_qemu.go index 5f149794b2..3d456201f2 100644 --- a/lxd/instance/qemu/vm_qemu.go +++ b/lxd/instance/qemu/vm_qemu.go @@ -23,6 +23,7 @@ import ( lxdClient "github.com/lxc/lxd/client" "github.com/lxc/lxd/lxd/backup" + "github.com/lxc/lxd/lxd/cgroup" "github.com/lxc/lxd/lxd/cluster" "github.com/lxc/lxd/lxd/db" "github.com/lxc/lxd/lxd/db/query" @@ -2208,16 +2209,26 @@ func (vm *Qemu) Export(w io.Writer, properties map[string]string) error { return fmt.Errorf("Export Not implemented") } -// CGroupGet is not implemented for VMs. -func (vm *Qemu) CGroupGet(key string) (string, error) { +// CGroupGetV1 is not implemented for VMs. +func (vm *Qemu) CGroupGet(property cgroup.Property) (string, error) { return "", fmt.Errorf("CGroupGet Not implemented") } +// CGroupGetV1 is not implemented for VMs. +func (vm *Qemu) CGroupGetV1(key string) (string, error) { + return "", fmt.Errorf("CGroupGetV1 Not implemented") +} + // CGroupSet is not implemented for VMs. -func (vm *Qemu) CGroupSet(key string, value string) error { +func (vm *Qemu) CGroupSet(property cgroup.Property, value string) error { return fmt.Errorf("CGroupSet Not implemented") } +// CGroupSetV1 is not implemented for VMs. +func (vm *Qemu) CGroupSetV1(key string, value string) error { + return fmt.Errorf("CGroupSetV1 Not implemented") +} + // VolatileSet sets one or more volatile config keys. func (vm *Qemu) VolatileSet(changes map[string]string) error { // Sanity check.
_______________________________________________ lxc-devel mailing list lxc-devel@lists.linuxcontainers.org http://lists.linuxcontainers.org/listinfo/lxc-devel