diff --git a/internal/server/cgroup/abstraction.go b/internal/server/cgroup/abstraction.go index 8573ca6f08..6ffcc9e664 100644 --- a/internal/server/cgroup/abstraction.go +++ b/internal/server/cgroup/abstraction.go @@ -153,7 +153,7 @@ func (cg *CGroup) GetProcessesUsage() (int64, error) { // SetMemorySwapLimit sets the hard limit for swap. func (cg *CGroup) SetMemorySwapLimit(limit int64) error { - if !cgControllers["memory"] { + if !cgControllers["memory.swap"] { return ErrControllerMissing } @@ -314,7 +314,7 @@ func (cg *CGroup) SetMemorySwappiness(limit int64) error { // GetMemorySwapLimit returns the hard limit on swap usage. func (cg *CGroup) GetMemorySwapLimit() (int64, error) { - if !cgControllers["memory"] { + if !cgControllers["memory.swap"] { return -1, ErrControllerMissing } @@ -337,7 +337,7 @@ func (cg *CGroup) GetMemorySwapLimit() (int64, error) { // GetMemorySwapUsage return current usage of swap. func (cg *CGroup) GetMemorySwapUsage() (int64, error) { - if !cgControllers["memory"] { + if !cgControllers["memory.swap"] { return -1, ErrControllerMissing } diff --git a/internal/server/cgroup/init.go b/internal/server/cgroup/init.go index 718ef17960..1d410c0e99 100644 --- a/internal/server/cgroup/init.go +++ b/internal/server/cgroup/init.go @@ -11,6 +11,7 @@ import ( "github.com/lxc/incus/v7/internal/server/db/cluster" "github.com/lxc/incus/v7/internal/server/db/warningtype" "github.com/lxc/incus/v7/shared/logger" + "github.com/lxc/incus/v7/shared/util" ) var cgControllers = map[string]bool{} @@ -37,6 +38,9 @@ const ( // Memory resource control. Memory + // MemorySwap resource control. + MemorySwap + // Pids resource control. Pids ) @@ -54,6 +58,8 @@ func Supports(resource Resource) bool { return cgControllers["io"] case Memory: return cgControllers["memory"] + case MemorySwap: + return cgControllers["memory.swap"] case Pids: return cgControllers["pids"] } @@ -98,6 +104,11 @@ func Warnings() []cluster.Warning { TypeCode: warningtype.MissingCGroupMemoryController, LastMessage: "memory limits will be ignored", }) + } else if !Supports(MemorySwap) { + warnings = append(warnings, cluster.Warning{ + TypeCode: warningtype.MissingCGroupMemorySwapAccounting, + LastMessage: "swap limits will be ignored", + }) } if !Supports(Pids) { @@ -156,5 +167,15 @@ func Init() { } _ = controllers.Close() + + // Check for swap accounting support (can be disabled through kernel arguments). + if cgControllers["memory"] { + for _, path := range []string{filepath.Join(cgPath, fields[2]), filepath.Join(cgPath, "init.scope")} { + if util.PathExists(filepath.Join(path, "memory.swap.current")) { + cgControllers["memory.swap"] = true + break + } + } + } } } diff --git a/internal/server/instance/drivers/driver_lxc.go b/internal/server/instance/drivers/driver_lxc.go index 0fe73e2b26..ab32bd67c3 100644 --- a/internal/server/instance/drivers/driver_lxc.go +++ b/internal/server/instance/drivers/driver_lxc.go @@ -1192,7 +1192,10 @@ func (d *lxc) initLXC(config bool) (*liblxc.Container, error) { if util.IsTrueOrEmpty(memorySwap) || util.IsFalse(memorySwap) { err = cg.SetMemorySwapLimit(0) if err != nil { - return nil, err + // Ignore missing swap accounting unless explicitly configured. + if memorySwap != "" || !errors.Is(err, cgroup.ErrControllerMissing) { + return nil, err + } } } else { // Additional memory as swap. @@ -5332,7 +5335,7 @@ func (d *lxc) Update(args db.InstanceArgs, userRequested bool) error { // Store the old values for revert oldMemswLimit := int64(-1) - if cgroup.Supports(cgroup.Memory) { + if cgroup.Supports(cgroup.MemorySwap) { oldMemswLimit, err = cg.GetMemorySwapLimit() if err != nil { oldMemswLimit = -1 @@ -5363,7 +5366,7 @@ func (d *lxc) Update(args db.InstanceArgs, userRequested bool) error { } // Reset everything - if cgroup.Supports(cgroup.Memory) { + if cgroup.Supports(cgroup.MemorySwap) { err = cg.SetMemorySwapLimit(-1) if err != nil { revertMemory() @@ -5398,26 +5401,27 @@ func (d *lxc) Update(args db.InstanceArgs, userRequested bool) error { return err } - if cgroup.Supports(cgroup.Memory) { - if util.IsTrueOrEmpty(memorySwap) || util.IsFalse(memorySwap) { - err = cg.SetMemorySwapLimit(0) - if err != nil { - revertMemory() - return err - } - } else { - // Additional memory as swap. - swapInt, err := units.ParseByteSizeString(memorySwap) - if err != nil { + if util.IsTrueOrEmpty(memorySwap) || util.IsFalse(memorySwap) { + err = cg.SetMemorySwapLimit(0) + if err != nil { + // Ignore missing swap accounting unless explicitly configured. + if memorySwap != "" || !errors.Is(err, cgroup.ErrControllerMissing) { revertMemory() return err } + } + } else { + // Additional memory as swap. + swapInt, err := units.ParseByteSizeString(memorySwap) + if err != nil { + revertMemory() + return err + } - err = cg.SetMemorySwapLimit(swapInt) - if err != nil { - revertMemory() - return err - } + err = cg.SetMemorySwapLimit(swapInt) + if err != nil { + revertMemory() + return err } } } @@ -5441,8 +5445,11 @@ func (d *lxc) Update(args db.InstanceArgs, userRequested bool) error { // Maximum priority (10) should be default swappiness (60). err = cg.SetMemorySwappiness(int64(70 - priority)) - if err != nil && !errors.Is(err, cgroup.ErrControllerMissing) { - return err + if err != nil { + // Ignore missing swappiness support unless explicitly configured. + if memorySwapPriority != "" || !errors.Is(err, cgroup.ErrControllerMissing) { + return err + } } } } @@ -9370,7 +9377,7 @@ func (d *lxc) Metrics(hostInterfaces []net.Interface) (*metrics.MetricSet, error out.AddSamples(metrics.MemoryOOMKillsTotal, metrics.Sample{Value: float64(oomKills)}) // Handle swap. - if cgroup.Supports(cgroup.Memory) { + if cgroup.Supports(cgroup.MemorySwap) { swapUsage, err := cg.GetMemorySwapUsage() if err != nil { d.logger.Warn("Failed to get swap usage", logger.Ctx{"err": err}) diff --git a/internal/server/seccomp/seccomp.go b/internal/server/seccomp/seccomp.go index 1dc7152178..3b293eeb44 100644 --- a/internal/server/seccomp/seccomp.go +++ b/internal/server/seccomp/seccomp.go @@ -1797,7 +1797,7 @@ func (srv *Server) HandleSysinfoSyscall(c Instance, siov *Iovec) int { instMetrics.Freeram = instMetrics.Totalram - uint64(memoryUsage) - instMetrics.Bufferram // Get instance swap info. - if cgroup.Supports(cgroup.Memory) { + if cgroup.Supports(cgroup.MemorySwap) { swapLimit, err := cg.GetMemorySwapLimit() if err != nil { l.Warn("Failed getting swap limit", logger.Ctx{"err": err})