Nvm and dimm information (#2415)
* Adding information about memory capacity and number of memory DIMMs by memory type Signed-off-by: Maciej "Iwan" Iwanowski <maciej.iwanowski@intel.com>
This commit is contained in:
parent
cfe6b50eb9
commit
a8de0e0fff
1
.gitignore
vendored
1
.gitignore
vendored
@ -8,3 +8,4 @@ cadvisor
|
||||
# Files generated by JetBrains IDEs, e.g. IntelliJ IDEA
|
||||
.idea/
|
||||
*.iml
|
||||
*.swp
|
||||
|
2
go.mod
2
go.mod
@ -45,7 +45,7 @@ require (
|
||||
github.com/influxdb/influxdb v0.9.6-0.20151125225445-9eab56311373
|
||||
github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8 // indirect
|
||||
github.com/karrick/godirwalk v1.7.5
|
||||
github.com/kevinburke/go-bindata v3.17.0+incompatible // indirect
|
||||
github.com/kevinburke/go-bindata v3.18.0+incompatible // indirect
|
||||
github.com/klauspost/crc32 v0.0.0-20151223135126-a3b15ae34567 // indirect
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.2 // indirect
|
||||
github.com/kr/pretty v0.0.0-20140723054909-088c856450c0
|
||||
|
2
go.sum
2
go.sum
@ -167,6 +167,8 @@ github.com/kevinburke/go-bindata v3.16.0+incompatible h1:TFzFZop2KxGhqNwsyjgmIh5
|
||||
github.com/kevinburke/go-bindata v3.16.0+incompatible/go.mod h1:/pEEZ72flUW2p0yi30bslSp9YqD9pysLxunQDdb2CPM=
|
||||
github.com/kevinburke/go-bindata v3.17.0+incompatible h1:eXG58bFD2FVMYilP6PfjjSwKy4YTHmifsI8TURmlVVg=
|
||||
github.com/kevinburke/go-bindata v3.17.0+incompatible/go.mod h1:/pEEZ72flUW2p0yi30bslSp9YqD9pysLxunQDdb2CPM=
|
||||
github.com/kevinburke/go-bindata v3.18.0+incompatible h1:NfOP49jFW7KyBl7UwTg0xkhSfHjESEwe2VMrcnSHG20=
|
||||
github.com/kevinburke/go-bindata v3.18.0+incompatible/go.mod h1:/pEEZ72flUW2p0yi30bslSp9YqD9pysLxunQDdb2CPM=
|
||||
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
|
||||
github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
|
@ -173,6 +173,9 @@ type MachineInfo struct {
|
||||
// The amount of memory (in bytes) in this machine
|
||||
MemoryCapacity uint64 `json:"memory_capacity"`
|
||||
|
||||
// Memory capacity and number of DIMMs by memory type
|
||||
MemoryByType map[string]*MemoryInfo `json:"memory_by_type"`
|
||||
|
||||
// HugePages on this machine.
|
||||
HugePages []HugePagesInfo `json:"hugepages"`
|
||||
|
||||
@ -208,6 +211,14 @@ type MachineInfo struct {
|
||||
InstanceID InstanceID `json:"instance_id"`
|
||||
}
|
||||
|
||||
type MemoryInfo struct {
|
||||
// The amount of memory (in bytes).
|
||||
Capacity uint64
|
||||
|
||||
// Number of memory DIMMs.
|
||||
DimmCount uint
|
||||
}
|
||||
|
||||
type VersionInfo struct {
|
||||
// Kernel version.
|
||||
KernelVersion string `json:"kernel_version"`
|
||||
|
@ -34,6 +34,7 @@ import (
|
||||
)
|
||||
|
||||
const hugepagesDirectory = "/sys/kernel/mm/hugepages/"
|
||||
const memoryControllerPath = "/sys/devices/system/edac/mc/"
|
||||
|
||||
var machineIdFilePath = flag.String("machine_id_file", "/etc/machine-id,/var/lib/dbus/machine-id", "Comma-separated list of files to check for machine-id. Use the first one that exists.")
|
||||
var bootIdFilePath = flag.String("boot_id_file", "/proc/sys/kernel/random/boot_id", "Comma-separated list of files to check for boot-id. Use the first one that exists.")
|
||||
@ -72,6 +73,11 @@ func Info(sysFs sysfs.SysFs, fsInfo fs.FsInfo, inHostNamespace bool) (*info.Mach
|
||||
return nil, err
|
||||
}
|
||||
|
||||
memoryByType, err := GetMachineMemoryByType(memoryControllerPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
hugePagesInfo, err := GetHugePagesInfo(hugepagesDirectory)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -113,6 +119,7 @@ func Info(sysFs sysfs.SysFs, fsInfo fs.FsInfo, inHostNamespace bool) (*info.Mach
|
||||
NumSockets: GetSockets(cpuinfo),
|
||||
CpuFrequency: clockSpeed,
|
||||
MemoryCapacity: memoryCapacity,
|
||||
MemoryByType: memoryByType,
|
||||
HugePages: hugePagesInfo,
|
||||
DiskMap: diskMap,
|
||||
NetworkDevices: netDevices,
|
||||
|
@ -19,6 +19,8 @@ import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strconv"
|
||||
@ -47,7 +49,9 @@ var (
|
||||
memoryCapacityRegexp = regexp.MustCompile(`MemTotal:\s*([0-9]+) kB`)
|
||||
swapCapacityRegexp = regexp.MustCompile(`SwapTotal:\s*([0-9]+) kB`)
|
||||
|
||||
cpuBusPath = "/sys/bus/cpu/devices/"
|
||||
cpuBusPath = "/sys/bus/cpu/devices/"
|
||||
isMemoryController = regexp.MustCompile("mc[0-9]+")
|
||||
isDimm = regexp.MustCompile("dimm[0-9]+")
|
||||
)
|
||||
|
||||
const maxFreqFile = "/sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq"
|
||||
@ -55,6 +59,8 @@ const nodePath = "/sys/devices/system/node"
|
||||
const sysFsCPUCoreID = "core_id"
|
||||
const sysFsCPUPhysicalPackageID = "physical_package_id"
|
||||
const sysFsCPUTopology = "topology"
|
||||
const memTypeFileName = "dimm_mem_type"
|
||||
const sizeFileName = "size"
|
||||
|
||||
// GetPhysicalCores returns number of CPU cores reading /proc/cpuinfo file or if needed information from sysfs cpu path
|
||||
func GetPhysicalCores(procInfo []byte) int {
|
||||
@ -133,6 +139,65 @@ func GetMachineMemoryCapacity() (uint64, error) {
|
||||
return memoryCapacity, err
|
||||
}
|
||||
|
||||
// GetMachineMemoryByType returns information about memory capcity and number of DIMMs.
|
||||
// Information is retrieved from sysfs edac per-DIMM API (/sys/devices/system/edac/mc/)
|
||||
// introduced in kernel 3.6. Documentation can be found at
|
||||
// https://www.kernel.org/doc/Documentation/admin-guide/ras.rst.
|
||||
// Full list of memory types can be found in edac_mc.c
|
||||
// (https://github.com/torvalds/linux/blob/v5.5/drivers/edac/edac_mc.c#L198)
|
||||
func GetMachineMemoryByType(edacPath string) (map[string]*info.MemoryInfo, error) {
|
||||
memory := map[string]*info.MemoryInfo{}
|
||||
names, err := ioutil.ReadDir(edacPath)
|
||||
// On some architectures (such as ARM) memory controller device may not exist.
|
||||
// If this is the case then we ignore error and return empty slice.
|
||||
_, ok := err.(*os.PathError)
|
||||
if err != nil && ok {
|
||||
return memory, nil
|
||||
} else if err != nil {
|
||||
return memory, err
|
||||
}
|
||||
for _, controllerDir := range names {
|
||||
controller := controllerDir.Name()
|
||||
if !isMemoryController.MatchString(controller) {
|
||||
continue
|
||||
}
|
||||
dimms, err := ioutil.ReadDir(path.Join(edacPath, controllerDir.Name()))
|
||||
if err != nil {
|
||||
return map[string]*info.MemoryInfo{}, err
|
||||
}
|
||||
for _, dimmDir := range dimms {
|
||||
dimm := dimmDir.Name()
|
||||
if !isDimm.MatchString(dimm) {
|
||||
continue
|
||||
}
|
||||
memType, err := ioutil.ReadFile(path.Join(edacPath, controller, dimm, memTypeFileName))
|
||||
readableMemType := strings.TrimSpace(string(memType))
|
||||
if err != nil {
|
||||
return map[string]*info.MemoryInfo{}, err
|
||||
}
|
||||
if _, exists := memory[readableMemType]; !exists {
|
||||
memory[readableMemType] = &info.MemoryInfo{}
|
||||
}
|
||||
size, err := ioutil.ReadFile(path.Join(edacPath, controller, dimm, sizeFileName))
|
||||
if err != nil {
|
||||
return map[string]*info.MemoryInfo{}, err
|
||||
}
|
||||
capacity, err := strconv.Atoi(strings.TrimSpace(string(size)))
|
||||
if err != nil {
|
||||
return map[string]*info.MemoryInfo{}, err
|
||||
}
|
||||
memory[readableMemType].Capacity += uint64(mbToBytes(capacity))
|
||||
memory[readableMemType].DimmCount++
|
||||
}
|
||||
}
|
||||
|
||||
return memory, nil
|
||||
}
|
||||
|
||||
func mbToBytes(megabytes int) int {
|
||||
return megabytes * 1024 * 1024
|
||||
}
|
||||
|
||||
// GetMachineSwapCapacity returns the machine's total swap from /proc/meminfo.
|
||||
// Returns the total swap capacity as an uint64 (number of bytes).
|
||||
func GetMachineSwapCapacity() (uint64, error) {
|
||||
|
1
machine/testdata/edac/mc/mc0/dimm0/dimm_mem_type
vendored
Normal file
1
machine/testdata/edac/mc/mc0/dimm0/dimm_mem_type
vendored
Normal file
@ -0,0 +1 @@
|
||||
Unbuffered-DDR4
|
1
machine/testdata/edac/mc/mc0/dimm0/size
vendored
Normal file
1
machine/testdata/edac/mc/mc0/dimm0/size
vendored
Normal file
@ -0,0 +1 @@
|
||||
789
|
1
machine/testdata/edac/mc/mc0/dimm1/dimm_mem_type
vendored
Normal file
1
machine/testdata/edac/mc/mc0/dimm1/dimm_mem_type
vendored
Normal file
@ -0,0 +1 @@
|
||||
Non-volatile-RAM
|
1
machine/testdata/edac/mc/mc0/dimm1/size
vendored
Normal file
1
machine/testdata/edac/mc/mc0/dimm1/size
vendored
Normal file
@ -0,0 +1 @@
|
||||
456
|
1
machine/testdata/edac/mc/mc0/dimm_is_fake/dimm_mem_type
vendored
Normal file
1
machine/testdata/edac/mc/mc0/dimm_is_fake/dimm_mem_type
vendored
Normal file
@ -0,0 +1 @@
|
||||
Unbuffered-DDR4
|
1
machine/testdata/edac/mc/mc0/dimm_is_fake/size
vendored
Normal file
1
machine/testdata/edac/mc/mc0/dimm_is_fake/size
vendored
Normal file
@ -0,0 +1 @@
|
||||
321
|
1
machine/testdata/edac/mc/mc1/dimm0/dimm_mem_type
vendored
Normal file
1
machine/testdata/edac/mc/mc1/dimm0/dimm_mem_type
vendored
Normal file
@ -0,0 +1 @@
|
||||
Non-volatile-RAM
|
1
machine/testdata/edac/mc/mc1/dimm0/size
vendored
Normal file
1
machine/testdata/edac/mc/mc1/dimm0/size
vendored
Normal file
@ -0,0 +1 @@
|
||||
123
|
1
machine/testdata/edac/mc/mc_fake/dimm0/dimm0/dimm_mem_type
vendored
Normal file
1
machine/testdata/edac/mc/mc_fake/dimm0/dimm0/dimm_mem_type
vendored
Normal file
@ -0,0 +1 @@
|
||||
Unbuffered-DDR4
|
1
machine/testdata/edac/mc/mc_fake/dimm0/dimm0/size
vendored
Normal file
1
machine/testdata/edac/mc/mc_fake/dimm0/dimm0/size
vendored
Normal file
@ -0,0 +1 @@
|
||||
789
|
1
machine/testdata/edac/mc/mc_fake/dimm0/dimm1/dimm_mem_type
vendored
Normal file
1
machine/testdata/edac/mc/mc_fake/dimm0/dimm1/dimm_mem_type
vendored
Normal file
@ -0,0 +1 @@
|
||||
Non-volatile-RAM
|
1
machine/testdata/edac/mc/mc_fake/dimm0/dimm1/size
vendored
Normal file
1
machine/testdata/edac/mc/mc_fake/dimm0/dimm1/size
vendored
Normal file
@ -0,0 +1 @@
|
||||
456
|
1
machine/testdata/edac/mc/mc_fake/dimm0/dimm_mem_type
vendored
Normal file
1
machine/testdata/edac/mc/mc_fake/dimm0/dimm_mem_type
vendored
Normal file
@ -0,0 +1 @@
|
||||
Unbuffered-DDR4
|
1
machine/testdata/edac/mc/mc_fake/dimm0/size
vendored
Normal file
1
machine/testdata/edac/mc/mc_fake/dimm0/size
vendored
Normal file
@ -0,0 +1 @@
|
||||
789
|
1
machine/testdata/edac/mc/mc_fake/dimm1/dimm_mem_type
vendored
Normal file
1
machine/testdata/edac/mc/mc_fake/dimm1/dimm_mem_type
vendored
Normal file
@ -0,0 +1 @@
|
||||
Non-volatile-RAM
|
1
machine/testdata/edac/mc/mc_fake/dimm1/size
vendored
Normal file
1
machine/testdata/edac/mc/mc_fake/dimm1/size
vendored
Normal file
@ -0,0 +1 @@
|
||||
456
|
@ -247,3 +247,23 @@ func TestGetHugePagesInfo(t *testing.T) {
|
||||
t.Errorf("Expected HugePagesInfo %+v, got %+v", expected, val)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMemoryInfo(t *testing.T) {
|
||||
testPath := "./testdata/edac/mc"
|
||||
memory, err := GetMachineMemoryByType(testPath)
|
||||
|
||||
assert.Nil(t, err)
|
||||
assert.Len(t, memory, 2)
|
||||
assert.Equal(t, uint64(789*1024*1024), memory["Unbuffered-DDR4"].Capacity)
|
||||
assert.Equal(t, uint64(579*1024*1024), memory["Non-volatile-RAM"].Capacity)
|
||||
assert.Equal(t, uint(1), memory["Unbuffered-DDR4"].DimmCount)
|
||||
assert.Equal(t, uint(2), memory["Non-volatile-RAM"].DimmCount)
|
||||
}
|
||||
|
||||
func TestMemoryInfoOnArchThatDoNotExposeMemoryController(t *testing.T) {
|
||||
testPath := "./there/is/no/spoon"
|
||||
memory, err := GetMachineMemoryByType(testPath)
|
||||
|
||||
assert.Nil(t, err)
|
||||
assert.Len(t, memory, 0)
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user