github.com/jaypipes/ghw@v0.21.1/pkg/topology/topology_linux.go (about) 1 // Use and distribution licensed under the Apache license version 2. 2 // 3 // See the COPYING file in the root project directory for full text. 4 // 5 6 package topology 7 8 import ( 9 "fmt" 10 "os" 11 "path/filepath" 12 "strconv" 13 "strings" 14 15 "github.com/jaypipes/ghw/pkg/context" 16 "github.com/jaypipes/ghw/pkg/cpu" 17 "github.com/jaypipes/ghw/pkg/linuxpath" 18 "github.com/jaypipes/ghw/pkg/memory" 19 ) 20 21 func (i *Info) load() error { 22 i.Nodes = topologyNodes(i.ctx) 23 if len(i.Nodes) == 1 { 24 i.Architecture = ArchitectureSMP 25 } else { 26 i.Architecture = ArchitectureNUMA 27 } 28 return nil 29 } 30 31 func topologyNodes(ctx *context.Context) []*Node { 32 paths := linuxpath.New(ctx) 33 nodes := make([]*Node, 0) 34 35 files, err := os.ReadDir(paths.SysDevicesSystemNode) 36 if err != nil { 37 ctx.Warn("failed to determine nodes: %s\n", err) 38 return nodes 39 } 40 for _, file := range files { 41 filename := file.Name() 42 if !strings.HasPrefix(filename, "node") { 43 continue 44 } 45 node := &Node{} 46 nodeID, err := strconv.Atoi(filename[4:]) 47 if err != nil { 48 ctx.Warn("failed to determine node ID: %s\n", err) 49 return nodes 50 } 51 node.ID = nodeID 52 cores, err := cpu.CoresForNode(ctx, nodeID) 53 if err != nil { 54 ctx.Warn("failed to determine cores for node: %s\n", err) 55 return nodes 56 } 57 node.Cores = cores 58 caches, err := memory.CachesForNode(ctx, nodeID) 59 if err != nil { 60 ctx.Warn("failed to determine caches for node: %s\n", err) 61 return nodes 62 } 63 node.Caches = caches 64 65 distances, err := distancesForNode(ctx, nodeID) 66 if err != nil { 67 ctx.Warn("failed to determine node distances for node: %s\n", err) 68 return nodes 69 } 70 node.Distances = distances 71 72 area, err := memory.AreaForNode(ctx, nodeID) 73 if err != nil { 74 ctx.Warn("failed to determine memory area for node: %s\n", err) 75 return nodes 76 } 77 node.Memory = area 78 79 nodes = append(nodes, node) 80 } 81 return nodes 82 } 83 84 func distancesForNode(ctx *context.Context, nodeID int) ([]int, error) { 85 paths := linuxpath.New(ctx) 86 path := filepath.Join( 87 paths.SysDevicesSystemNode, 88 fmt.Sprintf("node%d", nodeID), 89 "distance", 90 ) 91 92 data, err := os.ReadFile(path) 93 if err != nil { 94 return nil, err 95 } 96 97 items := strings.Fields(strings.TrimSpace(string(data))) 98 dists := make([]int, len(items)) // TODO: can a NUMA cell be offlined? 99 for idx, item := range items { 100 dist, err := strconv.Atoi(item) 101 if err != nil { 102 return dists, err 103 } 104 dists[idx] = dist 105 } 106 return dists, nil 107 }