Skip to content

Commit edd00eb

Browse files
authored
Merge pull request #4010 from HeRaNO/use-peak
feat: add `swapOnlyUsage` in `MemoryStats`
2 parents 1947d0c + 104b8dc commit edd00eb

File tree

6 files changed

+49
-8
lines changed

6 files changed

+49
-8
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
3131
This aligns cgroupv2 root usage more closely with cgroupv1 reporting.
3232
Additionally, report root swap usage as sum of swap and memory usage,
3333
aligned with v1 and existing non-root v2 reporting. (#3933)
34+
* Add `swapOnlyUsage` in `MemoryStats`. This field reports swap-only usage.
35+
For cgroupv1, `Usage` and `Failcnt` are set by subtracting memory usage
36+
from memory+swap usage. For cgroupv2, `Usage`, `Limit`, and `MaxUsage`
37+
are set. (#4010)
3438

3539
### Fixed
3640

libcontainer/cgroups/fs/memory.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,10 @@ func (s *MemoryGroup) GetStats(path string, stats *cgroups.Stats) error {
170170
return err
171171
}
172172
stats.MemoryStats.SwapUsage = swapUsage
173+
stats.MemoryStats.SwapOnlyUsage = cgroups.MemoryData{
174+
Usage: swapUsage.Usage - memoryUsage.Usage,
175+
Failcnt: swapUsage.Failcnt - memoryUsage.Failcnt,
176+
}
173177
kernelUsage, err := getMemoryData(path, "kmem")
174178
if err != nil {
175179
return err

libcontainer/cgroups/fs/memory_test.go

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -249,12 +249,13 @@ func TestMemoryStats(t *testing.T) {
249249
t.Fatal(err)
250250
}
251251
expectedStats := cgroups.MemoryStats{
252-
Cache: 512,
253-
Usage: cgroups.MemoryData{Usage: 2048, MaxUsage: 4096, Failcnt: 100, Limit: 8192},
254-
SwapUsage: cgroups.MemoryData{Usage: 2048, MaxUsage: 4096, Failcnt: 100, Limit: 8192},
255-
KernelUsage: cgroups.MemoryData{Usage: 2048, MaxUsage: 4096, Failcnt: 100, Limit: 8192},
256-
Stats: map[string]uint64{"cache": 512, "rss": 1024},
257-
UseHierarchy: true,
252+
Cache: 512,
253+
Usage: cgroups.MemoryData{Usage: 2048, MaxUsage: 4096, Failcnt: 100, Limit: 8192},
254+
SwapUsage: cgroups.MemoryData{Usage: 2048, MaxUsage: 4096, Failcnt: 100, Limit: 8192},
255+
SwapOnlyUsage: cgroups.MemoryData{Usage: 0, MaxUsage: 0, Failcnt: 0, Limit: 0},
256+
KernelUsage: cgroups.MemoryData{Usage: 2048, MaxUsage: 4096, Failcnt: 100, Limit: 8192},
257+
Stats: map[string]uint64{"cache": 512, "rss": 1024},
258+
UseHierarchy: true,
258259
PageUsageByNUMA: cgroups.PageUsageByNUMA{
259260
PageUsageByNUMAInner: cgroups.PageUsageByNUMAInner{
260261
Total: cgroups.PageStats{Total: 44611, Nodes: map[uint8]uint64{0: 32631, 1: 7501, 2: 1982, 3: 2497}},

libcontainer/cgroups/fs2/memory.go

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -105,25 +105,30 @@ func statMemory(dirPath string, stats *cgroups.Stats) error {
105105
memoryUsage, err := getMemoryDataV2(dirPath, "")
106106
if err != nil {
107107
if errors.Is(err, unix.ENOENT) && dirPath == UnifiedMountpoint {
108-
// The root cgroup does not have memory.{current,max}
108+
// The root cgroup does not have memory.{current,max,peak}
109109
// so emulate those using data from /proc/meminfo and
110110
// /sys/fs/cgroup/memory.stat
111111
return rootStatsFromMeminfo(stats)
112112
}
113113
return err
114114
}
115115
stats.MemoryStats.Usage = memoryUsage
116-
swapUsage, err := getMemoryDataV2(dirPath, "swap")
116+
swapOnlyUsage, err := getMemoryDataV2(dirPath, "swap")
117117
if err != nil {
118118
return err
119119
}
120+
stats.MemoryStats.SwapOnlyUsage = swapOnlyUsage
121+
swapUsage := swapOnlyUsage
120122
// As cgroup v1 reports SwapUsage values as mem+swap combined,
121123
// while in cgroup v2 swap values do not include memory,
122124
// report combined mem+swap for v1 compatibility.
123125
swapUsage.Usage += memoryUsage.Usage
124126
if swapUsage.Limit != math.MaxUint64 {
125127
swapUsage.Limit += memoryUsage.Limit
126128
}
129+
// The `MaxUsage` of mem+swap cannot simply combine mem with
130+
// swap. So set it to 0 for v1 compatibility.
131+
swapUsage.MaxUsage = 0
127132
stats.MemoryStats.SwapUsage = swapUsage
128133

129134
return nil
@@ -138,6 +143,7 @@ func getMemoryDataV2(path, name string) (cgroups.MemoryData, error) {
138143
}
139144
usage := moduleName + ".current"
140145
limit := moduleName + ".max"
146+
maxUsage := moduleName + ".peak"
141147

142148
value, err := fscommon.GetCgroupParamUint(path, usage)
143149
if err != nil {
@@ -157,6 +163,14 @@ func getMemoryDataV2(path, name string) (cgroups.MemoryData, error) {
157163
}
158164
memoryData.Limit = value
159165

166+
// `memory.peak` since kernel 5.19
167+
// `memory.swap.peak` since kernel 6.5
168+
value, err = fscommon.GetCgroupParamUint(path, maxUsage)
169+
if err != nil && !os.IsNotExist(err) {
170+
return cgroups.MemoryData{}, err
171+
}
172+
memoryData.MaxUsage = value
173+
160174
return memoryData, nil
161175
}
162176

libcontainer/cgroups/fs2/memory_test.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,10 @@ func TestStatMemoryPodCgroup(t *testing.T) {
9494
t.Fatal(err)
9595
}
9696

97+
if err := os.WriteFile(filepath.Join(fakeCgroupDir, "memory.peak"), []byte("987654321"), 0o644); err != nil {
98+
t.Fatal(err)
99+
}
100+
97101
gotStats := cgroups.NewStats()
98102

99103
// use a fake root path to trigger the pod cgroup lookup.
@@ -107,6 +111,18 @@ func TestStatMemoryPodCgroup(t *testing.T) {
107111
if gotStats.MemoryStats.Usage.Usage != expectedUsageBytes {
108112
t.Errorf("parsed cgroupv2 memory.stat doesn't match expected result: \ngot %#v\nexpected %#v\n", gotStats.MemoryStats.Usage.Usage, expectedUsageBytes)
109113
}
114+
115+
// result should be "memory.max"
116+
var expectedLimitBytes uint64 = 999999999
117+
if gotStats.MemoryStats.Usage.Limit != expectedLimitBytes {
118+
t.Errorf("parsed cgroupv2 memory.stat doesn't match expected result: \ngot %#v\nexpected %#v\n", gotStats.MemoryStats.Usage.Limit, expectedLimitBytes)
119+
}
120+
121+
// result should be "memory.peak"
122+
var expectedMaxUsageBytes uint64 = 987654321
123+
if gotStats.MemoryStats.Usage.MaxUsage != expectedMaxUsageBytes {
124+
t.Errorf("parsed cgroupv2 memory.stat doesn't match expected result: \ngot %#v\nexpected %#v\n", gotStats.MemoryStats.Usage.MaxUsage, expectedMaxUsageBytes)
125+
}
110126
}
111127

112128
func TestRootStatsFromMeminfo(t *testing.T) {

libcontainer/cgroups/stats.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,8 @@ type MemoryStats struct {
9191
Usage MemoryData `json:"usage,omitempty"`
9292
// usage of memory + swap
9393
SwapUsage MemoryData `json:"swap_usage,omitempty"`
94+
// usage of swap only
95+
SwapOnlyUsage MemoryData `json:"swap_only_usage,omitempty"`
9496
// usage of kernel memory
9597
KernelUsage MemoryData `json:"kernel_usage,omitempty"`
9698
// usage of kernel TCP memory

0 commit comments

Comments
 (0)