[lxc-devel] [lxd/master] lxd/cgroup: Initial abstraction for cgroupsv2 with pids support
shreyassood on Github
lxc-bot at linuxcontainers.org
Mon Dec 9 02:48:56 UTC 2019
A non-text attachment was scrubbed...
Name: not available
Type: text/x-mailbox
Size: 412 bytes
Desc: not available
URL: <http://lists.linuxcontainers.org/pipermail/lxc-devel/attachments/20191208/0e353845/attachment.bin>
-------------- next part --------------
From 8b76c03441f915acf68e8e16434e457ae82d41b3 Mon Sep 17 00:00:00 2001
From: Shreyas Sood <shreyassood at 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.
More information about the lxc-devel
mailing list