Skip to content

Commit 571eb6c

Browse files
committed
add numa_stat for cgroup v2
Signed-off-by: victoryang00 <[email protected]>
1 parent b338acc commit 571eb6c

File tree

2 files changed

+124
-0
lines changed

2 files changed

+124
-0
lines changed

libcontainer/cgroups/fs2/fs2.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,10 @@ import (
1313

1414
type parseError = fscommon.ParseError
1515

16+
func malformedLine(path, file, line string) error {
17+
return &parseError{Path: path, File: file, Err: fmt.Errorf("malformed line: %s", line)}
18+
}
19+
1620
type Manager struct {
1721
config *configs.Cgroup
1822
// dirPath is like "/sys/fs/cgroup/user.slice/user-1001.slice/session-1.scope"

libcontainer/cgroups/fs2/memory.go

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,12 @@ func statMemory(dirPath string, stats *cgroups.Stats) error {
102102
// cgroup v2 is always hierarchical.
103103
stats.MemoryStats.UseHierarchy = true
104104

105+
pagesByNUMA, err := getPageUsageByNUMAV2(dirPath)
106+
if err != nil {
107+
return err
108+
}
109+
stats.MemoryStats.PageUsageByNUMA = pagesByNUMA
110+
105111
memoryUsage, err := getMemoryDataV2(dirPath, "")
106112
if err != nil {
107113
if errors.Is(err, unix.ENOENT) && dirPath == UnifiedMountpoint {
@@ -219,3 +225,117 @@ func statsFromMeminfo(stats *cgroups.Stats) error {
219225

220226
return nil
221227
}
228+
229+
func getPageUsageByNUMAV2(path string) (cgroups.PageUsageByNUMA, error) {
230+
const (
231+
maxColumns = math.MaxUint8 + 1
232+
file = "memory.numa_stat"
233+
)
234+
stats := cgroups.PageUsageByNUMA{}
235+
236+
fd, err := cgroups.OpenFile(path, file, os.O_RDONLY)
237+
if os.IsNotExist(err) {
238+
return stats, nil
239+
} else if err != nil {
240+
return stats, err
241+
}
242+
defer fd.Close()
243+
244+
// anon N0=139022336 N1=2760704
245+
// file N0=449581056 N1=0
246+
// kernel_stack N0=3670016 N1=0
247+
// pagetables N0=4116480 N1=0
248+
// sec_pagetables N0=0 N1=0
249+
// shmem N0=0 N1=0
250+
// file_mapped N0=55029760 N1=0
251+
// file_dirty N0=0 N1=0
252+
// file_writeback N0=0 N1=0
253+
// swapcached N0=0 N1=0
254+
// anon_thp N0=0 N1=0
255+
// file_thp N0=0 N1=0
256+
// shmem_thp N0=0 N1=0
257+
// inactive_anon N0=138956800 N1=2752512
258+
// active_anon N0=65536 N1=8192
259+
// inactive_file N0=14770176 N1=0
260+
// active_file N0=434810880 N1=0
261+
// unevictable N0=0 N1=0
262+
// slab_reclaimable N0=2358224 N1=11088
263+
// slab_unreclaimable N0=2672352 N1=544144
264+
// workingset_refault_anon N0=0 N1=0
265+
// workingset_refault_file N0=0 N1=0
266+
// workingset_activate_anon N0=0 N1=0
267+
// workingset_activate_file N0=0 N1=0
268+
// workingset_restore_anon N0=0 N1=0
269+
// workingset_restore_file N0=0 N1=0
270+
// workingset_nodereclaim N0=0 N1=0
271+
272+
scanner := bufio.NewScanner(fd)
273+
for scanner.Scan() {
274+
var field *cgroups.PageStats
275+
276+
line := scanner.Text()
277+
columns := strings.SplitN(line, " ", maxColumns)
278+
for i, column := range columns {
279+
byNode := strings.SplitN(column, "=", 2)
280+
key := byNode[0]
281+
if i == 0 { // First column: key is name, val is total.
282+
field = getNUMAFieldV2(&stats, key)
283+
if field == nil { // unknown field (new kernel?)
284+
break
285+
}
286+
field.Nodes = map[uint8]uint64{}
287+
} else { // Subsequent columns: key is N<id>, val is usage.
288+
val := byNode[1]
289+
if len(key) < 2 || key[0] != 'N' {
290+
// This is definitely an error.
291+
return stats, malformedLine(path, file, line)
292+
}
293+
294+
n, err := strconv.ParseUint(key[1:], 10, 8)
295+
if err != nil {
296+
return stats, &parseError{Path: path, File: file, Err: err}
297+
}
298+
299+
usage, err := strconv.ParseUint(val, 10, 64)
300+
if err != nil {
301+
return stats, &parseError{Path: path, File: file, Err: err}
302+
}
303+
if !strings.HasSuffix(key, "thp") {
304+
field.Nodes[uint8(n)] += usage
305+
field.Total += usage
306+
} else {
307+
field.Nodes[uint8(n)] += (usage >> 5)
308+
field.Total += (usage >> 5)
309+
}
310+
}
311+
312+
}
313+
}
314+
if err := scanner.Err(); err != nil {
315+
return cgroups.PageUsageByNUMA{}, &parseError{Path: path, File: file, Err: err}
316+
}
317+
318+
return stats, nil
319+
}
320+
321+
func getNUMAFieldV2(stats *cgroups.PageUsageByNUMA, name string) *cgroups.PageStats {
322+
switch name {
323+
case "pagetable":
324+
return &stats.Total
325+
case "anon":
326+
return &stats.Anon
327+
case "anon_thp":
328+
return &stats.Anon
329+
case "file_mapped":
330+
return &stats.File
331+
case "file_dirty":
332+
return &stats.File
333+
case "file_writeback":
334+
return &stats.File
335+
case "file_thp":
336+
return &stats.File
337+
case "unevictable":
338+
return &stats.Unevictable
339+
}
340+
return nil
341+
}

0 commit comments

Comments
 (0)