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
}