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:
iwankgb 2020-03-06 20:56:13 +01:00 committed by GitHub
parent cfe6b50eb9
commit a8de0e0fff
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
23 changed files with 124 additions and 2 deletions

1
.gitignore vendored
View File

@ -8,3 +8,4 @@ cadvisor
# Files generated by JetBrains IDEs, e.g. IntelliJ IDEA
.idea/
*.iml
*.swp

2
go.mod
View File

@ -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
View File

@ -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=

View File

@ -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"`

View File

@ -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,

View File

@ -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) {

View File

@ -0,0 +1 @@
Unbuffered-DDR4

View File

@ -0,0 +1 @@
789

View File

@ -0,0 +1 @@
Non-volatile-RAM

View File

@ -0,0 +1 @@
456

View File

@ -0,0 +1 @@
Unbuffered-DDR4

View File

@ -0,0 +1 @@
321

View File

@ -0,0 +1 @@
Non-volatile-RAM

View File

@ -0,0 +1 @@
123

View File

@ -0,0 +1 @@
Unbuffered-DDR4

View File

@ -0,0 +1 @@
789

View File

@ -0,0 +1 @@
Non-volatile-RAM

View File

@ -0,0 +1 @@
456

View File

@ -0,0 +1 @@
Unbuffered-DDR4

View File

@ -0,0 +1 @@
789

View File

@ -0,0 +1 @@
Non-volatile-RAM

View File

@ -0,0 +1 @@
456

View File

@ -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)
}