Skip to content

Commit

Permalink
WIP: memory: topology: expose per-numa hugepages
Browse files Browse the repository at this point in the history
This patch wants to add hugepages informations on topology.Node,
so we can have per-NUMA-zone information.

WIP: docs are missing, and so are representation helpers.

Signed-off-by: Francesco Romani <[email protected]>
  • Loading branch information
ffromani committed Aug 27, 2021
1 parent 9c86f7a commit 0ecf508
Show file tree
Hide file tree
Showing 7 changed files with 147 additions and 4 deletions.
16 changes: 16 additions & 0 deletions pkg/memory/memory_hugepages.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
//
// Use and distribution licensed under the Apache license version 2.
//
// See the COPYING file in the root project directory for full text.
//

package memory

// TODO review
type Hugepages struct {
SizeKB int
FreeAmount int
TotalAmount int
}

// TODO: String()
60 changes: 60 additions & 0 deletions pkg/memory/memory_hugepages_linux.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
// Use and distribution licensed under the Apache license version 2.
//
// See the COPYING file in the root project directory for full text.
//

package memory

import (
"fmt"
"io/ioutil"
"path/filepath"

"github.com/jaypipes/ghw/pkg/context"
"github.com/jaypipes/ghw/pkg/linuxpath"
"github.com/jaypipes/ghw/pkg/util"
)

func HugepagesForNode(ctx *context.Context, nodeID int) ([]*Hugepages, error) {
paths := linuxpath.New(ctx)
path := filepath.Join(
paths.SysDevicesSystemNode,
fmt.Sprintf("node%d", nodeID),
"hugepages",
)
hugepages := []*Hugepages{}

entries, err := ioutil.ReadDir(path)
if err != nil {
return nil, err
}
for _, entry := range entries {
entryName := entry.Name()
entryPath := filepath.Join(path, entryName)
var hugepageSizeKB int
if n, err := fmt.Sscanf(entryName, "hugepages-%dkB", &hugepageSizeKB); n != 1 || err != nil {
ctx.Warn("malformed entry on %q: %v", entryPath, err)
continue
}

freeCount := util.SafeIntFromFile(ctx, filepath.Join(path, entryName, "free_hugepages"))
if freeCount < 0 {
ctx.Warn("failed to read free hugepages on %q", entryPath)
continue
}

totalCount := util.SafeIntFromFile(ctx, filepath.Join(path, entryName, "nr_hugepages"))
if totalCount < 0 {
ctx.Warn("failed to read total hugepages on %q", entryPath)
continue
}

hugepages = append(hugepages, &Hugepages{
SizeKB: hugepageSizeKB,
FreeAmount: freeCount,
TotalAmount: totalCount,
})
}

return hugepages, nil
}
1 change: 1 addition & 0 deletions pkg/snapshot/clonetree_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ func ExpectedCloneStaticContent() []string {
"/sys/devices/system/node/node*/distance",
"/sys/devices/system/node/node*/meminfo",
"/sys/devices/system/node/node*/memory*",
"/sys/devices/system/node/node*/hugepages/hugepages-*/*",
}
}

Expand Down
1 change: 1 addition & 0 deletions pkg/topology/topology.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ type Node struct {
Caches []*memory.Cache `json:"caches"`
Distances []int `json:"distances"`
Memory *memory.Area `json:"memory"`
Hugepages []*memory.Hugepages `json:"hugepages"`
}

func (n *Node) String() string {
Expand Down
15 changes: 11 additions & 4 deletions pkg/topology/topology_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,31 +51,38 @@ func topologyNodes(ctx *context.Context) []*Node {
node.ID = nodeID
cores, err := cpu.CoresForNode(ctx, nodeID)
if err != nil {
ctx.Warn("failed to determine cores for node: %s\n", err)
ctx.Warn("failed to determine cores for node %d: %s\n", nodeID, err)
return nodes
}
node.Cores = cores
caches, err := memory.CachesForNode(ctx, nodeID)
if err != nil {
ctx.Warn("failed to determine caches for node: %s\n", err)
ctx.Warn("failed to determine caches for node %d: %s\n", nodeID, err)
return nodes
}
node.Caches = caches

distances, err := distancesForNode(ctx, nodeID)
if err != nil {
ctx.Warn("failed to determine node distances for node: %s\n", err)
ctx.Warn("failed to determine node distances for node %d: %s\n", nodeID, err)
return nodes
}
node.Distances = distances

area, err := memory.AreaForNode(ctx, nodeID)
if err != nil {
ctx.Warn("failed to determine memory area for node: %s\n", err)
ctx.Warn("failed to determine memory area for node %d: %s\n", nodeID, err)
return nodes
}
node.Memory = area

hugepages, err := memory.HugepagesForNode(ctx, nodeID)
if err != nil {
ctx.Warn("failed to determine hugepages for node %d: %s\n", nodeID, err)
return nodes
}
node.Hugepages = hugepages

nodes = append(nodes, node)
}
return nodes
Expand Down
58 changes: 58 additions & 0 deletions pkg/topology/topology_linux_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -121,3 +121,61 @@ func TestTopologyPerNUMAMemory(t *testing.T) {
}
}
}

// nolint: gocyclo
func TestTopologyPerNUMAHugepages(t *testing.T) {
testdataPath, err := testdata.SnapshotsDirectory()
if err != nil {
t.Fatalf("Expected nil err, but got %v", err)
}

multiNumaSnapshot := filepath.Join(testdataPath, "linux-amd64-intel-xeon-L5640.tar.gz")
// from now on we use constants reflecting the content of the snapshot we requested,
// which we reviewed beforehand. IOW, you need to know the content of the
// snapshot to fully understand this test. Inspect it using
// GHW_SNAPSHOT_PATH="/path/to/linux-amd64-intel-xeon-L5640.tar.gz" ghwc topology
info, err := topology.New(option.WithSnapshot(option.SnapshotOptions{
Path: multiNumaSnapshot,
}))

if err != nil {
t.Fatalf("Expected nil err, but got %v", err)
}
if info == nil {
t.Fatalf("Expected non-nil TopologyInfo, but got nil")
}

if len(info.Nodes) != 2 {
t.Fatalf("Expected 2 nodes but got 0.")
}

for _, node := range info.Nodes {
if node.Hugepages == nil {
t.Fatalf("missing hugepages information for node %d", node.ID)
}

for _, hugepages := range node.Hugepages {
if !isSupportedHugepageSize(hugepages.SizeKB) {
t.Fatalf("node %d unsupported size: %d", node.ID, hugepages.SizeKB)
}

if hugepages.FreeAmount < 0 || hugepages.TotalAmount < 0 {
t.Fatalf("node %d malformed values: free=%d total=%d", node.ID, hugepages.FreeAmount, hugepages.TotalAmount)
}

if hugepages.TotalAmount > 0 && hugepages.FreeAmount > hugepages.TotalAmount {
t.Fatalf("node %d inconsistent values: free=%d total=%d", node.ID, hugepages.FreeAmount, hugepages.TotalAmount)
}
}
}
}

func isSupportedHugepageSize(sizeKB int) bool {
if sizeKB == 2*1024 {
return true
}
if sizeKB == 1*1024*1024 {
return true
}
return false
}
Binary file not shown.

0 comments on commit 0ecf508

Please sign in to comment.