@@ -26,6 +26,24 @@ import (
2626 "github.com/prometheus/procfs/internal/util"
2727)
2828
29+ // PciPowerState represents the power state of a PCI device.
30+ type PciPowerState string
31+
32+ const (
33+ PciPowerStateUnknown PciPowerState = "unknown"
34+ PciPowerStateError PciPowerState = "error"
35+ PciPowerStateD0 PciPowerState = "D0"
36+ PciPowerStateD1 PciPowerState = "D1"
37+ PciPowerStateD2 PciPowerState = "D2"
38+ PciPowerStateD3Hot PciPowerState = "D3hot"
39+ PciPowerStateD3Cold PciPowerState = "D3cold"
40+ )
41+
42+ // String returns the string representation of the power state.
43+ func (p PciPowerState ) String () string {
44+ return string (p )
45+ }
46+
2947const pciDevicesPath = "bus/pci/devices"
3048
3149// PciDeviceLocation represents the location of the device attached.
@@ -63,10 +81,23 @@ type PciDevice struct {
6381 SubsystemDevice uint32 // /sys/bus/pci/devices/<Location>/subsystem_device
6482 Revision uint32 // /sys/bus/pci/devices/<Location>/revision
6583
84+ NumaNode * int32 // /sys/bus/pci/devices/<Location>/numa_node
85+
6686 MaxLinkSpeed * float64 // /sys/bus/pci/devices/<Location>/max_link_speed
6787 MaxLinkWidth * float64 // /sys/bus/pci/devices/<Location>/max_link_width
6888 CurrentLinkSpeed * float64 // /sys/bus/pci/devices/<Location>/current_link_speed
6989 CurrentLinkWidth * float64 // /sys/bus/pci/devices/<Location>/current_link_width
90+
91+ SriovDriversAutoprobe * bool // /sys/bus/pci/devices/<Location>/sriov_drivers_autoprobe
92+ SriovNumvfs * uint32 // /sys/bus/pci/devices/<Location>/sriov_numvfs
93+ SriovOffset * uint32 // /sys/bus/pci/devices/<Location>/sriov_offset
94+ SriovStride * uint32 // /sys/bus/pci/devices/<Location>/sriov_stride
95+ SriovTotalvfs * uint32 // /sys/bus/pci/devices/<Location>/sriov_totalvfs
96+ SriovVfDevice * uint32 // /sys/bus/pci/devices/<Location>/sriov_vf_device
97+ SriovVfTotalMsix * uint64 // /sys/bus/pci/devices/<Location>/sriov_vf_total_msix
98+
99+ D3coldAllowed * bool // /sys/bus/pci/devices/<Location>/d3cold_allowed
100+ PowerState * PciPowerState // /sys/bus/pci/devices/<Location>/power_state
70101}
71102
72103func (pd PciDevice ) Name () string {
@@ -204,7 +235,7 @@ func (fs FS) parsePciDevice(name string) (*PciDevice, error) {
204235 }
205236 }
206237
207- for _ , f := range [... ]string {"max_link_speed" , "max_link_width" , "current_link_speed" , "current_link_width" } {
238+ for _ , f := range [... ]string {"max_link_speed" , "max_link_width" , "current_link_speed" , "current_link_width" , "numa_node" } {
208239 name := filepath .Join (path , f )
209240 valueStr , err := util .SysReadFile (name )
210241 if err != nil {
@@ -254,6 +285,123 @@ func (fs FS) parsePciDevice(name string) (*PciDevice, error) {
254285 case "current_link_width" :
255286 device .CurrentLinkWidth = & v
256287 }
288+
289+ case "numa_node" :
290+ value , err := strconv .ParseInt (valueStr , 10 , 32 )
291+ if err != nil {
292+ return nil , fmt .Errorf ("failed to parse %s %q: %w" , f , valueStr , err )
293+ }
294+ v := int32 (value )
295+ device .NumaNode = & v
296+ }
297+ }
298+
299+ // Parse SR-IOV files (these are optional and may not exist for all devices)
300+ for _ , f := range [... ]string {"sriov_drivers_autoprobe" , "sriov_numvfs" , "sriov_offset" , "sriov_stride" , "sriov_totalvfs" , "sriov_vf_device" , "sriov_vf_total_msix" } {
301+ name := filepath .Join (path , f )
302+ valueStr , err := util .SysReadFile (name )
303+ if err != nil {
304+ if os .IsNotExist (err ) {
305+ continue // SR-IOV files are optional
306+ }
307+ return nil , fmt .Errorf ("failed to read SR-IOV file %q: %w" , name , err )
308+ }
309+
310+ valueStr = strings .TrimSpace (valueStr )
311+ if valueStr == "" {
312+ continue
313+ }
314+
315+ switch f {
316+ case "sriov_drivers_autoprobe" :
317+ // sriov_drivers_autoprobe is a boolean (0 or 1)
318+ value , err := strconv .ParseInt (valueStr , 10 , 32 )
319+ if err != nil {
320+ return nil , fmt .Errorf ("failed to parse SR-IOV boolean %q: %w" , valueStr , err )
321+ }
322+ v := value != 0
323+ device .SriovDriversAutoprobe = & v
324+
325+ case "sriov_numvfs" :
326+ value , err := strconv .ParseUint (valueStr , 10 , 32 )
327+ if err != nil {
328+ return nil , fmt .Errorf ("failed to parse SR-IOV integer %q: %w" , valueStr , err )
329+ }
330+ v := uint32 (value )
331+ device .SriovNumvfs = & v
332+
333+ case "sriov_offset" :
334+ value , err := strconv .ParseUint (valueStr , 10 , 32 )
335+ if err != nil {
336+ return nil , fmt .Errorf ("failed to parse SR-IOV integer %q: %w" , valueStr , err )
337+ }
338+ v := uint32 (value )
339+ device .SriovOffset = & v
340+
341+ case "sriov_stride" :
342+ value , err := strconv .ParseUint (valueStr , 10 , 32 )
343+ if err != nil {
344+ return nil , fmt .Errorf ("failed to parse SR-IOV integer %q: %w" , valueStr , err )
345+ }
346+ v := uint32 (value )
347+ device .SriovStride = & v
348+
349+ case "sriov_totalvfs" :
350+ value , err := strconv .ParseUint (valueStr , 10 , 32 )
351+ if err != nil {
352+ return nil , fmt .Errorf ("failed to parse SR-IOV integer %q: %w" , valueStr , err )
353+ }
354+ v := uint32 (value )
355+ device .SriovTotalvfs = & v
356+
357+ case "sriov_vf_device" :
358+ value , err := strconv .ParseUint (valueStr , 10 , 32 )
359+ if err != nil {
360+ return nil , fmt .Errorf ("failed to parse SR-IOV integer %q: %w" , valueStr , err )
361+ }
362+ v := uint32 (value )
363+ device .SriovVfDevice = & v
364+
365+ case "sriov_vf_total_msix" :
366+ value , err := strconv .ParseUint (valueStr , 10 , 64 )
367+ if err != nil {
368+ return nil , fmt .Errorf ("failed to parse SR-IOV integer %q: %w" , valueStr , err )
369+ }
370+ v := uint64 (value )
371+ device .SriovVfTotalMsix = & v
372+ }
373+ }
374+
375+ // Parse power management files (these are optional and may not exist for all devices)
376+ for _ , f := range [... ]string {"d3cold_allowed" , "power_state" } {
377+ name := filepath .Join (path , f )
378+ valueStr , err := util .SysReadFile (name )
379+ if err != nil {
380+ if os .IsNotExist (err ) {
381+ continue // Power management files are optional
382+ }
383+ return nil , fmt .Errorf ("failed to read power management file %q: %w" , name , err )
384+ }
385+
386+ valueStr = strings .TrimSpace (valueStr )
387+ if valueStr == "" {
388+ continue
389+ }
390+
391+ switch f {
392+ case "d3cold_allowed" :
393+ // d3cold_allowed is a boolean (0 or 1)
394+ value , err := strconv .ParseInt (valueStr , 10 , 32 )
395+ if err != nil {
396+ return nil , fmt .Errorf ("failed to parse d3cold_allowed boolean %q: %w" , valueStr , err )
397+ }
398+ v := value != 0
399+ device .D3coldAllowed = & v
400+
401+ case "power_state" :
402+ // power_state is a string (one of: "unknown", "error", "D0", "D1", "D2", "D3hot", "D3cold")
403+ powerState := PciPowerState (valueStr )
404+ device .PowerState = & powerState
257405 }
258406 }
259407
0 commit comments