github.com/NVIDIA/aistore@v1.3.23-0.20240517131212-7df6609be51d/sys/mem_linux.go (about) 1 // Package sys provides methods to read system information 2 /* 3 * Copyright (c) 2018-2022, NVIDIA CORPORATION. All rights reserved. 4 */ 5 package sys 6 7 import ( 8 "math" 9 "strconv" 10 "strings" 11 12 "github.com/NVIDIA/aistore/cmn/cos" 13 "github.com/NVIDIA/aistore/cmn/debug" 14 ) 15 16 var mem0 = MemStat{ActualFree: math.MaxUint64} 17 18 func (mem *MemStat) setValue(name, valStr string) error { 19 val, err := strconv.ParseUint(valStr, 10, 64) 20 if err != nil { 21 debug.AssertNoErr(err) 22 return err 23 } 24 val *= cos.KiB 25 switch name { 26 case "MemTotal": 27 mem.Total = val 28 case "MemFree": 29 mem.Free = val 30 case "MemAvailable": 31 mem.ActualFree = val 32 case "Cached", "Buffers": 33 mem.BuffCache += val 34 case "SwapTotal": 35 mem.SwapTotal = val 36 case "SwapFree": 37 mem.SwapFree = val 38 } 39 return nil 40 } 41 42 // parse `/proc/meminfo` lines 43 func (mem *MemStat) parse(line string) error { 44 fields := strings.Split(line, ":") 45 if len(fields) != 2 { 46 return nil 47 } 48 name := fields[0] 49 valStr := strings.Fields(strings.TrimLeft(fields[1], " "))[0] 50 return mem.setValue(name, valStr) 51 } 52 53 func (mem *MemStat) host() (err error) { 54 *mem = mem0 // ActualFree = MaxUint64, zeros all the rest 55 if err = cos.ReadLines(hostMemPath, mem.parse); err != nil { 56 return 57 } 58 59 if mem.ActualFree == math.MaxUint64 { // remains unassigned (see above) 60 mem.ActualFree = mem.Free + mem.BuffCache 61 } 62 mem.Used = mem.Total - mem.Free 63 mem.ActualUsed = mem.Total - mem.ActualFree 64 mem.SwapUsed = mem.SwapTotal - mem.SwapFree 65 return 66 } 67 68 // parse `/sys/fs/cgroup/memory/memory.stat` lines 69 func (mem *MemStat) cgroupParse(line string) error { 70 fields := strings.Fields(line) 71 if len(fields) < 2 { 72 return nil 73 } 74 if fields[0] != "total_cache" { 75 return nil 76 } 77 val, err := strconv.ParseUint(fields[1], 10, 64) 78 if err != nil { 79 return err 80 } 81 mem.BuffCache += val 82 return nil 83 } 84 85 // Returns host stats if memory is not limited via cgroups "memory.limit_in_bytes". 86 func (mem *MemStat) container() error { 87 if err := mem.host(); err != nil { 88 return err 89 } 90 memLimit, err := cos.ReadOneUint64(contMemLimitPath) 91 if err != nil { 92 return nil 93 } 94 // It is safe to assume that the value greater than MaxInt64/2 indicates "no limit" 95 // https://unix.stackexchange.com/questions/420906/what-is-the-value-for-the-cgroups-limit-in-bytes-if-the-memory-is-not-restricte 96 if memLimit > math.MaxInt64/2 { 97 return nil 98 } 99 100 // this one is an approximate value that includes buff/cache 101 // (ie., kernel buffers and page caches that can be reclaimed) 102 memUsed, err := cos.ReadOneUint64(contMemUsedPath) 103 if err != nil { 104 return nil 105 } 106 mem.Total = memLimit 107 mem.Used = memUsed 108 mem.Free = mem.Total - mem.Used 109 110 // calculate memory used for buffcache 111 err = cos.ReadLines(contMemStatPath, mem.cgroupParse) 112 if err != nil { 113 debug.AssertNoErr(err) 114 // NOTE: returning host memory 115 return nil 116 } 117 118 mem.ActualUsed = memUsed - mem.BuffCache 119 mem.ActualFree = mem.Total - mem.ActualUsed 120 return nil 121 }