Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 25 additions & 12 deletions sysfs/net_class_aer.go
Original file line number Diff line number Diff line change
Expand Up @@ -133,13 +133,19 @@ func parseAerCounters(devicePath string) (*AerCounters, error) {
// parseCorrectableAerCounters parses correctable error counters in
// /sys/class/net/<iface>/device/aer_dev_correctable.
func parseCorrectableAerCounters(devicePath string, counters *CorrectableAerCounters) error {
path := filepath.Join(devicePath, "device", "aer_dev_correctable")
_, err := parseCorrectableAerFile(filepath.Join(devicePath, "device", "aer_dev_correctable"), counters)
return err
}

// parseCorrectableAerFile parses a correctable AER counters file such as
// aer_dev_correctable. It reports whether the file was present and readable.
func parseCorrectableAerFile(path string, counters *CorrectableAerCounters) (bool, error) {
value, err := parsers.SysReadFile(path)
if err != nil {
if canIgnoreError(err) {
return nil
return false, nil
}
return fmt.Errorf("failed to read file %q: %w", path, err)
return false, fmt.Errorf("failed to read file %q: %w", path, err)
}

for line := range strings.SplitSeq(string(value), "\n") {
Expand All @@ -148,12 +154,12 @@ func parseCorrectableAerCounters(devicePath string, counters *CorrectableAerCoun
}
fields := strings.Fields(line)
if len(fields) != 2 {
return fmt.Errorf("unexpected number of fields: %v", fields)
return false, fmt.Errorf("unexpected number of fields: %v", fields)
}
counterName := fields[0]
value, err := strconv.ParseUint(fields[1], 10, 64)
if err != nil {
return fmt.Errorf("error parsing value for %s: %w", counterName, err)
return false, fmt.Errorf("error parsing value for %s: %w", counterName, err)
}

switch counterName {
Expand All @@ -178,21 +184,28 @@ func parseCorrectableAerCounters(devicePath string, counters *CorrectableAerCoun
}
}

return nil
return true, nil
}

// parseUncorrectableAerCounters parses uncorrectable error counters in
// /sys/class/net/<iface>/device/aer_dev_[non]fatal.
func parseUncorrectableAerCounters(devicePath, counterType string,
counters *UncorrectableAerCounters,
) error {
path := filepath.Join(devicePath, "device", "aer_dev_"+counterType)
_, err := parseUncorrectableAerFile(filepath.Join(devicePath, "device", "aer_dev_"+counterType), counters)
return err
}

// parseUncorrectableAerFile parses an uncorrectable AER counters file such as
// aer_dev_fatal or aer_dev_nonfatal. It reports whether the file was present
// and readable.
func parseUncorrectableAerFile(path string, counters *UncorrectableAerCounters) (bool, error) {
value, err := parsers.ReadFileNoStat(path)
if err != nil {
if canIgnoreError(err) {
return nil
return false, nil
}
return fmt.Errorf("failed to read file %q: %w", path, err)
return false, fmt.Errorf("failed to read file %q: %w", path, err)
}

for line := range strings.SplitSeq(string(value), "\n") {
Expand All @@ -201,12 +214,12 @@ func parseUncorrectableAerCounters(devicePath, counterType string,
}
fields := strings.Fields(line)
if len(fields) != 2 {
return fmt.Errorf("unexpected number of fields: %v", fields)
return false, fmt.Errorf("unexpected number of fields: %v", fields)
}
counterName := fields[0]
value, err := strconv.ParseUint(fields[1], 10, 64)
if err != nil {
return fmt.Errorf("error parsing value for %s: %w", counterName, err)
return false, fmt.Errorf("error parsing value for %s: %w", counterName, err)
}

switch counterName {
Expand Down Expand Up @@ -251,5 +264,5 @@ func parseUncorrectableAerCounters(devicePath, counterType string,
}
}

return nil
return true, nil
}
7 changes: 6 additions & 1 deletion sysfs/net_class_aer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,12 @@ func TestAerCounters(t *testing.T) {

ac, _ := fs.AerCounters()
aerCounters := AllAerCounters{
"enp3s0f0": AerCounters{Name: "enp3s0f0"},
"enp3s0f0": AerCounters{
Name: "enp3s0f0",
Correctable: CorrectableAerCounters{RxErr: 2, BadTLP: 1, Timeout: 3},
Fatal: UncorrectableAerCounters{MalfTLP: 1},
NonFatal: UncorrectableAerCounters{CmpltTO: 1, UnsupReq: 4},
},
"eth0": AerCounters{
Name: "eth0",
Correctable: CorrectableAerCounters{
Expand Down
69 changes: 69 additions & 0 deletions sysfs/pci_device.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,18 @@ type PciDevice struct {

D3coldAllowed *bool // /sys/bus/pci/devices/<Location>/d3cold_allowed
PowerState *PciPowerState // /sys/bus/pci/devices/<Location>/power_state

// PCIe Advanced Error Reporting counters. These are nil when the kernel
// does not expose AER statistics for the device.
AerCorrectable *CorrectableAerCounters // /sys/bus/pci/devices/<Location>/aer_dev_correctable
AerFatal *UncorrectableAerCounters // /sys/bus/pci/devices/<Location>/aer_dev_fatal
AerNonFatal *UncorrectableAerCounters // /sys/bus/pci/devices/<Location>/aer_dev_nonfatal

// Totals of the errors a PCIe Root Port has received from the devices
// below it. These are nil for devices that are not AER-capable Root Ports.
AerRootPortTotalErrCor *uint64 // /sys/bus/pci/devices/<Location>/aer_rootport_total_err_cor
AerRootPortTotalErrFatal *uint64 // /sys/bus/pci/devices/<Location>/aer_rootport_total_err_fatal
AerRootPortTotalErrNonFatal *uint64 // /sys/bus/pci/devices/<Location>/aer_rootport_total_err_nonfatal
}

func (pd PciDevice) Name() string {
Expand Down Expand Up @@ -417,5 +429,62 @@ func (fs FS) parsePciDevice(name string) (*PciDevice, error) {
}
}

// Parse AER counters (optional: only present on PCIe devices with AER
// support and kernels built with CONFIG_PCIEAER).
var correctable CorrectableAerCounters
found, err := parseCorrectableAerFile(filepath.Join(path, "aer_dev_correctable"), &correctable)
if err != nil {
return nil, fmt.Errorf("failed to parse AER counters %s: %w", device.Location, err)
}
if found {
device.AerCorrectable = &correctable
}
for _, f := range [...]string{"aer_dev_fatal", "aer_dev_nonfatal"} {
var uncorrectable UncorrectableAerCounters
found, err := parseUncorrectableAerFile(filepath.Join(path, f), &uncorrectable)
if err != nil {
return nil, fmt.Errorf("failed to parse AER counters %s: %w", device.Location, err)
}
if !found {
continue
}
switch f {
case "aer_dev_fatal":
device.AerFatal = &uncorrectable
case "aer_dev_nonfatal":
device.AerNonFatal = &uncorrectable
}
}

// Parse Root Port AER totals (optional: only present on Root Ports).
for _, f := range [...]string{"aer_rootport_total_err_cor", "aer_rootport_total_err_fatal", "aer_rootport_total_err_nonfatal"} {
name := filepath.Join(path, f)
valueStr, err := parsers.SysReadFile(name)
if err != nil {
if os.IsNotExist(err) {
continue
}
return nil, fmt.Errorf("failed to read AER Root Port file %q %s: %w", name, device.Location, err)
}

valueStr = strings.TrimSpace(valueStr)
if valueStr == "" {
continue
}
value, err := strconv.ParseUint(valueStr, 10, 64)
if err != nil {
return nil, fmt.Errorf("failed to parse %s %q %s: %w", f, valueStr, device.Location, err)
}

switch f {
case "aer_rootport_total_err_cor":
device.AerRootPortTotalErrCor = &value
case "aer_rootport_total_err_fatal":
device.AerRootPortTotalErrFatal = &value
case "aer_rootport_total_err_nonfatal":
device.AerRootPortTotalErrNonFatal = &value
}
}

return device, nil
}
23 changes: 19 additions & 4 deletions sysfs/pci_device_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,11 @@ func TestPciDevices(t *testing.T) {
NumaNodeNeg1 = int32(-1)
D3coldAllowed = true
PowerState = PciPowerStateD0

// Root Port AER totals on 0000:00:02.1
RootPortErrCor = uint64(7)
RootPortErrFatal = uint64(0)
RootPortErrNonFatal = uint64(2)
)
want := PciDevices{
"0000:00:02:1": PciDevice{
Expand All @@ -78,6 +83,10 @@ func TestPciDevices(t *testing.T) {

D3coldAllowed: &D3coldAllowed,
PowerState: &PowerState,

AerRootPortTotalErrCor: &RootPortErrCor,
AerRootPortTotalErrFatal: &RootPortErrFatal,
AerRootPortTotalErrNonFatal: &RootPortErrNonFatal,
},
"0000:01:00:0": PciDevice{
Location: PciDeviceLocation{
Expand Down Expand Up @@ -106,8 +115,11 @@ func TestPciDevices(t *testing.T) {
CurrentLinkSpeed: &LinkSpeed8GTs,
CurrentLinkWidth: &LinkWidth4,

D3coldAllowed: &D3coldAllowed,
PowerState: &PowerState,
D3coldAllowed: &D3coldAllowed,
PowerState: &PowerState,
AerCorrectable: &CorrectableAerCounters{},
AerFatal: &UncorrectableAerCounters{},
AerNonFatal: &UncorrectableAerCounters{},
},
"0000:a2:00:0": PciDevice{
Location: PciDeviceLocation{
Expand Down Expand Up @@ -141,8 +153,11 @@ func TestPciDevices(t *testing.T) {
SriovVfTotalMsix: &SriovVfTotalMsix,

// Power management fields
D3coldAllowed: &D3coldAllowed,
PowerState: &PowerState,
D3coldAllowed: &D3coldAllowed,
PowerState: &PowerState,
AerCorrectable: &CorrectableAerCounters{RxErr: 2, BadTLP: 1, Timeout: 3},
AerFatal: &UncorrectableAerCounters{MalfTLP: 1},
AerNonFatal: &UncorrectableAerCounters{CmpltTO: 1, UnsupReq: 4},
},
}

Expand Down
33 changes: 24 additions & 9 deletions testdata/fixtures.ttar
Original file line number Diff line number Diff line change
Expand Up @@ -10755,6 +10755,21 @@ Lines: 1
0xc0a9
Mode: 444
# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Path: fixtures/sys/devices/pci0000:00/0000:00:02.1/aer_rootport_total_err_cor
Lines: 1
7
Mode: 444
# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Path: fixtures/sys/devices/pci0000:00/0000:00:02.1/aer_rootport_total_err_fatal
Lines: 1
0
Mode: 444
# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Path: fixtures/sys/devices/pci0000:00/0000:00:02.1/aer_rootport_total_err_nonfatal
Lines: 1
2
Mode: 444
# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Path: fixtures/sys/devices/pci0000:00/0000:00:02.1/ari_enabled
Lines: 1
0
Expand Down Expand Up @@ -17338,15 +17353,15 @@ Mode: 755
# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Path: fixtures/sys/devices/pci0000:a2/0000:a2:00.0/aer_dev_correctable
Lines: 9
RxErr 0
BadTLP 0
RxErr 2
BadTLP 1
BadDLLP 0
Rollover 0
Timeout 0
Timeout 3
NonFatalErr 0
CorrIntErr 0
HeaderOF 0
TOTAL_ERR_COR 0
TOTAL_ERR_COR 6
Mode: 444
# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Path: fixtures/sys/devices/pci0000:a2/0000:a2:00.0/aer_dev_fatal
Expand All @@ -17360,7 +17375,7 @@ CmpltTO 0
CmpltAbrt 0
UnxCmplt 0
RxOF 0
MalfTLP 0
MalfTLP 1
ECRC 0
UnsupReq 0
ACSViol 0
Expand All @@ -17369,7 +17384,7 @@ BlockedTLP 0
AtomicOpBlocked 0
TLPBlockedErr 0
PoisonTLPBlocked 0
TOTAL_ERR_FATAL 0
TOTAL_ERR_FATAL 1
Mode: 444
# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Path: fixtures/sys/devices/pci0000:a2/0000:a2:00.0/aer_dev_nonfatal
Expand All @@ -17379,20 +17394,20 @@ DLP 0
SDES 0
TLP 0
FCP 0
CmpltTO 0
CmpltTO 1
CmpltAbrt 0
UnxCmplt 0
RxOF 0
MalfTLP 0
ECRC 0
UnsupReq 0
UnsupReq 4
ACSViol 0
UncorrIntErr 0
BlockedTLP 0
AtomicOpBlocked 0
TLPBlockedErr 0
PoisonTLPBlocked 0
TOTAL_ERR_NONFATAL 0
TOTAL_ERR_NONFATAL 5
Mode: 444
# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Path: fixtures/sys/devices/pci0000:a2/0000:a2:00.0/ari_enabled
Expand Down
Loading