aboutsummaryrefslogtreecommitdiff
path: root/tools/numap/internal
diff options
context:
space:
mode:
Diffstat (limited to 'tools/numap/internal')
-rw-r--r--tools/numap/internal/hwids/hwids.go148
-rw-r--r--tools/numap/internal/sysfs/parse.go21
-rw-r--r--tools/numap/internal/sysfs/pci.go145
3 files changed, 314 insertions, 0 deletions
diff --git a/tools/numap/internal/hwids/hwids.go b/tools/numap/internal/hwids/hwids.go
new file mode 100644
index 0000000..6aa9d8a
--- /dev/null
+++ b/tools/numap/internal/hwids/hwids.go
@@ -0,0 +1,148 @@
+package hwids
+
+import (
+ "bufio"
+ "fmt"
+ "os"
+ "strings"
+)
+
+var pciPath = []string{
+ "/usr/share/hwdata/pci.ids",
+ "/usr/share/misc/pci.ids",
+}
+
+type PCIType int
+
+const (
+ PCIVendor PCIType = iota
+ PCIDevice
+ PCISubsystem
+)
+
+type PciDevices map[uint16][]PciDevice
+
+// PciDevice represents a PCI device
+type PciDevice struct {
+ Type PCIType
+ Vendor, Device uint16
+ SubVendor, SubDevice uint16
+ VendorName, DeviceName string
+ SubName string
+}
+
+// Load load the hardware database for PCI devices and return a map of
+// vendor -> list of devices.
+func Load() (PciDevices, error) {
+ // if the environment variable HWDATAPATH is set, we add it to the
+ // list of paths we check for the hardware database.
+ extraPath := os.Getenv("HWDATA")
+ if extraPath != "" {
+ pciPath = append(pciPath, extraPath)
+ }
+
+ for _, f := range pciPath {
+ fh, err := os.Open(f)
+ if err != nil {
+ continue
+ }
+ defer fh.Close()
+ return parse(fh)
+ }
+ return PciDevices{}, fmt.Errorf("hwids: could not find a pci.ids file")
+}
+
+func parse(f *os.File) (PciDevices, error) {
+ devices := make(PciDevices)
+
+ s := bufio.NewScanner(f)
+
+ // this is to keep track of the current device. The format of the
+ // file is as follow:
+ // vendor vendor_name
+ // device device_name <-- single tab
+ // subvendor subdevice subsystem_name <-- two tabs
+ // the variable is to keep track of the current vendor / device
+ cur := PciDevice{}
+
+ for s.Scan() {
+ l := s.Text()
+ // skip empty lines or lines that are a comment
+ if len(l) == 0 || l[0] == '#' {
+ continue
+ }
+ // lines starting with a C are the classes definitions, and
+ // they are at the end of the file, which means we're done
+ // parsing the devices
+ if l[0] == 'C' {
+ break
+ }
+
+ parts := strings.SplitN(l, " ", 2)
+ if len(parts) != 2 {
+ return devices, fmt.Errorf("hwids: malformed PCI ID line (missing ID separator): %s", l)
+ }
+
+ ids, name := parts[0], parts[1]
+ if len(ids) < 2 || len(name) == 0 {
+ return devices, fmt.Errorf("hwids: malformed PCI ID line (empty ID or name): %s", l)
+ }
+
+ cur.Type = PCIVendor
+
+ if ids[0] == '\t' {
+ if ids[1] == '\t' {
+ cur.Type = PCISubsystem
+ } else {
+ cur.Type = PCIDevice
+ }
+ }
+
+ var err error
+ switch cur.Type {
+ case PCIVendor:
+ _, err = fmt.Sscanf(ids, "%x", &cur.Vendor)
+ cur.VendorName = name
+ case PCIDevice:
+ _, err = fmt.Sscanf(ids, "%x", &cur.Device)
+ cur.DeviceName = name
+ case PCISubsystem:
+ _, err = fmt.Sscanf(ids, "%x %x", &cur.SubVendor, &cur.SubDevice)
+ cur.SubName = name
+ }
+
+ if err != nil {
+ return devices, fmt.Errorf("hwids: malformed PCI ID line: %s: %v", l, err)
+ }
+
+ // This is to reset the state when we are moving to a
+ // different vendor or device
+ switch cur.Type {
+ case PCIVendor:
+ cur.Device = 0
+ cur.DeviceName = ""
+ fallthrough
+ case PCIDevice:
+ cur.SubVendor = 0
+ cur.SubDevice = 0
+ cur.SubName = ""
+ }
+
+ _, ok := devices[cur.Vendor]
+ if ok {
+ _devices := devices[cur.Vendor]
+ _devices = append(_devices, cur)
+ devices[cur.Vendor] = _devices
+
+ } else {
+ _devices := []PciDevice{cur}
+ devices[cur.Vendor] = _devices
+ }
+ }
+
+ if err := s.Err(); err != nil {
+ return devices, fmt.Errorf("hwids: failed to read PCI ID line: %v", err)
+ }
+
+ return devices, nil
+}
diff --git a/tools/numap/internal/sysfs/parse.go b/tools/numap/internal/sysfs/parse.go
new file mode 100644
index 0000000..d518653
--- /dev/null
+++ b/tools/numap/internal/sysfs/parse.go
@@ -0,0 +1,21 @@
+package sysfs
+
+import (
+ "io/ioutil"
+ "strconv"
+ "strings"
+)
+
+// ContentUint64 parses the content of a file in sysfs, and convert
+// from hex to uint64.
+func ContentUint64(path string) (uint64, error) {
+ content, err := ioutil.ReadFile(path)
+ if err != nil {
+ return 0, err
+ }
+ result, err := strconv.ParseUint(strings.TrimSpace(string(content)), 0, 64)
+ if err != nil {
+ return 0, err
+ }
+ return result, nil
+}
diff --git a/tools/numap/internal/sysfs/pci.go b/tools/numap/internal/sysfs/pci.go
new file mode 100644
index 0000000..9e714b1
--- /dev/null
+++ b/tools/numap/internal/sysfs/pci.go
@@ -0,0 +1,145 @@
+package sysfs
+
+import (
+ "fmt"
+ "io/ioutil"
+ "path"
+ "path/filepath"
+ "strconv"
+ "strings"
+)
+
+const (
+ sysFsPCIDevicesPath = "/sys/bus/pci/devices/"
+)
+
+type PCIDevice struct {
+ NumaNode int
+ ID string
+ Device, Vendor uint64
+ SubVendor, SubDevice uint64
+ Class uint64
+ MSIs []int
+}
+
+func ScanPCIDevices() []PCIDevice {
+ devices, err := ioutil.ReadDir(sysFsPCIDevicesPath)
+ if err != nil {
+ panic(err)
+ }
+
+ pciDevices := []PCIDevice{}
+
+ for _, device := range devices {
+ dpath := filepath.Join(sysFsPCIDevicesPath, device.Name())
+ pcid, err := NewPCIDevice(dpath, device.Name())
+ if err != nil {
+ panic(err)
+ }
+ pciDevices = append(pciDevices, pcid)
+ }
+ return pciDevices
+}
+
+func getPCIDeviceClass(path string) (uint64, error) {
+ return ContentUint64(filepath.Join(path, "class"))
+}
+
+func getPCIDeviceVendor(path string) (uint64, error) {
+ return ContentUint64(filepath.Join(path, "vendor"))
+}
+
+func getPCIDeviceId(path string) (uint64, error) {
+ return ContentUint64(filepath.Join(path, "device"))
+}
+
+func getPCIDeviceSubsystemDevice(path string) (uint64, error) {
+ return ContentUint64(filepath.Join(path, "subsystem_device"))
+}
+
+func getPCIDeviceSubsystemVendor(path string) (uint64, error) {
+ return ContentUint64(filepath.Join(path, "subsystem_vendor"))
+}
+
+func getPCIDeviceNumaNode(path string) int {
+ content, err := ioutil.ReadFile(filepath.Join(path, "numa_node"))
+ if err != nil {
+ panic(err)
+ }
+ nodeNum, err := strconv.Atoi(strings.TrimSpace(string(content)))
+ if err != nil {
+ panic(err)
+ }
+ return nodeNum
+}
+
+func getPCIDeviceMSIx(p string) []int {
+ g := fmt.Sprintf("%s/*", filepath.Join(p, "msi_irqs"))
+ files, err := filepath.Glob(g)
+ if err != nil {
+ panic(err)
+ }
+ if len(files) == 0 {
+ return []int{}
+ }
+
+ msix := []int{}
+
+ for _, f := range files {
+ content, err := ioutil.ReadFile(f)
+ if err != nil {
+ panic(err)
+ }
+ if strings.TrimSpace(string(content)) == "msix" {
+ base := path.Base(f)
+ v, err := strconv.Atoi(base)
+ if err != nil {
+ panic(err)
+ }
+ msix = append(msix, v)
+ }
+ }
+ return msix
+}
+
+func NewPCIDevice(path, name string) (PCIDevice, error) {
+ nodeNum := getPCIDeviceNumaNode(path)
+
+ device, err := getPCIDeviceId(path)
+ if err != nil {
+ return PCIDevice{}, err
+ }
+
+ vendor, err := getPCIDeviceVendor(path)
+ if err != nil {
+ return PCIDevice{}, err
+ }
+
+ subvendor, err := getPCIDeviceSubsystemVendor(path)
+ if err != nil {
+ return PCIDevice{}, err
+ }
+
+ subdevice, err := getPCIDeviceSubsystemDevice(path)
+ if err != nil {
+ return PCIDevice{}, err
+ }
+
+ deviceClass, err := getPCIDeviceClass(path)
+ if err != nil {
+ return PCIDevice{}, err
+ }
+
+ msix := getPCIDeviceMSIx(path)
+
+ return PCIDevice{
+ ID: name,
+ Device: device,
+ Class: deviceClass,
+ NumaNode: nodeNum,
+ Vendor: vendor,
+ SubVendor: subvendor,
+ SubDevice: subdevice,
+ MSIs: msix,
+ }, nil
+}