diff --git a/doc/api-extensions.md b/doc/api-extensions.md index f16c3877f28..7bd290abff4 100644 --- a/doc/api-extensions.md +++ b/doc/api-extensions.md @@ -2430,3 +2430,6 @@ Those integrations attach to network peers through some new fields: * `type` (`local` for current behavior, `remote` for integrations) * `target_integration` (reference to the integration) + +## `instance_memory_swap_bytes` +This extends `limits.memory.swap` to allow for a total limit in bytes. diff --git a/doc/config_options.txt b/doc/config_options.txt index b3d80ed12ae..a18c56b192b 100644 --- a/doc/config_options.txt +++ b/doc/config_options.txt @@ -439,9 +439,11 @@ If this option is set to `false`, regular system memory is used. :condition: "container" :defaultdesc: "`true`" :liveupdate: "yes" -:shortdesc: "Whether to encourage/discourage swapping less used pages for this instance" -:type: "bool" - +:shortdesc: "Control swap usage by the instance" +:type: "string" +When set to `true` or `false`, it controls whether the container is likely to get some of +its memory swapped by the kernel. Alternatively, it can be set to a bytes value which will +then allow the container to make use of additional memory through swap. ``` ```{config:option} limits.memory.swap.priority instance-resource-limits diff --git a/internal/instance/config.go b/internal/instance/config.go index ccef496de35..892dc4394c5 100644 --- a/internal/instance/config.go +++ b/internal/instance/config.go @@ -506,14 +506,16 @@ var InstanceConfigKeysContainer = map[string]func(value string) error{ "limits.memory.enforce": validate.Optional(validate.IsOneOf("soft", "hard")), // gendoc:generate(entity=instance, group=resource-limits, key=limits.memory.swap) - // + // When set to `true` or `false`, it controls whether the container is likely to get some of + // its memory swapped by the kernel. Alternatively, it can be set to a bytes value which will + // then allow the container to make use of additional memory through swap. // --- - // type: bool + // type: string // defaultdesc: `true` // liveupdate: yes // condition: container - // shortdesc: Whether to encourage/discourage swapping less used pages for this instance - "limits.memory.swap": validate.Optional(validate.IsBool), + // shortdesc: Control swap usage by the instance + "limits.memory.swap": validate.Optional(validate.Or(validate.IsBool, validate.IsSize)), // gendoc:generate(entity=instance, group=resource-limits, key=limits.memory.swap.priority) // Specify an integer between 0 and 10. diff --git a/internal/server/instance/drivers/driver_lxc.go b/internal/server/instance/drivers/driver_lxc.go index 51d13c0b9db..11b0532e3e2 100644 --- a/internal/server/instance/drivers/driver_lxc.go +++ b/internal/server/instance/drivers/driver_lxc.go @@ -1155,27 +1155,28 @@ func (d *lxc) initLXC(config bool) (*liblxc.Container, error) { return nil, err } } else { - if d.state.OS.CGInfo.Supports(cgroup.MemorySwap, cg) { - err = cg.SetMemoryLimit(valueInt) - if err != nil { - return nil, err - } + err = cg.SetMemoryLimit(valueInt) + if err != nil { + return nil, err + } - if util.IsFalse(memorySwap) { + if d.state.OS.CGInfo.Supports(cgroup.MemorySwap, cg) { + if util.IsTrueOrEmpty(memorySwap) || util.IsFalse(memorySwap) { err = cg.SetMemorySwapLimit(0) if err != nil { return nil, err } } else { - err = cg.SetMemorySwapLimit(valueInt) + // Additional memory as swap. + swapInt, err := units.ParseByteSizeString(memorySwap) + if err != nil { + return nil, err + } + + err = cg.SetMemorySwapLimit(swapInt) if err != nil { return nil, err } - } - } else { - err = cg.SetMemoryLimit(valueInt) - if err != nil { - return nil, err } } @@ -4624,31 +4625,32 @@ func (d *lxc) Update(args db.InstanceArgs, userRequested bool) error { return err } } else { - if d.state.OS.CGInfo.Supports(cgroup.MemorySwap, cg) { - err = cg.SetMemoryLimit(memoryInt) - if err != nil { - revertMemory() - return err - } + err = cg.SetMemoryLimit(memoryInt) + if err != nil { + revertMemory() + return err + } - if util.IsFalse(memorySwap) { + if d.state.OS.CGInfo.Supports(cgroup.MemorySwap, cg) { + if util.IsTrueOrEmpty(memorySwap) || util.IsFalse(memorySwap) { err = cg.SetMemorySwapLimit(0) if err != nil { revertMemory() return err } } else { - err = cg.SetMemorySwapLimit(memoryInt) + // 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 } - } - } else { - err = cg.SetMemoryLimit(memoryInt) - if err != nil { - revertMemory() - return err } } @@ -4666,7 +4668,6 @@ func (d *lxc) Update(args db.InstanceArgs, userRequested bool) error { // Configure the swappiness if key == "limits.memory.swap" || key == "limits.memory.swap.priority" { - memorySwap := d.expandedConfig["limits.memory.swap"] memorySwapPriority := d.expandedConfig["limits.memory.swap.priority"] if util.IsFalse(memorySwap) { err = cg.SetMemorySwappiness(0) diff --git a/internal/server/metadata/configuration.json b/internal/server/metadata/configuration.json index b2a94b9447d..9ebe0ec2c18 100644 --- a/internal/server/metadata/configuration.json +++ b/internal/server/metadata/configuration.json @@ -453,9 +453,9 @@ "condition": "container", "defaultdesc": "`true`", "liveupdate": "yes", - "longdesc": "", - "shortdesc": "Whether to encourage/discourage swapping less used pages for this instance", - "type": "bool" + "longdesc": "When set to `true` or `false`, it controls whether the container is likely to get some of\nits memory swapped by the kernel. Alternatively, it can be set to a bytes value which will\nthen allow the container to make use of additional memory through swap.", + "shortdesc": "Control swap usage by the instance", + "type": "string" } }, { diff --git a/internal/server/project/permissions.go b/internal/server/project/permissions.go index eb643e97ecf..4cc6cb2cc88 100644 --- a/internal/server/project/permissions.go +++ b/internal/server/project/permissions.go @@ -836,6 +836,7 @@ func isContainerLowLevelOptionForbidden(key string) bool { "boot.host_shutdown_action", "boot.host_shutdown_timeout", "linux.kernel_modules", + "limits.memory.swap", "raw.apparmor", "raw.idmap", "raw.lxc", diff --git a/internal/version/api.go b/internal/version/api.go index 76075a80c31..7ae36ca0cd7 100644 --- a/internal/version/api.go +++ b/internal/version/api.go @@ -406,6 +406,7 @@ var APIExtensions = []string{ "numa_cpu_balanced", "image_restriction_nesting", "network_integrations", + "instance_memory_swap_bytes", } // APIExtensionsCount returns the number of available API extensions.