about summary refs log tree commit diff
path: root/tools/numap/numa.go
blob: 402ea1d0511db648747e55995789df1bd1e19ef6 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
package main

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

	"golang.fcuny.net/numap/internal/hwids"
	"golang.fcuny.net/numap/internal/sysfs"
)

const (
	node_root      = "/sys/devices/system/node/node*"
	CLASS_NVMe     = 67586
	CLASS_ETHERNET = 131072
	CLASS_GPU      = 197120
)

type node struct {
	Name       string      `json:"name"`
	Path       string      `json:"path"`
	CpuList    string      `json:"cpulist"`
	PCIDevices []PCIDevice `json:"pci_devices"`
}

type PCIDevice struct {
	Vendor string `json:"vendor"`
	Name   string `json:"name"`
}

func findNodes(hwdb hwids.PciDevices) (map[string]node, error) {
	nodes := make(map[string]node)

	files, err := filepath.Glob(node_root)
	if err != nil {
		return nil, fmt.Errorf("Failed to find NUMA nodes under %s: %+v", node_root, err)
	}
	if len(files) == 0 {
		return nil, fmt.Errorf("Could not find NUMA node in %s", node_root)
	}

	for _, f := range files {
		n, err := newNode(f)
		if err != nil {
			return make(map[string]node), err
		}
		nodes[n.Name] = n
	}

	r, err := mapPCIDevicesToNumaNode(hwdb)
	if err != nil {
		panic(err)
	}
	for k, v := range r {
		nodeName := fmt.Sprintf("node%d", k)
		n := nodes[nodeName]
		n.PCIDevices = v
		nodes[nodeName] = n
	}
	return nodes, nil
}

func mapPCIDevicesToNumaNode(hwdb hwids.PciDevices) (map[int][]PCIDevice, error) {
	devices := sysfs.ScanPCIDevices()
	r := map[int][]PCIDevice{}

	for _, d := range devices {
		if d.Class == CLASS_NVMe || d.Class == CLASS_ETHERNET || d.Class == CLASS_GPU {
			_, ok := hwdb[uint16(d.Vendor)]
			if ok {
				desc := hwdb[uint16(d.Vendor)]
				var vendor, name string
				for _, m := range desc {
					if uint64(m.Device) == d.Device && uint64(m.Vendor) == d.Vendor {
						vendor = m.VendorName
						name = m.DeviceName
						break
					}
				}
				pciDevice := PCIDevice{
					Vendor: vendor,
					Name:   name,
				}
				r[d.NumaNode] = append(r[d.NumaNode], pciDevice)
			}
		}
	}
	return r, nil
}

func newNode(p string) (node, error) {
	_, name := path.Split(p)

	cpulist, err := cpuList(p)
	if err != nil {
		return node{}, err
	}

	return node{
		Name:       name,
		Path:       p,
		CpuList:    cpulist,
		PCIDevices: []PCIDevice{},
	}, nil
}

func cpuList(p string) (string, error) {
	lpath := filepath.Join(p, "cpulist")
	c, err := ioutil.ReadFile(lpath)
	if err != nil {
		return "", fmt.Errorf("Failed to open %s: %+v", lpath, err)
	}
	return strings.TrimRight(string(c), "\n"), nil
}