mirror of
https://github.com/siderolabs/talos.git
synced 2026-05-05 12:26:21 +02:00
feat: implement zswap support
Zswap allows to compress pages in memory before they hit the actual swap device. Both swap and zswap (or either one of these) can be enabled. Fixes #10675 Signed-off-by: Andrey Smirnov <andrey.smirnov@siderolabs.com>
This commit is contained in:
parent
7f0300f108
commit
c880835c80
@ -243,3 +243,17 @@ message VolumeStatusSpec {
|
||||
string parent_id = 19;
|
||||
}
|
||||
|
||||
// ZswapStatusSpec is the spec for ZswapStatus resource.
|
||||
message ZswapStatusSpec {
|
||||
uint64 total_size_bytes = 1;
|
||||
string total_size_human = 2;
|
||||
uint64 stored_pages = 3;
|
||||
uint64 pool_limit_hit = 4;
|
||||
uint64 reject_reclaim_fail = 5;
|
||||
uint64 reject_alloc_fail = 6;
|
||||
uint64 reject_kmemcache_fail = 7;
|
||||
uint64 reject_compress_fail = 8;
|
||||
uint64 reject_compress_poor = 9;
|
||||
uint64 written_back_pages = 10;
|
||||
}
|
||||
|
||||
|
||||
@ -7,3 +7,9 @@ columns:
|
||||
template: '{{ .MemorySwapHigh.HumanizeIBytes | printf "%8s" }}'
|
||||
- name: SwapMax
|
||||
template: '{{ .MemorySwapMax.HumanizeIBytes | printf "%8s" }}'
|
||||
- name: ZswapCurrent
|
||||
template: '{{ .MemoryZswapCurrent.HumanizeIBytes | printf "%8s" }}'
|
||||
- name: ZswapMax
|
||||
template: '{{ .MemoryZswapMax.HumanizeIBytes | printf "%8s" }}'
|
||||
- name: ZswapWriteback
|
||||
template: '{{ .MemoryZswapWriteback }}'
|
||||
|
||||
@ -69,6 +69,13 @@ Talos now validates Kubernetes version in the image submitted in the machine con
|
||||
Previously this check was performed only on upgrade, but now it is consistently applied to upgrade, initial provisioning, and machine configuration updates.
|
||||
|
||||
This implies that all image references should contain the tag, even if the image is pinned by digest.
|
||||
"""
|
||||
|
||||
[notes.zswap]
|
||||
title = "Zswap Support"
|
||||
description = """\
|
||||
Talos now supports zswap, a compressed cache for swap pages.
|
||||
This feature can be enabled by using [ZswapConfig](https://www.talos.dev/v1.11/reference/configuration/block/zswapconfig/) document in the machine configuration.
|
||||
"""
|
||||
|
||||
[make_deps]
|
||||
|
||||
@ -21,3 +21,8 @@ machine:
|
||||
extraConfig:
|
||||
memorySwap:
|
||||
swapBehavior: LimitedSwap
|
||||
---
|
||||
apiVersion: v1alpha1
|
||||
kind: ZswapConfig
|
||||
maxPoolPercent: 25
|
||||
shrinkerEnabled: true
|
||||
|
||||
115
internal/app/machined/pkg/controllers/block/zswap_config.go
Normal file
115
internal/app/machined/pkg/controllers/block/zswap_config.go
Normal file
@ -0,0 +1,115 @@
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
package block
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strconv"
|
||||
|
||||
"github.com/cosi-project/runtime/pkg/controller"
|
||||
"github.com/cosi-project/runtime/pkg/safe"
|
||||
"github.com/cosi-project/runtime/pkg/state"
|
||||
"github.com/siderolabs/gen/optional"
|
||||
"go.uber.org/zap"
|
||||
|
||||
configconfig "github.com/siderolabs/talos/pkg/machinery/config/config"
|
||||
"github.com/siderolabs/talos/pkg/machinery/resources/config"
|
||||
"github.com/siderolabs/talos/pkg/machinery/resources/runtime"
|
||||
)
|
||||
|
||||
// ZswapConfigController provides zswap configuration based machine configuration.
|
||||
type ZswapConfigController struct{}
|
||||
|
||||
// Name implements controller.Controller interface.
|
||||
func (ctrl *ZswapConfigController) Name() string {
|
||||
return "block.ZswapConfigController"
|
||||
}
|
||||
|
||||
// Inputs implements controller.Controller interface.
|
||||
func (ctrl *ZswapConfigController) Inputs() []controller.Input {
|
||||
return []controller.Input{
|
||||
{
|
||||
Namespace: config.NamespaceName,
|
||||
Type: config.MachineConfigType,
|
||||
ID: optional.Some(config.ActiveID),
|
||||
Kind: controller.InputWeak,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// Outputs implements controller.Controller interface.
|
||||
func (ctrl *ZswapConfigController) Outputs() []controller.Output {
|
||||
return []controller.Output{
|
||||
{
|
||||
Type: runtime.KernelParamSpecType,
|
||||
Kind: controller.OutputShared,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// Run implements controller.Controller interface.
|
||||
//
|
||||
//nolint:gocyclo
|
||||
func (ctrl *ZswapConfigController) Run(ctx context.Context, r controller.Runtime, _ *zap.Logger) error {
|
||||
for {
|
||||
select {
|
||||
case <-r.EventCh():
|
||||
case <-ctx.Done():
|
||||
return nil
|
||||
}
|
||||
|
||||
// load config if present
|
||||
cfg, err := safe.ReaderGetByID[*config.MachineConfig](ctx, r, config.ActiveID)
|
||||
if err != nil && !state.IsNotFoundError(err) {
|
||||
return fmt.Errorf("error fetching machine configuration")
|
||||
}
|
||||
|
||||
r.StartTrackingOutputs()
|
||||
|
||||
var zswapCfg configconfig.ZswapConfig
|
||||
|
||||
if cfg != nil {
|
||||
zswapCfg = cfg.Config().ZswapConfig()
|
||||
}
|
||||
|
||||
if zswapCfg != nil { // enabled
|
||||
if err := safe.WriterModify(ctx, r, runtime.NewKernelParamSpec(runtime.NamespaceName, "sys.module.zswap.parameters.enabled"),
|
||||
func(p *runtime.KernelParamSpec) error {
|
||||
p.TypedSpec().Value = "Y"
|
||||
|
||||
return nil
|
||||
}); err != nil {
|
||||
return fmt.Errorf("error setting zswap config: %w", err)
|
||||
}
|
||||
|
||||
if err := safe.WriterModify(ctx, r, runtime.NewKernelParamSpec(runtime.NamespaceName, "sys.module.zswap.parameters.max_pool_percent"),
|
||||
func(p *runtime.KernelParamSpec) error {
|
||||
p.TypedSpec().Value = strconv.Itoa(zswapCfg.MaxPoolPercent())
|
||||
|
||||
return nil
|
||||
}); err != nil {
|
||||
return fmt.Errorf("error setting zswap config: %w", err)
|
||||
}
|
||||
|
||||
if err := safe.WriterModify(ctx, r, runtime.NewKernelParamSpec(runtime.NamespaceName, "sys.module.zswap.parameters.shrinker_enabled"),
|
||||
func(p *runtime.KernelParamSpec) error {
|
||||
if zswapCfg.ShrinkerEnabled() {
|
||||
p.TypedSpec().Value = "Y"
|
||||
} else {
|
||||
p.TypedSpec().Value = "N"
|
||||
}
|
||||
|
||||
return nil
|
||||
}); err != nil {
|
||||
return fmt.Errorf("error setting zswap config: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
if err = safe.CleanupOutputs[*runtime.KernelParamSpec](ctx, r); err != nil {
|
||||
return fmt.Errorf("error cleaning up volume configuration: %w", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
131
internal/app/machined/pkg/controllers/block/zswap_status.go
Normal file
131
internal/app/machined/pkg/controllers/block/zswap_status.go
Normal file
@ -0,0 +1,131 @@
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
package block
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/cosi-project/runtime/pkg/controller"
|
||||
"github.com/cosi-project/runtime/pkg/safe"
|
||||
"github.com/dustin/go-humanize"
|
||||
"go.uber.org/zap"
|
||||
|
||||
machineruntime "github.com/siderolabs/talos/internal/app/machined/pkg/runtime"
|
||||
"github.com/siderolabs/talos/pkg/machinery/resources/block"
|
||||
"github.com/siderolabs/talos/pkg/machinery/resources/runtime"
|
||||
)
|
||||
|
||||
// ZswapStatusController provides a view of active swap devices.
|
||||
type ZswapStatusController struct {
|
||||
V1Alpha1Mode machineruntime.Mode
|
||||
}
|
||||
|
||||
// Name implements controller.Controller interface.
|
||||
func (ctrl *ZswapStatusController) Name() string {
|
||||
return "block.ZswapStatusController"
|
||||
}
|
||||
|
||||
// Inputs implements controller.Controller interface.
|
||||
func (ctrl *ZswapStatusController) Inputs() []controller.Input {
|
||||
return []controller.Input{
|
||||
{
|
||||
// not really a dependency, but we refresh zswap status kernel param change
|
||||
Namespace: runtime.NamespaceName,
|
||||
Type: runtime.KernelParamStatusType,
|
||||
Kind: controller.InputWeak,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// Outputs implements controller.Controller interface.
|
||||
func (ctrl *ZswapStatusController) Outputs() []controller.Output {
|
||||
return []controller.Output{
|
||||
{
|
||||
Type: block.ZswapStatusType,
|
||||
Kind: controller.OutputExclusive,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// Run implements controller.Controller interface.
|
||||
//
|
||||
//nolint:gocyclo
|
||||
func (ctrl *ZswapStatusController) Run(ctx context.Context, r controller.Runtime, logger *zap.Logger) error {
|
||||
// in container mode, no zswap applies
|
||||
if ctrl.V1Alpha1Mode == machineruntime.ModeContainer {
|
||||
return nil
|
||||
}
|
||||
|
||||
// there is no way to watch for zswap status, so we are going to poll every minute
|
||||
ticker := time.NewTicker(time.Minute)
|
||||
defer ticker.Stop()
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return nil
|
||||
case <-r.EventCh():
|
||||
case <-ticker.C:
|
||||
}
|
||||
|
||||
r.StartTrackingOutputs()
|
||||
|
||||
// try to read a single status file to see if zswap is enabled
|
||||
if _, err := os.ReadFile("/sys/kernel/debug/zswap/pool_total_size"); err == nil {
|
||||
if err = safe.WriterModify(ctx, r, block.NewZswapStatus(block.NamespaceName, block.ZswapStatusID),
|
||||
func(zs *block.ZswapStatus) error {
|
||||
for _, p := range []struct {
|
||||
name string
|
||||
out *uint64
|
||||
}{
|
||||
{"pool_total_size", &zs.TypedSpec().TotalSizeBytes},
|
||||
{"stored_pages", &zs.TypedSpec().StoredPages},
|
||||
{"pool_limit_hit", &zs.TypedSpec().PoolLimitHit},
|
||||
{"reject_reclaim_fail", &zs.TypedSpec().RejectReclaimFail},
|
||||
{"reject_alloc_fail", &zs.TypedSpec().RejectAllocFail},
|
||||
{"reject_kmemcache_fail", &zs.TypedSpec().RejectKmemcacheFail},
|
||||
{"reject_compress_fail", &zs.TypedSpec().RejectCompressFail},
|
||||
{"reject_compress_poor", &zs.TypedSpec().RejectCompressPoor},
|
||||
{"written_back_pages", &zs.TypedSpec().WrittenBackPages},
|
||||
} {
|
||||
if err := ctrl.readZswapParam(p.name, p.out); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
zs.TypedSpec().TotalSizeHuman = humanize.IBytes(zs.TypedSpec().TotalSizeBytes)
|
||||
|
||||
return nil
|
||||
},
|
||||
); err != nil {
|
||||
return fmt.Errorf("failed to create zswap status: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
if err := safe.CleanupOutputs[*block.ZswapStatus](ctx, r); err != nil {
|
||||
return fmt.Errorf("failed to cleanup outputs: %w", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (ctrl *ZswapStatusController) readZswapParam(name string, out *uint64) error {
|
||||
content, err := os.ReadFile(filepath.Join("/sys/kernel/debug/zswap", name))
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to read zswap parameter %q: %w", name, err)
|
||||
}
|
||||
|
||||
*out, err = strconv.ParseUint(string(bytes.TrimSpace(content)), 10, 64)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to parse zswap parameter %q: %w", name, err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@ -110,6 +110,10 @@ func (ctrl *Controller) Run(ctx context.Context, drainer *runtime.Drainer) error
|
||||
V1Alpha1Mode: ctrl.v1alpha1Runtime.State().Platform().Mode(),
|
||||
},
|
||||
&block.VolumeManagerController{},
|
||||
&block.ZswapConfigController{},
|
||||
&block.ZswapStatusController{
|
||||
V1Alpha1Mode: ctrl.v1alpha1Runtime.State().Platform().Mode(),
|
||||
},
|
||||
&cluster.AffiliateMergeController{},
|
||||
cluster.NewConfigController(),
|
||||
&cluster.DiscoveryServiceController{},
|
||||
|
||||
@ -112,6 +112,7 @@ func NewState() (*State, error) {
|
||||
&block.VolumeMountRequest{},
|
||||
&block.VolumeMountStatus{},
|
||||
&block.VolumeStatus{},
|
||||
&block.ZswapStatus{},
|
||||
&cluster.Affiliate{},
|
||||
&cluster.Config{},
|
||||
&cluster.Identity{},
|
||||
|
||||
@ -152,9 +152,10 @@ func CreateSystemCgroups(ctx context.Context, log *zap.Logger, rt runtime.Runtim
|
||||
name: constants.CgroupApid,
|
||||
resources: &cgroup2.Resources{
|
||||
Memory: &cgroup2.Memory{
|
||||
Min: pointer.To[int64](constants.CgroupApidReservedMemory),
|
||||
Low: pointer.To[int64](constants.CgroupApidReservedMemory * 2),
|
||||
Max: zeroIfRace(pointer.To[int64](constants.CgroupApidMaxMemory)),
|
||||
Min: pointer.To[int64](constants.CgroupApidReservedMemory),
|
||||
Low: pointer.To[int64](constants.CgroupApidReservedMemory * 2),
|
||||
Max: zeroIfRace(pointer.To[int64](constants.CgroupApidMaxMemory)),
|
||||
Swap: pointer.To[int64](0),
|
||||
},
|
||||
CPU: &cgroup2.CPU{
|
||||
Weight: pointer.To[uint64](cgroup.MillicoresToCPUWeight(cgroup.MilliCores(constants.CgroupApidMillicores))),
|
||||
@ -165,9 +166,10 @@ func CreateSystemCgroups(ctx context.Context, log *zap.Logger, rt runtime.Runtim
|
||||
name: constants.CgroupTrustd,
|
||||
resources: &cgroup2.Resources{
|
||||
Memory: &cgroup2.Memory{
|
||||
Min: pointer.To[int64](constants.CgroupTrustdReservedMemory),
|
||||
Low: pointer.To[int64](constants.CgroupTrustdReservedMemory * 2),
|
||||
Max: zeroIfRace(pointer.To[int64](constants.CgroupTrustdMaxMemory)),
|
||||
Min: pointer.To[int64](constants.CgroupTrustdReservedMemory),
|
||||
Low: pointer.To[int64](constants.CgroupTrustdReservedMemory * 2),
|
||||
Max: zeroIfRace(pointer.To[int64](constants.CgroupTrustdMaxMemory)),
|
||||
Swap: pointer.To[int64](0),
|
||||
},
|
||||
CPU: &cgroup2.CPU{
|
||||
Weight: pointer.To[uint64](cgroup.MillicoresToCPUWeight(cgroup.MilliCores(constants.CgroupTrustdMillicores))),
|
||||
|
||||
@ -755,6 +755,29 @@ func (suite *VolumesSuite) TestSwapOnOff() {
|
||||
}))
|
||||
}
|
||||
|
||||
// TestZswapStatus verifies that all zswap-enabled machines have zswap running.
|
||||
func (suite *VolumesSuite) TestZswapStatus() {
|
||||
for _, node := range suite.DiscoverNodeInternalIPs(suite.ctx) {
|
||||
suite.Run(node, func() {
|
||||
ctx := client.WithNode(suite.ctx, node)
|
||||
|
||||
cfg, err := suite.ReadConfigFromNode(ctx)
|
||||
suite.Require().NoError(err)
|
||||
|
||||
if cfg.ZswapConfig() == nil {
|
||||
suite.T().Skipf("skipping test, zswap is not enabled on node %s", node)
|
||||
}
|
||||
|
||||
rtestutils.AssertResource(ctx, suite.T(), suite.Client.COSI,
|
||||
block.ZswapStatusID,
|
||||
func(vs *block.ZswapStatus, asrt *assert.Assertions) {
|
||||
suite.T().Logf("zswap total size %s, stored pages %d", vs.TypedSpec().TotalSizeHuman, vs.TypedSpec().StoredPages)
|
||||
},
|
||||
)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func init() {
|
||||
allSuites = append(allSuites, new(VolumesSuite))
|
||||
}
|
||||
|
||||
@ -134,6 +134,10 @@ type Node struct {
|
||||
MemorySwapMax Value
|
||||
MemorySwapPeak Value
|
||||
|
||||
MemoryZswapCurrent Value
|
||||
MemoryZswapMax Value
|
||||
MemoryZswapWriteback Value
|
||||
|
||||
PidsCurrent Value
|
||||
PidsEvents FlatMap
|
||||
PidsMax Value
|
||||
@ -280,6 +284,12 @@ func (n *Node) Parse(filename string, r io.Reader) error {
|
||||
return parseSingleValue(ParseNewlineSeparatedValues, &n.MemorySwapMax, r)
|
||||
case "memory.swap.peak":
|
||||
return parseSingleValue(ParseNewlineSeparatedValues, &n.MemorySwapPeak, r)
|
||||
case "memory.zswap.current":
|
||||
return parseSingleValue(ParseNewlineSeparatedValues, &n.MemoryZswapCurrent, r)
|
||||
case "memory.zswap.max":
|
||||
return parseSingleValue(ParseNewlineSeparatedValues, &n.MemoryZswapMax, r)
|
||||
case "memory.zswap.writeback":
|
||||
return parseSingleValue(ParseNewlineSeparatedValues, &n.MemoryZswapWriteback, r)
|
||||
case "pids.current":
|
||||
return parseSingleValue(ParseNewlineSeparatedValues, &n.PidsCurrent, r)
|
||||
case "pids.events":
|
||||
|
||||
@ -41,6 +41,7 @@ func PseudoSubMountPoints() Points {
|
||||
NewPoint("securityfs", "/sys/kernel/security", "securityfs", WithFlags(unix.MS_NOSUID|unix.MS_NOEXEC|unix.MS_NODEV|unix.MS_RELATIME)),
|
||||
NewPoint("tracefs", "/sys/kernel/tracing", "tracefs", WithFlags(unix.MS_NOSUID|unix.MS_NOEXEC|unix.MS_NODEV)),
|
||||
NewPoint("configfs", "/sys/kernel/config", "configfs", WithFlags(unix.MS_NOSUID|unix.MS_NOEXEC|unix.MS_NODEV|unix.MS_RELATIME)),
|
||||
NewPoint("debugfs", "/sys/kernel/debug", "debugfs", WithFlags(unix.MS_NOSUID|unix.MS_NOEXEC|unix.MS_NODEV|unix.MS_RELATIME)),
|
||||
}
|
||||
|
||||
if _, err := os.Stat(constants.EFIVarsMountPoint); err == nil {
|
||||
|
||||
@ -2025,6 +2025,123 @@ func (x *VolumeStatusSpec) GetParentId() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
// ZswapStatusSpec is the spec for ZswapStatus resource.
|
||||
type ZswapStatusSpec struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
TotalSizeBytes uint64 `protobuf:"varint,1,opt,name=total_size_bytes,json=totalSizeBytes,proto3" json:"total_size_bytes,omitempty"`
|
||||
TotalSizeHuman string `protobuf:"bytes,2,opt,name=total_size_human,json=totalSizeHuman,proto3" json:"total_size_human,omitempty"`
|
||||
StoredPages uint64 `protobuf:"varint,3,opt,name=stored_pages,json=storedPages,proto3" json:"stored_pages,omitempty"`
|
||||
PoolLimitHit uint64 `protobuf:"varint,4,opt,name=pool_limit_hit,json=poolLimitHit,proto3" json:"pool_limit_hit,omitempty"`
|
||||
RejectReclaimFail uint64 `protobuf:"varint,5,opt,name=reject_reclaim_fail,json=rejectReclaimFail,proto3" json:"reject_reclaim_fail,omitempty"`
|
||||
RejectAllocFail uint64 `protobuf:"varint,6,opt,name=reject_alloc_fail,json=rejectAllocFail,proto3" json:"reject_alloc_fail,omitempty"`
|
||||
RejectKmemcacheFail uint64 `protobuf:"varint,7,opt,name=reject_kmemcache_fail,json=rejectKmemcacheFail,proto3" json:"reject_kmemcache_fail,omitempty"`
|
||||
RejectCompressFail uint64 `protobuf:"varint,8,opt,name=reject_compress_fail,json=rejectCompressFail,proto3" json:"reject_compress_fail,omitempty"`
|
||||
RejectCompressPoor uint64 `protobuf:"varint,9,opt,name=reject_compress_poor,json=rejectCompressPoor,proto3" json:"reject_compress_poor,omitempty"`
|
||||
WrittenBackPages uint64 `protobuf:"varint,10,opt,name=written_back_pages,json=writtenBackPages,proto3" json:"written_back_pages,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *ZswapStatusSpec) Reset() {
|
||||
*x = ZswapStatusSpec{}
|
||||
mi := &file_resource_definitions_block_block_proto_msgTypes[24]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *ZswapStatusSpec) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*ZswapStatusSpec) ProtoMessage() {}
|
||||
|
||||
func (x *ZswapStatusSpec) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_resource_definitions_block_block_proto_msgTypes[24]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use ZswapStatusSpec.ProtoReflect.Descriptor instead.
|
||||
func (*ZswapStatusSpec) Descriptor() ([]byte, []int) {
|
||||
return file_resource_definitions_block_block_proto_rawDescGZIP(), []int{24}
|
||||
}
|
||||
|
||||
func (x *ZswapStatusSpec) GetTotalSizeBytes() uint64 {
|
||||
if x != nil {
|
||||
return x.TotalSizeBytes
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *ZswapStatusSpec) GetTotalSizeHuman() string {
|
||||
if x != nil {
|
||||
return x.TotalSizeHuman
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *ZswapStatusSpec) GetStoredPages() uint64 {
|
||||
if x != nil {
|
||||
return x.StoredPages
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *ZswapStatusSpec) GetPoolLimitHit() uint64 {
|
||||
if x != nil {
|
||||
return x.PoolLimitHit
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *ZswapStatusSpec) GetRejectReclaimFail() uint64 {
|
||||
if x != nil {
|
||||
return x.RejectReclaimFail
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *ZswapStatusSpec) GetRejectAllocFail() uint64 {
|
||||
if x != nil {
|
||||
return x.RejectAllocFail
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *ZswapStatusSpec) GetRejectKmemcacheFail() uint64 {
|
||||
if x != nil {
|
||||
return x.RejectKmemcacheFail
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *ZswapStatusSpec) GetRejectCompressFail() uint64 {
|
||||
if x != nil {
|
||||
return x.RejectCompressFail
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *ZswapStatusSpec) GetRejectCompressPoor() uint64 {
|
||||
if x != nil {
|
||||
return x.RejectCompressPoor
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *ZswapStatusSpec) GetWrittenBackPages() uint64 {
|
||||
if x != nil {
|
||||
return x.WrittenBackPages
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
var File_resource_definitions_block_block_proto protoreflect.FileDescriptor
|
||||
|
||||
const file_resource_definitions_block_block_proto_rawDesc = "" +
|
||||
@ -2225,7 +2342,19 @@ const file_resource_definitions_block_block_proto_rawDesc = "" +
|
||||
"\x04type\x18\x10 \x01(\x0e21.talos.resource.definitions.enums.BlockVolumeTypeR\x04type\x12<\n" +
|
||||
"\x1aconfigured_encryption_keys\x18\x11 \x03(\tR\x18configuredEncryptionKeys\x12\\\n" +
|
||||
"\fsymlink_spec\x18\x12 \x01(\v29.talos.resource.definitions.block.SymlinkProvisioningSpecR\vsymlinkSpec\x12\x1b\n" +
|
||||
"\tparent_id\x18\x13 \x01(\tR\bparentIdBt\n" +
|
||||
"\tparent_id\x18\x13 \x01(\tR\bparentId\"\xd0\x03\n" +
|
||||
"\x0fZswapStatusSpec\x12(\n" +
|
||||
"\x10total_size_bytes\x18\x01 \x01(\x04R\x0etotalSizeBytes\x12(\n" +
|
||||
"\x10total_size_human\x18\x02 \x01(\tR\x0etotalSizeHuman\x12!\n" +
|
||||
"\fstored_pages\x18\x03 \x01(\x04R\vstoredPages\x12$\n" +
|
||||
"\x0epool_limit_hit\x18\x04 \x01(\x04R\fpoolLimitHit\x12.\n" +
|
||||
"\x13reject_reclaim_fail\x18\x05 \x01(\x04R\x11rejectReclaimFail\x12*\n" +
|
||||
"\x11reject_alloc_fail\x18\x06 \x01(\x04R\x0frejectAllocFail\x122\n" +
|
||||
"\x15reject_kmemcache_fail\x18\a \x01(\x04R\x13rejectKmemcacheFail\x120\n" +
|
||||
"\x14reject_compress_fail\x18\b \x01(\x04R\x12rejectCompressFail\x120\n" +
|
||||
"\x14reject_compress_poor\x18\t \x01(\x04R\x12rejectCompressPoor\x12,\n" +
|
||||
"\x12written_back_pages\x18\n" +
|
||||
" \x01(\x04R\x10writtenBackPagesBt\n" +
|
||||
"(dev.talos.api.resource.definitions.blockZHgithub.com/siderolabs/talos/pkg/machinery/api/resource/definitions/blockb\x06proto3"
|
||||
|
||||
var (
|
||||
@ -2240,7 +2369,7 @@ func file_resource_definitions_block_block_proto_rawDescGZIP() []byte {
|
||||
return file_resource_definitions_block_block_proto_rawDescData
|
||||
}
|
||||
|
||||
var file_resource_definitions_block_block_proto_msgTypes = make([]protoimpl.MessageInfo, 24)
|
||||
var file_resource_definitions_block_block_proto_msgTypes = make([]protoimpl.MessageInfo, 25)
|
||||
var file_resource_definitions_block_block_proto_goTypes = []any{
|
||||
(*DeviceSpec)(nil), // 0: talos.resource.definitions.block.DeviceSpec
|
||||
(*DiscoveredVolumeSpec)(nil), // 1: talos.resource.definitions.block.DiscoveredVolumeSpec
|
||||
@ -2266,38 +2395,39 @@ var file_resource_definitions_block_block_proto_goTypes = []any{
|
||||
(*VolumeMountRequestSpec)(nil), // 21: talos.resource.definitions.block.VolumeMountRequestSpec
|
||||
(*VolumeMountStatusSpec)(nil), // 22: talos.resource.definitions.block.VolumeMountStatusSpec
|
||||
(*VolumeStatusSpec)(nil), // 23: talos.resource.definitions.block.VolumeStatusSpec
|
||||
(*v1alpha1.CheckedExpr)(nil), // 24: google.api.expr.v1alpha1.CheckedExpr
|
||||
(enums.BlockEncryptionKeyType)(0), // 25: talos.resource.definitions.enums.BlockEncryptionKeyType
|
||||
(enums.BlockEncryptionProviderType)(0), // 26: talos.resource.definitions.enums.BlockEncryptionProviderType
|
||||
(enums.BlockFilesystemType)(0), // 27: talos.resource.definitions.enums.BlockFilesystemType
|
||||
(enums.BlockVolumeType)(0), // 28: talos.resource.definitions.enums.BlockVolumeType
|
||||
(enums.BlockVolumePhase)(0), // 29: talos.resource.definitions.enums.BlockVolumePhase
|
||||
(*ZswapStatusSpec)(nil), // 24: talos.resource.definitions.block.ZswapStatusSpec
|
||||
(*v1alpha1.CheckedExpr)(nil), // 25: google.api.expr.v1alpha1.CheckedExpr
|
||||
(enums.BlockEncryptionKeyType)(0), // 26: talos.resource.definitions.enums.BlockEncryptionKeyType
|
||||
(enums.BlockEncryptionProviderType)(0), // 27: talos.resource.definitions.enums.BlockEncryptionProviderType
|
||||
(enums.BlockFilesystemType)(0), // 28: talos.resource.definitions.enums.BlockFilesystemType
|
||||
(enums.BlockVolumeType)(0), // 29: talos.resource.definitions.enums.BlockVolumeType
|
||||
(enums.BlockVolumePhase)(0), // 30: talos.resource.definitions.enums.BlockVolumePhase
|
||||
}
|
||||
var file_resource_definitions_block_block_proto_depIdxs = []int32{
|
||||
24, // 0: talos.resource.definitions.block.DiskSelector.match:type_name -> google.api.expr.v1alpha1.CheckedExpr
|
||||
25, // 1: talos.resource.definitions.block.EncryptionKey.type:type_name -> talos.resource.definitions.enums.BlockEncryptionKeyType
|
||||
26, // 2: talos.resource.definitions.block.EncryptionSpec.provider:type_name -> talos.resource.definitions.enums.BlockEncryptionProviderType
|
||||
25, // 0: talos.resource.definitions.block.DiskSelector.match:type_name -> google.api.expr.v1alpha1.CheckedExpr
|
||||
26, // 1: talos.resource.definitions.block.EncryptionKey.type:type_name -> talos.resource.definitions.enums.BlockEncryptionKeyType
|
||||
27, // 2: talos.resource.definitions.block.EncryptionSpec.provider:type_name -> talos.resource.definitions.enums.BlockEncryptionProviderType
|
||||
6, // 3: talos.resource.definitions.block.EncryptionSpec.keys:type_name -> talos.resource.definitions.block.EncryptionKey
|
||||
27, // 4: talos.resource.definitions.block.FilesystemSpec.type:type_name -> talos.resource.definitions.enums.BlockFilesystemType
|
||||
24, // 5: talos.resource.definitions.block.LocatorSpec.match:type_name -> google.api.expr.v1alpha1.CheckedExpr
|
||||
28, // 4: talos.resource.definitions.block.FilesystemSpec.type:type_name -> talos.resource.definitions.enums.BlockFilesystemType
|
||||
25, // 5: talos.resource.definitions.block.LocatorSpec.match:type_name -> google.api.expr.v1alpha1.CheckedExpr
|
||||
10, // 6: talos.resource.definitions.block.MountStatusSpec.spec:type_name -> talos.resource.definitions.block.MountRequestSpec
|
||||
27, // 7: talos.resource.definitions.block.MountStatusSpec.filesystem:type_name -> talos.resource.definitions.enums.BlockFilesystemType
|
||||
26, // 8: talos.resource.definitions.block.MountStatusSpec.encryption_provider:type_name -> talos.resource.definitions.enums.BlockEncryptionProviderType
|
||||
28, // 7: talos.resource.definitions.block.MountStatusSpec.filesystem:type_name -> talos.resource.definitions.enums.BlockFilesystemType
|
||||
27, // 8: talos.resource.definitions.block.MountStatusSpec.encryption_provider:type_name -> talos.resource.definitions.enums.BlockEncryptionProviderType
|
||||
4, // 9: talos.resource.definitions.block.ProvisioningSpec.disk_selector:type_name -> talos.resource.definitions.block.DiskSelector
|
||||
13, // 10: talos.resource.definitions.block.ProvisioningSpec.partition_spec:type_name -> talos.resource.definitions.block.PartitionSpec
|
||||
8, // 11: talos.resource.definitions.block.ProvisioningSpec.filesystem_spec:type_name -> talos.resource.definitions.block.FilesystemSpec
|
||||
28, // 12: talos.resource.definitions.block.VolumeConfigSpec.type:type_name -> talos.resource.definitions.enums.BlockVolumeType
|
||||
29, // 12: talos.resource.definitions.block.VolumeConfigSpec.type:type_name -> talos.resource.definitions.enums.BlockVolumeType
|
||||
14, // 13: talos.resource.definitions.block.VolumeConfigSpec.provisioning:type_name -> talos.resource.definitions.block.ProvisioningSpec
|
||||
9, // 14: talos.resource.definitions.block.VolumeConfigSpec.locator:type_name -> talos.resource.definitions.block.LocatorSpec
|
||||
11, // 15: talos.resource.definitions.block.VolumeConfigSpec.mount:type_name -> talos.resource.definitions.block.MountSpec
|
||||
7, // 16: talos.resource.definitions.block.VolumeConfigSpec.encryption:type_name -> talos.resource.definitions.block.EncryptionSpec
|
||||
16, // 17: talos.resource.definitions.block.VolumeConfigSpec.symlink:type_name -> talos.resource.definitions.block.SymlinkProvisioningSpec
|
||||
29, // 18: talos.resource.definitions.block.VolumeStatusSpec.phase:type_name -> talos.resource.definitions.enums.BlockVolumePhase
|
||||
29, // 19: talos.resource.definitions.block.VolumeStatusSpec.pre_fail_phase:type_name -> talos.resource.definitions.enums.BlockVolumePhase
|
||||
27, // 20: talos.resource.definitions.block.VolumeStatusSpec.filesystem:type_name -> talos.resource.definitions.enums.BlockFilesystemType
|
||||
26, // 21: talos.resource.definitions.block.VolumeStatusSpec.encryption_provider:type_name -> talos.resource.definitions.enums.BlockEncryptionProviderType
|
||||
30, // 18: talos.resource.definitions.block.VolumeStatusSpec.phase:type_name -> talos.resource.definitions.enums.BlockVolumePhase
|
||||
30, // 19: talos.resource.definitions.block.VolumeStatusSpec.pre_fail_phase:type_name -> talos.resource.definitions.enums.BlockVolumePhase
|
||||
28, // 20: talos.resource.definitions.block.VolumeStatusSpec.filesystem:type_name -> talos.resource.definitions.enums.BlockFilesystemType
|
||||
27, // 21: talos.resource.definitions.block.VolumeStatusSpec.encryption_provider:type_name -> talos.resource.definitions.enums.BlockEncryptionProviderType
|
||||
11, // 22: talos.resource.definitions.block.VolumeStatusSpec.mount_spec:type_name -> talos.resource.definitions.block.MountSpec
|
||||
28, // 23: talos.resource.definitions.block.VolumeStatusSpec.type:type_name -> talos.resource.definitions.enums.BlockVolumeType
|
||||
29, // 23: talos.resource.definitions.block.VolumeStatusSpec.type:type_name -> talos.resource.definitions.enums.BlockVolumeType
|
||||
16, // 24: talos.resource.definitions.block.VolumeStatusSpec.symlink_spec:type_name -> talos.resource.definitions.block.SymlinkProvisioningSpec
|
||||
25, // [25:25] is the sub-list for method output_type
|
||||
25, // [25:25] is the sub-list for method input_type
|
||||
@ -2317,7 +2447,7 @@ func file_resource_definitions_block_block_proto_init() {
|
||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||
RawDescriptor: unsafe.Slice(unsafe.StringData(file_resource_definitions_block_block_proto_rawDesc), len(file_resource_definitions_block_block_proto_rawDesc)),
|
||||
NumEnums: 0,
|
||||
NumMessages: 24,
|
||||
NumMessages: 25,
|
||||
NumExtensions: 0,
|
||||
NumServices: 0,
|
||||
},
|
||||
|
||||
@ -1859,6 +1859,91 @@ func (m *VolumeStatusSpec) MarshalToSizedBufferVT(dAtA []byte) (int, error) {
|
||||
return len(dAtA) - i, nil
|
||||
}
|
||||
|
||||
func (m *ZswapStatusSpec) MarshalVT() (dAtA []byte, err error) {
|
||||
if m == nil {
|
||||
return nil, nil
|
||||
}
|
||||
size := m.SizeVT()
|
||||
dAtA = make([]byte, size)
|
||||
n, err := m.MarshalToSizedBufferVT(dAtA[:size])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return dAtA[:n], nil
|
||||
}
|
||||
|
||||
func (m *ZswapStatusSpec) MarshalToVT(dAtA []byte) (int, error) {
|
||||
size := m.SizeVT()
|
||||
return m.MarshalToSizedBufferVT(dAtA[:size])
|
||||
}
|
||||
|
||||
func (m *ZswapStatusSpec) MarshalToSizedBufferVT(dAtA []byte) (int, error) {
|
||||
if m == nil {
|
||||
return 0, nil
|
||||
}
|
||||
i := len(dAtA)
|
||||
_ = i
|
||||
var l int
|
||||
_ = l
|
||||
if m.unknownFields != nil {
|
||||
i -= len(m.unknownFields)
|
||||
copy(dAtA[i:], m.unknownFields)
|
||||
}
|
||||
if m.WrittenBackPages != 0 {
|
||||
i = protohelpers.EncodeVarint(dAtA, i, uint64(m.WrittenBackPages))
|
||||
i--
|
||||
dAtA[i] = 0x50
|
||||
}
|
||||
if m.RejectCompressPoor != 0 {
|
||||
i = protohelpers.EncodeVarint(dAtA, i, uint64(m.RejectCompressPoor))
|
||||
i--
|
||||
dAtA[i] = 0x48
|
||||
}
|
||||
if m.RejectCompressFail != 0 {
|
||||
i = protohelpers.EncodeVarint(dAtA, i, uint64(m.RejectCompressFail))
|
||||
i--
|
||||
dAtA[i] = 0x40
|
||||
}
|
||||
if m.RejectKmemcacheFail != 0 {
|
||||
i = protohelpers.EncodeVarint(dAtA, i, uint64(m.RejectKmemcacheFail))
|
||||
i--
|
||||
dAtA[i] = 0x38
|
||||
}
|
||||
if m.RejectAllocFail != 0 {
|
||||
i = protohelpers.EncodeVarint(dAtA, i, uint64(m.RejectAllocFail))
|
||||
i--
|
||||
dAtA[i] = 0x30
|
||||
}
|
||||
if m.RejectReclaimFail != 0 {
|
||||
i = protohelpers.EncodeVarint(dAtA, i, uint64(m.RejectReclaimFail))
|
||||
i--
|
||||
dAtA[i] = 0x28
|
||||
}
|
||||
if m.PoolLimitHit != 0 {
|
||||
i = protohelpers.EncodeVarint(dAtA, i, uint64(m.PoolLimitHit))
|
||||
i--
|
||||
dAtA[i] = 0x20
|
||||
}
|
||||
if m.StoredPages != 0 {
|
||||
i = protohelpers.EncodeVarint(dAtA, i, uint64(m.StoredPages))
|
||||
i--
|
||||
dAtA[i] = 0x18
|
||||
}
|
||||
if len(m.TotalSizeHuman) > 0 {
|
||||
i -= len(m.TotalSizeHuman)
|
||||
copy(dAtA[i:], m.TotalSizeHuman)
|
||||
i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.TotalSizeHuman)))
|
||||
i--
|
||||
dAtA[i] = 0x12
|
||||
}
|
||||
if m.TotalSizeBytes != 0 {
|
||||
i = protohelpers.EncodeVarint(dAtA, i, uint64(m.TotalSizeBytes))
|
||||
i--
|
||||
dAtA[i] = 0x8
|
||||
}
|
||||
return len(dAtA) - i, nil
|
||||
}
|
||||
|
||||
func (m *DeviceSpec) SizeVT() (n int) {
|
||||
if m == nil {
|
||||
return 0
|
||||
@ -2631,6 +2716,47 @@ func (m *VolumeStatusSpec) SizeVT() (n int) {
|
||||
return n
|
||||
}
|
||||
|
||||
func (m *ZswapStatusSpec) SizeVT() (n int) {
|
||||
if m == nil {
|
||||
return 0
|
||||
}
|
||||
var l int
|
||||
_ = l
|
||||
if m.TotalSizeBytes != 0 {
|
||||
n += 1 + protohelpers.SizeOfVarint(uint64(m.TotalSizeBytes))
|
||||
}
|
||||
l = len(m.TotalSizeHuman)
|
||||
if l > 0 {
|
||||
n += 1 + l + protohelpers.SizeOfVarint(uint64(l))
|
||||
}
|
||||
if m.StoredPages != 0 {
|
||||
n += 1 + protohelpers.SizeOfVarint(uint64(m.StoredPages))
|
||||
}
|
||||
if m.PoolLimitHit != 0 {
|
||||
n += 1 + protohelpers.SizeOfVarint(uint64(m.PoolLimitHit))
|
||||
}
|
||||
if m.RejectReclaimFail != 0 {
|
||||
n += 1 + protohelpers.SizeOfVarint(uint64(m.RejectReclaimFail))
|
||||
}
|
||||
if m.RejectAllocFail != 0 {
|
||||
n += 1 + protohelpers.SizeOfVarint(uint64(m.RejectAllocFail))
|
||||
}
|
||||
if m.RejectKmemcacheFail != 0 {
|
||||
n += 1 + protohelpers.SizeOfVarint(uint64(m.RejectKmemcacheFail))
|
||||
}
|
||||
if m.RejectCompressFail != 0 {
|
||||
n += 1 + protohelpers.SizeOfVarint(uint64(m.RejectCompressFail))
|
||||
}
|
||||
if m.RejectCompressPoor != 0 {
|
||||
n += 1 + protohelpers.SizeOfVarint(uint64(m.RejectCompressPoor))
|
||||
}
|
||||
if m.WrittenBackPages != 0 {
|
||||
n += 1 + protohelpers.SizeOfVarint(uint64(m.WrittenBackPages))
|
||||
}
|
||||
n += len(m.unknownFields)
|
||||
return n
|
||||
}
|
||||
|
||||
func (m *DeviceSpec) UnmarshalVT(dAtA []byte) error {
|
||||
l := len(dAtA)
|
||||
iNdEx := 0
|
||||
@ -7662,3 +7788,257 @@ func (m *VolumeStatusSpec) UnmarshalVT(dAtA []byte) error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
func (m *ZswapStatusSpec) UnmarshalVT(dAtA []byte) error {
|
||||
l := len(dAtA)
|
||||
iNdEx := 0
|
||||
for iNdEx < l {
|
||||
preIndex := iNdEx
|
||||
var wire uint64
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return protohelpers.ErrIntOverflow
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
wire |= uint64(b&0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
fieldNum := int32(wire >> 3)
|
||||
wireType := int(wire & 0x7)
|
||||
if wireType == 4 {
|
||||
return fmt.Errorf("proto: ZswapStatusSpec: wiretype end group for non-group")
|
||||
}
|
||||
if fieldNum <= 0 {
|
||||
return fmt.Errorf("proto: ZswapStatusSpec: illegal tag %d (wire type %d)", fieldNum, wire)
|
||||
}
|
||||
switch fieldNum {
|
||||
case 1:
|
||||
if wireType != 0 {
|
||||
return fmt.Errorf("proto: wrong wireType = %d for field TotalSizeBytes", wireType)
|
||||
}
|
||||
m.TotalSizeBytes = 0
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return protohelpers.ErrIntOverflow
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
m.TotalSizeBytes |= uint64(b&0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
case 2:
|
||||
if wireType != 2 {
|
||||
return fmt.Errorf("proto: wrong wireType = %d for field TotalSizeHuman", wireType)
|
||||
}
|
||||
var stringLen uint64
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return protohelpers.ErrIntOverflow
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
stringLen |= uint64(b&0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
intStringLen := int(stringLen)
|
||||
if intStringLen < 0 {
|
||||
return protohelpers.ErrInvalidLength
|
||||
}
|
||||
postIndex := iNdEx + intStringLen
|
||||
if postIndex < 0 {
|
||||
return protohelpers.ErrInvalidLength
|
||||
}
|
||||
if postIndex > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
m.TotalSizeHuman = string(dAtA[iNdEx:postIndex])
|
||||
iNdEx = postIndex
|
||||
case 3:
|
||||
if wireType != 0 {
|
||||
return fmt.Errorf("proto: wrong wireType = %d for field StoredPages", wireType)
|
||||
}
|
||||
m.StoredPages = 0
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return protohelpers.ErrIntOverflow
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
m.StoredPages |= uint64(b&0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
case 4:
|
||||
if wireType != 0 {
|
||||
return fmt.Errorf("proto: wrong wireType = %d for field PoolLimitHit", wireType)
|
||||
}
|
||||
m.PoolLimitHit = 0
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return protohelpers.ErrIntOverflow
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
m.PoolLimitHit |= uint64(b&0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
case 5:
|
||||
if wireType != 0 {
|
||||
return fmt.Errorf("proto: wrong wireType = %d for field RejectReclaimFail", wireType)
|
||||
}
|
||||
m.RejectReclaimFail = 0
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return protohelpers.ErrIntOverflow
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
m.RejectReclaimFail |= uint64(b&0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
case 6:
|
||||
if wireType != 0 {
|
||||
return fmt.Errorf("proto: wrong wireType = %d for field RejectAllocFail", wireType)
|
||||
}
|
||||
m.RejectAllocFail = 0
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return protohelpers.ErrIntOverflow
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
m.RejectAllocFail |= uint64(b&0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
case 7:
|
||||
if wireType != 0 {
|
||||
return fmt.Errorf("proto: wrong wireType = %d for field RejectKmemcacheFail", wireType)
|
||||
}
|
||||
m.RejectKmemcacheFail = 0
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return protohelpers.ErrIntOverflow
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
m.RejectKmemcacheFail |= uint64(b&0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
case 8:
|
||||
if wireType != 0 {
|
||||
return fmt.Errorf("proto: wrong wireType = %d for field RejectCompressFail", wireType)
|
||||
}
|
||||
m.RejectCompressFail = 0
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return protohelpers.ErrIntOverflow
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
m.RejectCompressFail |= uint64(b&0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
case 9:
|
||||
if wireType != 0 {
|
||||
return fmt.Errorf("proto: wrong wireType = %d for field RejectCompressPoor", wireType)
|
||||
}
|
||||
m.RejectCompressPoor = 0
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return protohelpers.ErrIntOverflow
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
m.RejectCompressPoor |= uint64(b&0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
case 10:
|
||||
if wireType != 0 {
|
||||
return fmt.Errorf("proto: wrong wireType = %d for field WrittenBackPages", wireType)
|
||||
}
|
||||
m.WrittenBackPages = 0
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return protohelpers.ErrIntOverflow
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
m.WrittenBackPages |= uint64(b&0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
default:
|
||||
iNdEx = preIndex
|
||||
skippy, err := protohelpers.Skip(dAtA[iNdEx:])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if (skippy < 0) || (iNdEx+skippy) < 0 {
|
||||
return protohelpers.ErrInvalidLength
|
||||
}
|
||||
if (iNdEx + skippy) > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...)
|
||||
iNdEx += skippy
|
||||
}
|
||||
}
|
||||
|
||||
if iNdEx > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -21,4 +21,5 @@ type Config interface { //nolint:interfacebloat
|
||||
EthernetConfigs() []EthernetConfig
|
||||
UserVolumeConfigs() []UserVolumeConfig
|
||||
SwapVolumeConfigs() []SwapVolumeConfig
|
||||
ZswapConfig() ZswapConfig
|
||||
}
|
||||
|
||||
@ -98,3 +98,10 @@ type SwapVolumeConfig interface {
|
||||
Provisioning() VolumeProvisioningConfig
|
||||
Encryption() EncryptionConfig
|
||||
}
|
||||
|
||||
// ZswapConfig defines the interface to access zswap configuration.
|
||||
type ZswapConfig interface {
|
||||
ZswapConfigSignal()
|
||||
MaxPoolPercent() int
|
||||
ShrinkerEnabled() bool
|
||||
}
|
||||
|
||||
@ -230,6 +230,16 @@ func (container *Container) SwapVolumeConfigs() []config.SwapVolumeConfig {
|
||||
return findMatchingDocs[config.SwapVolumeConfig](container.documents)
|
||||
}
|
||||
|
||||
// ZswapConfig implements config.Config interface.
|
||||
func (container *Container) ZswapConfig() config.ZswapConfig {
|
||||
matching := findMatchingDocs[config.ZswapConfig](container.documents)
|
||||
if len(matching) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
return matching[0]
|
||||
}
|
||||
|
||||
// Bytes returns source YAML representation (if available) or does default encoding.
|
||||
func (container *Container) Bytes() ([]byte, error) {
|
||||
if !container.readonly {
|
||||
|
||||
@ -370,6 +370,49 @@
|
||||
],
|
||||
"description": "VolumeConfig is a system volume configuration document.\\nNote: at the moment, only `EPHEMERAL` and `IMAGE-CACHE` system volumes are supported.\\n"
|
||||
},
|
||||
"block.ZswapConfigV1Alpha1": {
|
||||
"properties": {
|
||||
"apiVersion": {
|
||||
"enum": [
|
||||
"v1alpha1"
|
||||
],
|
||||
"title": "apiVersion",
|
||||
"description": "apiVersion is the API version of the resource.\n",
|
||||
"markdownDescription": "apiVersion is the API version of the resource.",
|
||||
"x-intellij-html-description": "\u003cp\u003eapiVersion is the API version of the resource.\u003c/p\u003e\n"
|
||||
},
|
||||
"kind": {
|
||||
"enum": [
|
||||
"ZswapConfig"
|
||||
],
|
||||
"title": "kind",
|
||||
"description": "kind is the kind of the resource.\n",
|
||||
"markdownDescription": "kind is the kind of the resource.",
|
||||
"x-intellij-html-description": "\u003cp\u003ekind is the kind of the resource.\u003c/p\u003e\n"
|
||||
},
|
||||
"maxPoolPercent": {
|
||||
"type": "integer",
|
||||
"title": "maxPoolPercent",
|
||||
"description": "The maximum percent of memory that zswap can use.\nThis is a percentage of the total system memory.\nThe value must be between 0 and 100.\n",
|
||||
"markdownDescription": "The maximum percent of memory that zswap can use.\nThis is a percentage of the total system memory.\nThe value must be between 0 and 100.",
|
||||
"x-intellij-html-description": "\u003cp\u003eThe maximum percent of memory that zswap can use.\nThis is a percentage of the total system memory.\nThe value must be between 0 and 100.\u003c/p\u003e\n"
|
||||
},
|
||||
"shrinkerEnabled": {
|
||||
"type": "boolean",
|
||||
"title": "shrinkerEnabled",
|
||||
"description": "Enable the shrinker feature: kernel might move\ncold pages from zswap to swap device to free up memory\nfor other use cases.\n",
|
||||
"markdownDescription": "Enable the shrinker feature: kernel might move\ncold pages from zswap to swap device to free up memory\nfor other use cases.",
|
||||
"x-intellij-html-description": "\u003cp\u003eEnable the shrinker feature: kernel might move\ncold pages from zswap to swap device to free up memory\nfor other use cases.\u003c/p\u003e\n"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"type": "object",
|
||||
"required": [
|
||||
"apiVersion",
|
||||
"kind"
|
||||
],
|
||||
"description": "ZswapConfig is a zswap (compressed memory) configuration document.\\nWhen zswap is enabled, Linux kernel compresses pages that would otherwise be swapped out to disk.\\nThe compressed pages are stored in a memory pool, which is used to avoid writing to disk\\nwhen the system is under memory pressure.\\n"
|
||||
},
|
||||
"extensions.ConfigFile": {
|
||||
"properties": {
|
||||
"content": {
|
||||
@ -4242,6 +4285,9 @@
|
||||
{
|
||||
"$ref": "#/$defs/block.VolumeConfigV1Alpha1"
|
||||
},
|
||||
{
|
||||
"$ref": "#/$defs/block.ZswapConfigV1Alpha1"
|
||||
},
|
||||
{
|
||||
"$ref": "#/$defs/extensions.ServiceConfigV1Alpha1"
|
||||
},
|
||||
|
||||
@ -5,6 +5,6 @@
|
||||
// Package block provides block device and volume configuration documents.
|
||||
package block
|
||||
|
||||
//go:generate docgen -output block_doc.go block.go encryption.go swap_volume_config.go user_volume_config.go volume_config.go
|
||||
//go:generate docgen -output block_doc.go block.go encryption.go swap_volume_config.go user_volume_config.go volume_config.go zswap_config.go
|
||||
|
||||
//go:generate deep-copy -type SwapVolumeConfigV1Alpha1 -type UserVolumeConfigV1Alpha1 -type VolumeConfigV1Alpha1 -pointer-receiver -header-file ../../../../../hack/boilerplate.txt -o deep_copy.generated.go .
|
||||
//go:generate deep-copy -type SwapVolumeConfigV1Alpha1 -type UserVolumeConfigV1Alpha1 -type VolumeConfigV1Alpha1 -type ZswapConfigV1Alpha1 -pointer-receiver -header-file ../../../../../hack/boilerplate.txt -o deep_copy.generated.go .
|
||||
|
||||
@ -462,6 +462,35 @@ func (DiskSelector) Doc() *encoder.Doc {
|
||||
return doc
|
||||
}
|
||||
|
||||
func (ZswapConfigV1Alpha1) Doc() *encoder.Doc {
|
||||
doc := &encoder.Doc{
|
||||
Type: "ZswapConfig",
|
||||
Comments: [3]string{"" /* encoder.HeadComment */, "ZswapConfig is a zswap (compressed memory) configuration document." /* encoder.LineComment */, "" /* encoder.FootComment */},
|
||||
Description: "ZswapConfig is a zswap (compressed memory) configuration document.\nWhen zswap is enabled, Linux kernel compresses pages that would otherwise be swapped out to disk.\nThe compressed pages are stored in a memory pool, which is used to avoid writing to disk\nwhen the system is under memory pressure.\n",
|
||||
Fields: []encoder.Doc{
|
||||
{},
|
||||
{
|
||||
Name: "maxPoolPercent",
|
||||
Type: "int",
|
||||
Note: "",
|
||||
Description: "The maximum percent of memory that zswap can use.\nThis is a percentage of the total system memory.\nThe value must be between 0 and 100.",
|
||||
Comments: [3]string{"" /* encoder.HeadComment */, "The maximum percent of memory that zswap can use." /* encoder.LineComment */, "" /* encoder.FootComment */},
|
||||
},
|
||||
{
|
||||
Name: "shrinkerEnabled",
|
||||
Type: "bool",
|
||||
Note: "",
|
||||
Description: "Enable the shrinker feature: kernel might move\ncold pages from zswap to swap device to free up memory\nfor other use cases.",
|
||||
Comments: [3]string{"" /* encoder.HeadComment */, "Enable the shrinker feature: kernel might move" /* encoder.LineComment */, "" /* encoder.FootComment */},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
doc.AddExample("", exampleZswapConfigV1Alpha1())
|
||||
|
||||
return doc
|
||||
}
|
||||
|
||||
// GetFileDoc returns documentation for the file block_doc.go.
|
||||
func GetFileDoc() *encoder.FileDoc {
|
||||
return &encoder.FileDoc{
|
||||
@ -480,6 +509,7 @@ func GetFileDoc() *encoder.FileDoc {
|
||||
VolumeConfigV1Alpha1{}.Doc(),
|
||||
ProvisioningSpec{}.Doc(),
|
||||
DiskSelector{}.Doc(),
|
||||
ZswapConfigV1Alpha1{}.Doc(),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
// Code generated by "deep-copy -type SwapVolumeConfigV1Alpha1 -type UserVolumeConfigV1Alpha1 -type VolumeConfigV1Alpha1 -pointer-receiver -header-file ../../../../../hack/boilerplate.txt -o deep_copy.generated.go ."; DO NOT EDIT.
|
||||
// Code generated by "deep-copy -type SwapVolumeConfigV1Alpha1 -type UserVolumeConfigV1Alpha1 -type VolumeConfigV1Alpha1 -type ZswapConfigV1Alpha1 -pointer-receiver -header-file ../../../../../hack/boilerplate.txt -o deep_copy.generated.go ."; DO NOT EDIT.
|
||||
|
||||
package block
|
||||
|
||||
@ -143,3 +143,17 @@ func (o *VolumeConfigV1Alpha1) DeepCopy() *VolumeConfigV1Alpha1 {
|
||||
}
|
||||
return &cp
|
||||
}
|
||||
|
||||
// DeepCopy generates a deep copy of *ZswapConfigV1Alpha1.
|
||||
func (o *ZswapConfigV1Alpha1) DeepCopy() *ZswapConfigV1Alpha1 {
|
||||
var cp ZswapConfigV1Alpha1 = *o
|
||||
if o.MaxPoolPercentConfig != nil {
|
||||
cp.MaxPoolPercentConfig = new(int)
|
||||
*cp.MaxPoolPercentConfig = *o.MaxPoolPercentConfig
|
||||
}
|
||||
if o.ShrinkerEnabledConfig != nil {
|
||||
cp.ShrinkerEnabledConfig = new(bool)
|
||||
*cp.ShrinkerEnabledConfig = *o.ShrinkerEnabledConfig
|
||||
}
|
||||
return &cp
|
||||
}
|
||||
|
||||
4
pkg/machinery/config/types/block/testdata/zswapconfig_full.yaml
vendored
Normal file
4
pkg/machinery/config/types/block/testdata/zswapconfig_full.yaml
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
apiVersion: v1alpha1
|
||||
kind: ZswapConfig
|
||||
maxPoolPercent: 50
|
||||
shrinkerEnabled: true
|
||||
2
pkg/machinery/config/types/block/testdata/zswapconfig_min.yaml
vendored
Normal file
2
pkg/machinery/config/types/block/testdata/zswapconfig_min.yaml
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
apiVersion: v1alpha1
|
||||
kind: ZswapConfig
|
||||
127
pkg/machinery/config/types/block/zswap_config.go
Normal file
127
pkg/machinery/config/types/block/zswap_config.go
Normal file
@ -0,0 +1,127 @@
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
package block
|
||||
|
||||
//docgen:jsonschema
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/siderolabs/go-pointer"
|
||||
|
||||
"github.com/siderolabs/talos/pkg/machinery/config/config"
|
||||
"github.com/siderolabs/talos/pkg/machinery/config/internal/registry"
|
||||
"github.com/siderolabs/talos/pkg/machinery/config/types/meta"
|
||||
"github.com/siderolabs/talos/pkg/machinery/config/validation"
|
||||
)
|
||||
|
||||
// ZswapConfigKind is a config document kind.
|
||||
const ZswapConfigKind = "ZswapConfig"
|
||||
|
||||
func init() {
|
||||
registry.Register(ZswapConfigKind, func(version string) config.Document {
|
||||
switch version {
|
||||
case "v1alpha1": //nolint:goconst
|
||||
return &ZswapConfigV1Alpha1{}
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// Check interfaces.
|
||||
var (
|
||||
_ config.ZswapConfig = &ZswapConfigV1Alpha1{}
|
||||
_ config.Validator = &ZswapConfigV1Alpha1{}
|
||||
)
|
||||
|
||||
// ZswapConfigV1Alpha1 is a zswap (compressed memory) configuration document.
|
||||
//
|
||||
// description: |
|
||||
// When zswap is enabled, Linux kernel compresses pages that would otherwise be swapped out to disk.
|
||||
// The compressed pages are stored in a memory pool, which is used to avoid writing to disk
|
||||
// when the system is under memory pressure.
|
||||
// examples:
|
||||
// - value: exampleZswapConfigV1Alpha1()
|
||||
// alias: ZswapConfig
|
||||
// schemaRoot: true
|
||||
// schemaMeta: v1alpha1/ZswapConfig
|
||||
type ZswapConfigV1Alpha1 struct {
|
||||
meta.Meta `yaml:",inline"`
|
||||
|
||||
// description: |
|
||||
// The maximum percent of memory that zswap can use.
|
||||
// This is a percentage of the total system memory.
|
||||
// The value must be between 0 and 100.
|
||||
MaxPoolPercentConfig *int `yaml:"maxPoolPercent,omitempty"`
|
||||
// description: |
|
||||
// Enable the shrinker feature: kernel might move
|
||||
// cold pages from zswap to swap device to free up memory
|
||||
// for other use cases.
|
||||
ShrinkerEnabledConfig *bool `yaml:"shrinkerEnabled,omitempty"`
|
||||
}
|
||||
|
||||
// NewZswapConfigV1Alpha1 creates a new zswap config document.
|
||||
func NewZswapConfigV1Alpha1() *ZswapConfigV1Alpha1 {
|
||||
return &ZswapConfigV1Alpha1{
|
||||
Meta: meta.Meta{
|
||||
MetaKind: ZswapConfigKind,
|
||||
MetaAPIVersion: "v1alpha1",
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func exampleZswapConfigV1Alpha1() *ZswapConfigV1Alpha1 {
|
||||
cfg := NewZswapConfigV1Alpha1()
|
||||
cfg.MaxPoolPercentConfig = pointer.To(25)
|
||||
cfg.ShrinkerEnabledConfig = pointer.To(true)
|
||||
|
||||
return cfg
|
||||
}
|
||||
|
||||
// Clone implements config.Document interface.
|
||||
func (s *ZswapConfigV1Alpha1) Clone() config.Document {
|
||||
return s.DeepCopy()
|
||||
}
|
||||
|
||||
// Validate implements config.Validator interface.
|
||||
//
|
||||
//nolint:gocyclo
|
||||
func (s *ZswapConfigV1Alpha1) Validate(validation.RuntimeMode, ...validation.Option) ([]string, error) {
|
||||
var (
|
||||
warnings []string
|
||||
validationErrors error
|
||||
)
|
||||
|
||||
if s.MaxPoolPercentConfig != nil {
|
||||
if *s.MaxPoolPercentConfig < 0 || *s.MaxPoolPercentConfig > 100 {
|
||||
validationErrors = errors.Join(validationErrors, fmt.Errorf("maxPoolPercent must be between 0 and 100"))
|
||||
}
|
||||
}
|
||||
|
||||
return warnings, validationErrors
|
||||
}
|
||||
|
||||
// ZswapConfigSignal is a signal for zswap config.
|
||||
func (s *ZswapConfigV1Alpha1) ZswapConfigSignal() {}
|
||||
|
||||
// MaxPoolPercent implements config.ZswapConfig interface.
|
||||
func (s *ZswapConfigV1Alpha1) MaxPoolPercent() int {
|
||||
if s.MaxPoolPercentConfig == nil {
|
||||
return 20
|
||||
}
|
||||
|
||||
return pointer.SafeDeref(s.MaxPoolPercentConfig)
|
||||
}
|
||||
|
||||
// ShrinkerEnabled implements config.ZswapConfig interface.
|
||||
func (s *ZswapConfigV1Alpha1) ShrinkerEnabled() bool {
|
||||
if s.ShrinkerEnabledConfig == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
return pointer.SafeDeref(s.ShrinkerEnabledConfig)
|
||||
}
|
||||
141
pkg/machinery/config/types/block/zswap_config_test.go
Normal file
141
pkg/machinery/config/types/block/zswap_config_test.go
Normal file
@ -0,0 +1,141 @@
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
//nolint:dupl,goconst
|
||||
package block_test
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/siderolabs/go-pointer"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/siderolabs/talos/pkg/machinery/config/configloader"
|
||||
"github.com/siderolabs/talos/pkg/machinery/config/encoder"
|
||||
"github.com/siderolabs/talos/pkg/machinery/config/types/block"
|
||||
)
|
||||
|
||||
func TestZswapConfigMarshalUnmarshal(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
for _, test := range []struct {
|
||||
name string
|
||||
|
||||
filename string
|
||||
cfg func(t *testing.T) *block.ZswapConfigV1Alpha1
|
||||
}{
|
||||
{
|
||||
name: "full config",
|
||||
filename: "zswapconfig_full.yaml",
|
||||
cfg: func(t *testing.T) *block.ZswapConfigV1Alpha1 {
|
||||
c := block.NewZswapConfigV1Alpha1()
|
||||
c.MaxPoolPercentConfig = pointer.To(50)
|
||||
c.ShrinkerEnabledConfig = pointer.To(true)
|
||||
|
||||
return c
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "min config",
|
||||
filename: "zswapconfig_min.yaml",
|
||||
cfg: func(t *testing.T) *block.ZswapConfigV1Alpha1 {
|
||||
c := block.NewZswapConfigV1Alpha1()
|
||||
|
||||
return c
|
||||
},
|
||||
},
|
||||
} {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
cfg := test.cfg(t)
|
||||
|
||||
warnings, err := cfg.Validate(validationMode{})
|
||||
require.NoError(t, err)
|
||||
require.Empty(t, warnings)
|
||||
|
||||
marshaled, err := encoder.NewEncoder(cfg, encoder.WithComments(encoder.CommentsDisabled)).Encode()
|
||||
require.NoError(t, err)
|
||||
|
||||
t.Log(string(marshaled))
|
||||
|
||||
expectedMarshaled, err := os.ReadFile(filepath.Join("testdata", test.filename))
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, string(expectedMarshaled), string(marshaled))
|
||||
|
||||
provider, err := configloader.NewFromBytes(expectedMarshaled)
|
||||
require.NoError(t, err)
|
||||
|
||||
docs := provider.Documents()
|
||||
require.Len(t, docs, 1)
|
||||
|
||||
assert.Equal(t, cfg, docs[0])
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestZswapVolumeConfigValidate(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
for _, test := range []struct {
|
||||
name string
|
||||
|
||||
cfg func(t *testing.T) *block.ZswapConfigV1Alpha1
|
||||
|
||||
expectedErrors string
|
||||
}{
|
||||
{
|
||||
name: "minimal",
|
||||
|
||||
cfg: func(t *testing.T) *block.ZswapConfigV1Alpha1 {
|
||||
c := block.NewZswapConfigV1Alpha1()
|
||||
|
||||
return c
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "full",
|
||||
|
||||
cfg: func(t *testing.T) *block.ZswapConfigV1Alpha1 {
|
||||
c := block.NewZswapConfigV1Alpha1()
|
||||
c.MaxPoolPercentConfig = pointer.To(50)
|
||||
c.ShrinkerEnabledConfig = pointer.To(true)
|
||||
|
||||
return c
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "invalid percent",
|
||||
|
||||
cfg: func(t *testing.T) *block.ZswapConfigV1Alpha1 {
|
||||
c := block.NewZswapConfigV1Alpha1()
|
||||
c.MaxPoolPercentConfig = pointer.To(150)
|
||||
|
||||
return c
|
||||
},
|
||||
|
||||
expectedErrors: "maxPoolPercent must be between 0 and 100",
|
||||
},
|
||||
} {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
cfg := test.cfg(t)
|
||||
|
||||
_, err := cfg.Validate(validationMode{})
|
||||
|
||||
if test.expectedErrors == "" {
|
||||
require.NoError(t, err)
|
||||
} else {
|
||||
require.Error(t, err)
|
||||
|
||||
assert.EqualError(t, err, test.expectedErrors)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -19,7 +19,7 @@ import (
|
||||
"github.com/siderolabs/talos/pkg/machinery/resources/v1alpha1"
|
||||
)
|
||||
|
||||
//go:generate deep-copy -type DeviceSpec -type DiscoveredVolumeSpec -type DiscoveryRefreshRequestSpec -type DiscoveryRefreshStatusSpec -type DiskSpec -type MountRequestSpec -type MountStatusSpec -type SwapStatusSpec -type SymlinkSpec -type SystemDiskSpec -type UserDiskConfigStatusSpec -type VolumeConfigSpec -type VolumeLifecycleSpec -type VolumeMountRequestSpec -type VolumeMountStatusSpec -type VolumeStatusSpec -header-file ../../../../hack/boilerplate.txt -o deep_copy.generated.go .
|
||||
//go:generate deep-copy -type DeviceSpec -type DiscoveredVolumeSpec -type DiscoveryRefreshRequestSpec -type DiscoveryRefreshStatusSpec -type DiskSpec -type MountRequestSpec -type MountStatusSpec -type SwapStatusSpec -type SymlinkSpec -type SystemDiskSpec -type UserDiskConfigStatusSpec -type VolumeConfigSpec -type VolumeLifecycleSpec -type VolumeMountRequestSpec -type VolumeMountStatusSpec -type VolumeStatusSpec -type ZswapStatusSpec -header-file ../../../../hack/boilerplate.txt -o deep_copy.generated.go .
|
||||
|
||||
//go:generate enumer -type=VolumeType,VolumePhase,FilesystemType,EncryptionKeyType,EncryptionProviderType -linecomment -text
|
||||
|
||||
|
||||
@ -40,6 +40,7 @@ func TestRegisterResource(t *testing.T) {
|
||||
&block.VolumeMountRequest{},
|
||||
&block.VolumeMountStatus{},
|
||||
&block.VolumeStatus{},
|
||||
&block.ZswapStatus{},
|
||||
} {
|
||||
assert.NoError(t, resourceRegistry.Register(ctx, resource))
|
||||
}
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
// Code generated by "deep-copy -type DeviceSpec -type DiscoveredVolumeSpec -type DiscoveryRefreshRequestSpec -type DiscoveryRefreshStatusSpec -type DiskSpec -type MountRequestSpec -type MountStatusSpec -type SwapStatusSpec -type SymlinkSpec -type SystemDiskSpec -type UserDiskConfigStatusSpec -type VolumeConfigSpec -type VolumeLifecycleSpec -type VolumeMountRequestSpec -type VolumeMountStatusSpec -type VolumeStatusSpec -header-file ../../../../hack/boilerplate.txt -o deep_copy.generated.go ."; DO NOT EDIT.
|
||||
// Code generated by "deep-copy -type DeviceSpec -type DiscoveredVolumeSpec -type DiscoveryRefreshRequestSpec -type DiscoveryRefreshStatusSpec -type DiskSpec -type MountRequestSpec -type MountStatusSpec -type SwapStatusSpec -type SymlinkSpec -type SystemDiskSpec -type UserDiskConfigStatusSpec -type VolumeConfigSpec -type VolumeLifecycleSpec -type VolumeMountRequestSpec -type VolumeMountStatusSpec -type VolumeStatusSpec -type ZswapStatusSpec -header-file ../../../../hack/boilerplate.txt -o deep_copy.generated.go ."; DO NOT EDIT.
|
||||
|
||||
package block
|
||||
|
||||
@ -148,3 +148,9 @@ func (o VolumeStatusSpec) DeepCopy() VolumeStatusSpec {
|
||||
}
|
||||
return cp
|
||||
}
|
||||
|
||||
// DeepCopy generates a deep copy of ZswapStatusSpec.
|
||||
func (o ZswapStatusSpec) DeepCopy() ZswapStatusSpec {
|
||||
var cp ZswapStatusSpec = o
|
||||
return cp
|
||||
}
|
||||
|
||||
86
pkg/machinery/resources/block/zswap_status.go
Normal file
86
pkg/machinery/resources/block/zswap_status.go
Normal file
@ -0,0 +1,86 @@
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
package block
|
||||
|
||||
import (
|
||||
"github.com/cosi-project/runtime/pkg/resource"
|
||||
"github.com/cosi-project/runtime/pkg/resource/meta"
|
||||
"github.com/cosi-project/runtime/pkg/resource/protobuf"
|
||||
"github.com/cosi-project/runtime/pkg/resource/typed"
|
||||
|
||||
"github.com/siderolabs/talos/pkg/machinery/proto"
|
||||
)
|
||||
|
||||
// ZswapStatusType is type of ZswapStatus resource.
|
||||
const ZswapStatusType = resource.Type("ZswapStatuses.block.talos.dev")
|
||||
|
||||
// ZswapStatus resource holds status of zwap subsystem.
|
||||
type ZswapStatus = typed.Resource[ZswapStatusSpec, ZswapStatusExtension]
|
||||
|
||||
// ZswapStatusID is the ID of the singleton ZswapStatus resource.
|
||||
const ZswapStatusID resource.ID = "zswap"
|
||||
|
||||
// ZswapStatusSpec is the spec for ZswapStatus resource.
|
||||
//
|
||||
//gotagsrewrite:gen
|
||||
type ZswapStatusSpec struct {
|
||||
TotalSizeBytes uint64 `yaml:"totalSize" protobuf:"1"`
|
||||
TotalSizeHuman string `yaml:"totalSizeHuman" protobuf:"2"`
|
||||
StoredPages uint64 `yaml:"storedPages" protobuf:"3"`
|
||||
PoolLimitHit uint64 `yaml:"poolLimitHit" protobuf:"4"`
|
||||
RejectReclaimFail uint64 `yaml:"rejectReclaimFail" protobuf:"5"`
|
||||
RejectAllocFail uint64 `yaml:"rejectAllocFail" protobuf:"6"`
|
||||
RejectKmemcacheFail uint64 `yaml:"rejectKmemcacheFail" protobuf:"7"`
|
||||
RejectCompressFail uint64 `yaml:"rejectCompressFail" protobuf:"8"`
|
||||
RejectCompressPoor uint64 `yaml:"rejectCompressPoor" protobuf:"9"`
|
||||
WrittenBackPages uint64 `yaml:"writtenBackPages" protobuf:"10"`
|
||||
}
|
||||
|
||||
// NewZswapStatus initializes a ZswapStatus resource.
|
||||
func NewZswapStatus(namespace resource.Namespace, id resource.ID) *ZswapStatus {
|
||||
return typed.NewResource[ZswapStatusSpec, ZswapStatusExtension](
|
||||
resource.NewMetadata(namespace, ZswapStatusType, id, resource.VersionUndefined),
|
||||
ZswapStatusSpec{},
|
||||
)
|
||||
}
|
||||
|
||||
// ZswapStatusExtension is auxiliary resource data for ZswapStatus.
|
||||
type ZswapStatusExtension struct{}
|
||||
|
||||
// ResourceDefinition implements meta.ResourceDefinitionProvider interface.
|
||||
func (ZswapStatusExtension) ResourceDefinition() meta.ResourceDefinitionSpec {
|
||||
return meta.ResourceDefinitionSpec{
|
||||
Type: ZswapStatusType,
|
||||
Aliases: []resource.Type{},
|
||||
DefaultNamespace: NamespaceName,
|
||||
PrintColumns: []meta.PrintColumn{
|
||||
{
|
||||
Name: "Total Size",
|
||||
JSONPath: "{.totalSizeHuman}",
|
||||
},
|
||||
{
|
||||
Name: "Stored Pages",
|
||||
JSONPath: "{.storedPages}",
|
||||
},
|
||||
{
|
||||
Name: "Written Back",
|
||||
JSONPath: "{.writtenBackPages}",
|
||||
},
|
||||
{
|
||||
Name: "Pool Limit Hit",
|
||||
JSONPath: "{.poolLimitHit}",
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func init() {
|
||||
proto.RegisterDefaultTypes()
|
||||
|
||||
err := protobuf.RegisterDynamic[ZswapStatusSpec](ZswapStatusType, &ZswapStatus{})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
@ -51,6 +51,7 @@ description: Talos gRPC API reference.
|
||||
- [VolumeMountRequestSpec](#talos.resource.definitions.block.VolumeMountRequestSpec)
|
||||
- [VolumeMountStatusSpec](#talos.resource.definitions.block.VolumeMountStatusSpec)
|
||||
- [VolumeStatusSpec](#talos.resource.definitions.block.VolumeStatusSpec)
|
||||
- [ZswapStatusSpec](#talos.resource.definitions.block.ZswapStatusSpec)
|
||||
|
||||
- [resource/definitions/cluster/cluster.proto](#resource/definitions/cluster/cluster.proto)
|
||||
- [AffiliateSpec](#talos.resource.definitions.cluster.AffiliateSpec)
|
||||
@ -1302,6 +1303,30 @@ VolumeStatusSpec is the spec for VolumeStatus resource.
|
||||
|
||||
|
||||
|
||||
|
||||
<a name="talos.resource.definitions.block.ZswapStatusSpec"></a>
|
||||
|
||||
### ZswapStatusSpec
|
||||
ZswapStatusSpec is the spec for ZswapStatus resource.
|
||||
|
||||
|
||||
| Field | Type | Label | Description |
|
||||
| ----- | ---- | ----- | ----------- |
|
||||
| total_size_bytes | [uint64](#uint64) | | |
|
||||
| total_size_human | [string](#string) | | |
|
||||
| stored_pages | [uint64](#uint64) | | |
|
||||
| pool_limit_hit | [uint64](#uint64) | | |
|
||||
| reject_reclaim_fail | [uint64](#uint64) | | |
|
||||
| reject_alloc_fail | [uint64](#uint64) | | |
|
||||
| reject_kmemcache_fail | [uint64](#uint64) | | |
|
||||
| reject_compress_fail | [uint64](#uint64) | | |
|
||||
| reject_compress_poor | [uint64](#uint64) | | |
|
||||
| written_back_pages | [uint64](#uint64) | | |
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<!-- end messages -->
|
||||
|
||||
<!-- end enums -->
|
||||
|
||||
@ -0,0 +1,37 @@
|
||||
---
|
||||
description: |
|
||||
ZswapConfig is a zswap (compressed memory) configuration document.
|
||||
When zswap is enabled, Linux kernel compresses pages that would otherwise be swapped out to disk.
|
||||
The compressed pages are stored in a memory pool, which is used to avoid writing to disk
|
||||
when the system is under memory pressure.
|
||||
title: ZswapConfig
|
||||
---
|
||||
|
||||
<!-- markdownlint-disable -->
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
{{< highlight yaml >}}
|
||||
apiVersion: v1alpha1
|
||||
kind: ZswapConfig
|
||||
maxPoolPercent: 25 # The maximum percent of memory that zswap can use.
|
||||
shrinkerEnabled: true # Enable the shrinker feature: kernel might move
|
||||
{{< /highlight >}}
|
||||
|
||||
|
||||
| Field | Type | Description | Value(s) |
|
||||
|-------|------|-------------|----------|
|
||||
|`maxPoolPercent` |int |The maximum percent of memory that zswap can use.<br>This is a percentage of the total system memory.<br>The value must be between 0 and 100. | |
|
||||
|`shrinkerEnabled` |bool |Enable the shrinker feature: kernel might move<br>cold pages from zswap to swap device to free up memory<br>for other use cases. | |
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@ -370,6 +370,49 @@
|
||||
],
|
||||
"description": "VolumeConfig is a system volume configuration document.\\nNote: at the moment, only `EPHEMERAL` and `IMAGE-CACHE` system volumes are supported.\\n"
|
||||
},
|
||||
"block.ZswapConfigV1Alpha1": {
|
||||
"properties": {
|
||||
"apiVersion": {
|
||||
"enum": [
|
||||
"v1alpha1"
|
||||
],
|
||||
"title": "apiVersion",
|
||||
"description": "apiVersion is the API version of the resource.\n",
|
||||
"markdownDescription": "apiVersion is the API version of the resource.",
|
||||
"x-intellij-html-description": "\u003cp\u003eapiVersion is the API version of the resource.\u003c/p\u003e\n"
|
||||
},
|
||||
"kind": {
|
||||
"enum": [
|
||||
"ZswapConfig"
|
||||
],
|
||||
"title": "kind",
|
||||
"description": "kind is the kind of the resource.\n",
|
||||
"markdownDescription": "kind is the kind of the resource.",
|
||||
"x-intellij-html-description": "\u003cp\u003ekind is the kind of the resource.\u003c/p\u003e\n"
|
||||
},
|
||||
"maxPoolPercent": {
|
||||
"type": "integer",
|
||||
"title": "maxPoolPercent",
|
||||
"description": "The maximum percent of memory that zswap can use.\nThis is a percentage of the total system memory.\nThe value must be between 0 and 100.\n",
|
||||
"markdownDescription": "The maximum percent of memory that zswap can use.\nThis is a percentage of the total system memory.\nThe value must be between 0 and 100.",
|
||||
"x-intellij-html-description": "\u003cp\u003eThe maximum percent of memory that zswap can use.\nThis is a percentage of the total system memory.\nThe value must be between 0 and 100.\u003c/p\u003e\n"
|
||||
},
|
||||
"shrinkerEnabled": {
|
||||
"type": "boolean",
|
||||
"title": "shrinkerEnabled",
|
||||
"description": "Enable the shrinker feature: kernel might move\ncold pages from zswap to swap device to free up memory\nfor other use cases.\n",
|
||||
"markdownDescription": "Enable the shrinker feature: kernel might move\ncold pages from zswap to swap device to free up memory\nfor other use cases.",
|
||||
"x-intellij-html-description": "\u003cp\u003eEnable the shrinker feature: kernel might move\ncold pages from zswap to swap device to free up memory\nfor other use cases.\u003c/p\u003e\n"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"type": "object",
|
||||
"required": [
|
||||
"apiVersion",
|
||||
"kind"
|
||||
],
|
||||
"description": "ZswapConfig is a zswap (compressed memory) configuration document.\\nWhen zswap is enabled, Linux kernel compresses pages that would otherwise be swapped out to disk.\\nThe compressed pages are stored in a memory pool, which is used to avoid writing to disk\\nwhen the system is under memory pressure.\\n"
|
||||
},
|
||||
"extensions.ConfigFile": {
|
||||
"properties": {
|
||||
"content": {
|
||||
@ -4242,6 +4285,9 @@
|
||||
{
|
||||
"$ref": "#/$defs/block.VolumeConfigV1Alpha1"
|
||||
},
|
||||
{
|
||||
"$ref": "#/$defs/block.ZswapConfigV1Alpha1"
|
||||
},
|
||||
{
|
||||
"$ref": "#/$defs/extensions.ServiceConfigV1Alpha1"
|
||||
},
|
||||
|
||||
134
website/content/v1.11/talos-guides/configuration/swap.md
Normal file
134
website/content/v1.11/talos-guides/configuration/swap.md
Normal file
@ -0,0 +1,134 @@
|
||||
---
|
||||
title: "Swap"
|
||||
description: "Guide on managing swap devices and zswap configuration in Talos Linux."
|
||||
---
|
||||
|
||||
This guide provides an overview of the swap management features in Talos Linux.
|
||||
|
||||
## Overview
|
||||
|
||||
Swap devices are used to extend the available memory on a system by allowing the kernel to move inactive pages from RAM to disk.
|
||||
Swap might be useful to free up memory when running memory-intensive workloads, but it can also lead to performance degradation if used excessively.
|
||||
On other hand, moving inactive pages to swap can allow Linux to use more memory for buffers and caches, which can improve performance for workloads that benefit from caching.
|
||||
|
||||
Zswap is a compressed cache for swap pages that can help reduce the performance impact of swapping by keeping frequently accessed pages in memory.
|
||||
Swap and zswap can be used together, but they can also be configured independently.
|
||||
|
||||
Swap and zswap are disabled by default in Talos, but can be enabled through the configuration.
|
||||
|
||||
## Swap Devices
|
||||
|
||||
Swap devices can be configured in the [Talos machine configuration]({{< relref "../../reference/configuration/block/swapvolumeconfig" >}}) similar to how [User Volumes]({{< relref "disk-management#" >}}) are configured.
|
||||
As swap devices contain memory pages, it is recommended to enable disk encryption for swap devices to prevent sensitive data from being written to disk in plaintext.
|
||||
It is also recommended to use a separate disk for swap devices to avoid performance degradation on the system disk and other workloads.
|
||||
|
||||
For example, to configure a swap device on a NVMe disk of size 4GiB, using static key for encryption, the following configuration patch can be used:
|
||||
|
||||
```yaml
|
||||
apiVersion: v1alpha1
|
||||
kind: SwapVolumeConfig
|
||||
name: swap1
|
||||
provisioning:
|
||||
diskSelector:
|
||||
match: 'disk.transport == "nvme"'
|
||||
minSize: 4GiB
|
||||
maxSize: 4GiB
|
||||
encryption:
|
||||
provider: luks2
|
||||
keys:
|
||||
- slot: 0
|
||||
tpm: {}
|
||||
- slot: 1
|
||||
static:
|
||||
passphrase: topsecret
|
||||
```
|
||||
|
||||
Talos Linux will automatically provision the partition on the disk, label it as `s-swap1`, encrypt it using the provided key, and enable it as a swap device.
|
||||
|
||||
Current swap status can be checked using `talosctl get swap` command:
|
||||
|
||||
```shell
|
||||
$ talosctl -n 172.20.0.5 get swap
|
||||
NODE NAMESPACE TYPE ID VERSION DEVICE SIZE USED PRIORITY
|
||||
172.20.0.5 runtime SwapStatus /dev/nvme0n2p2 1 /dev/nvme0n2p2 3.9 GiB 100 MiB -2
|
||||
```
|
||||
|
||||
Removing a `SwapVolumeConfig` document will remove the swap device from the system, but the partition will remain on the disk.
|
||||
|
||||
To wipe the disk data, and make it allocatable again, use the following command (replace `nvme0n2p2` with the actual device name):
|
||||
|
||||
```bash
|
||||
talosctl wipe disk nvme0n2p2 --drop-partition
|
||||
```
|
||||
|
||||
## Zswap
|
||||
|
||||
Zswap is a compressed cache for swap pages that can help reduce the performance impact of swapping by keeping frequently accessed pages in memory.
|
||||
Zswap can be enabled in the [Talos machine configuration]({{< relref "../../reference/configuration/block/zswapconfig" >}}):
|
||||
|
||||
```yaml
|
||||
apiVersion: v1alpha1
|
||||
kind: ZswapConfig
|
||||
maxPoolPercent: 20
|
||||
```
|
||||
|
||||
This configuration enables zswap with a maximum pool size of 20% of the total system memory.
|
||||
To check the current zswap status, you can use the `talosctl get zswapstatus` command:
|
||||
|
||||
```shell
|
||||
$ talosctl -n 172.20.0.5 get zswapstatus
|
||||
NODE NAMESPACE TYPE ID VERSION TOTAL SIZE STORED PAGES WRITTEN BACK POOL LIMIT HIT
|
||||
172.20.0.5 runtime ZswapStatus zswap 1 0 B 0 0 0
|
||||
```
|
||||
|
||||
Removing a `ZswapConfig` document will disable zswap on the system.
|
||||
|
||||
## Kubernetes and Swap
|
||||
|
||||
Kubernetes by default [does not allow swap to be used by containers](https://kubernetes.io/blog/2025/03/25/swap-linux-improvements/), as it can lead to performance issues and unpredictable behavior.
|
||||
|
||||
At the very minimum, enable swap usage in the `kubelet` configuration with the following machine configuration patch:
|
||||
|
||||
```yaml
|
||||
machine:
|
||||
kubelet:
|
||||
extraConfig:
|
||||
memorySwap:
|
||||
swapBehavior: LimitedSwap
|
||||
```
|
||||
|
||||
Current swap and zwap usage and limits can be checked using the `talosctl cgroups` [command]({{< relref "../../advanced/cgroups-analysis" >}}):
|
||||
|
||||
```shell
|
||||
$ talosctl cgroups --preset=swap
|
||||
NAME SwapCurrent SwapPeak SwapHigh SwapMax ZswapCurrent ZswapMax ZswapWriteback
|
||||
. unset unset unset unset unset unset 1
|
||||
├──init 0 B 0 B max max 0 B max 1
|
||||
├──kubepods 0 B 0 B max max 0 B max 1
|
||||
│ ├──besteffort 0 B 0 B max max 0 B max 1
|
||||
│ │ └──kube-system/kube-proxy-5gwvz 0 B 0 B max max 0 B max 1
|
||||
│ │ ├──kube-proxy 0 B 0 B max 0 B 0 B max 1
|
||||
│ │ └──sandbox 0 B 0 B max max 0 B max 1
|
||||
│ └──burstable 0 B 0 B max max 0 B max 1
|
||||
│ ├──kube-system/coredns-78d87fb69b-qd6xd 0 B 0 B max max 0 B max 1
|
||||
│ │ ├──coredns 0 B 0 B max 0 B 0 B max 1
|
||||
│ │ └──sandbox 0 B 0 B max max 0 B max 1
|
||||
│ ├──kube-system/coredns-78d87fb69b-z8xj2 0 B 0 B max max 0 B max 1
|
||||
│ │ ├──coredns 0 B 0 B max 0 B 0 B max 1
|
||||
│ │ └──sandbox 0 B 0 B max max 0 B max 1
|
||||
│ └──kube-system/kube-flannel-qqd8v 0 B 0 B max max 0 B max 1
|
||||
│ ├──kube-flannel 0 B 0 B max 0 B 0 B max 1
|
||||
│ └──sandbox 0 B 0 B max max 0 B max 1
|
||||
├──podruntime 0 B 0 B max max 0 B max 1
|
||||
│ ├──kubelet 0 B 0 B max max 0 B max 1
|
||||
│ └──runtime 0 B 0 B max max 0 B max 1
|
||||
└──system 0 B 0 B max max 0 B max 1
|
||||
├──apid 0 B 0 B max 0 B 0 B max 1
|
||||
├──dashboard 0 B 0 B max max 0 B max 1
|
||||
├──runtime 0 B 0 B max max 0 B max 1
|
||||
├──trustd 0 B 0 B max 0 B 0 B max 1
|
||||
└──udevd 0 B 0 B max max 0 B max 1
|
||||
```
|
||||
|
||||
If `SwapMax` is set to `0 B`, it means that swap is not enabled for this cgroup (container/pod).
|
||||
Current swap and zswap usage can be seen in the `SwapCurrent` and `ZswapCurrent` columns, respectively.
|
||||
Loading…
x
Reference in New Issue
Block a user