1717package nvpci
1818
1919import (
20+ "errors"
2021 "fmt"
2122 "os"
2223 "path"
@@ -258,6 +259,28 @@ func (p *nvpci) GetNvidiaDeviceByPciBusID(address string) (*NvidiaPCIDevice, err
258259 return p .getNvidiaDeviceByPciBusID (address , nil )
259260}
260261
262+ // readPCIFieldString reads a sysfs PCI attribute file and returns its contents with any surrounding whitespaces trimmed.
263+ func readPCIFieldString (devicePath , field string ) (string , error ) {
264+ raw , err := os .ReadFile (path .Join (devicePath , field ))
265+ if err != nil {
266+ return "" , fmt .Errorf ("unable to read PCI %s for %s: %w" , field , devicePath , err )
267+ }
268+ return strings .TrimSpace (string (raw )), nil
269+ }
270+
271+ // readPCIField reads a sysfs PCI attribute file and parses it as an unsigned integer.
272+ func readPCIField (devicePath , field string , bitSize int ) (uint64 , error ) {
273+ str , err := readPCIFieldString (devicePath , field )
274+ if err != nil {
275+ return 0 , err
276+ }
277+ val , err := strconv .ParseUint (str , 0 , bitSize )
278+ if err != nil {
279+ return 0 , fmt .Errorf ("unable to parse PCI %s for %s: %w" , field , devicePath , err )
280+ }
281+ return val , nil
282+ }
283+
261284func (p * nvpci ) getNvidiaDeviceByPciBusID (address string , cache map [string ]* NvidiaPCIDevice ) (* NvidiaPCIDevice , error ) {
262285 if cache != nil {
263286 if pciDevice , exists := cache [address ]; exists {
@@ -266,68 +289,51 @@ func (p *nvpci) getNvidiaDeviceByPciBusID(address string, cache map[string]*Nvid
266289 }
267290 devicePath := filepath .Join (p .pciDevicesRoot , address )
268291
269- vendor , err := os . ReadFile ( path . Join ( devicePath , "vendor" ) )
292+ vendorID , err := readPCIField ( devicePath , "vendor" , 16 )
270293 if err != nil {
271- return nil , fmt .Errorf ("unable to read PCI device vendor id for %s: %v" , address , err )
272- }
273- vendorStr := strings .TrimSpace (string (vendor ))
274- vendorID , err := strconv .ParseUint (vendorStr , 0 , 16 )
275- if err != nil {
276- return nil , fmt .Errorf ("unable to convert vendor string to uint16: %v" , vendorStr )
294+ return nil , err
277295 }
278296
279297 if uint16 (vendorID ) != PCINvidiaVendorID && uint16 (vendorID ) != PCIMellanoxVendorID {
280298 return nil , nil
281299 }
282300
283- class , err := os . ReadFile ( path . Join ( devicePath , "class" ) )
301+ classID , err := readPCIField ( devicePath , "class" , 32 )
284302 if err != nil {
285- return nil , fmt . Errorf ( "unable to read PCI device class for %s: %v" , address , err )
303+ return nil , err
286304 }
287- classStr := strings . TrimSpace ( string ( class ))
288- classID , err := strconv . ParseUint ( classStr , 0 , 32 )
305+
306+ deviceID , err := readPCIField ( devicePath , "device" , 16 )
289307 if err != nil {
290- return nil , fmt . Errorf ( "unable to convert class string to uint32: %v" , classStr )
308+ return nil , err
291309 }
292310
293- device , err := os . ReadFile ( path . Join ( devicePath , "device" ) )
311+ numaStr , err := readPCIFieldString ( devicePath , "numa_node" )
294312 if err != nil {
295- return nil , fmt . Errorf ( "unable to read PCI device id for %s: %v" , address , err )
313+ return nil , err
296314 }
297- deviceStr := strings . TrimSpace ( string ( device ))
298- deviceID , err := strconv .ParseUint ( deviceStr , 0 , 16 )
315+ // numa_node is parsed as a signed integer since "-1" is a valid value meaning "no NUMA affinity".
316+ numaNode , err := strconv .ParseInt ( numaStr , 0 , 64 )
299317 if err != nil {
300- return nil , fmt .Errorf ("unable to convert device string to uint16 : %v " , deviceStr )
318+ return nil , fmt .Errorf ("unable to parse PCI numa_node for %s : %w " , devicePath , err )
301319 }
302320
303- var subsystemVendorID uint64
304- subsystemVendor , err := os .ReadFile (path .Join (devicePath , "subsystem_vendor" ))
305- switch {
306- case err == nil :
307- subsystemVendorStr := strings .TrimSpace (string (subsystemVendor ))
308- subsystemVendorID , err = strconv .ParseUint (subsystemVendorStr , 0 , 16 )
309- if err != nil {
310- return nil , fmt .Errorf ("unable to convert subsystem vendor string to uint16: %v" , subsystemVendorStr )
321+ // Tolerate missing subsystem files: some environments (e.g. certain virtualised or passthrough PCI topologies)
322+ // do not expose them, so the IDs will default to 0.
323+ subsystemVendorID , err := readPCIField (devicePath , "subsystem_vendor" , 16 )
324+ if err != nil {
325+ if ! errors .Is (err , os .ErrNotExist ) {
326+ return nil , err
311327 }
312- case os .IsNotExist (err ):
313328 p .logger .Warningf ("subsystem_vendor file not found for %s" , address )
314- default :
315- return nil , fmt .Errorf ("unable to read PCI subsystem vendor id for %s: %v" , address , err )
316329 }
317330
318- var subsystemDeviceID uint64
319- subsystemDevice , err := os .ReadFile (path .Join (devicePath , "subsystem_device" ))
320- switch {
321- case err == nil :
322- subsystemDeviceStr := strings .TrimSpace (string (subsystemDevice ))
323- subsystemDeviceID , err = strconv .ParseUint (subsystemDeviceStr , 0 , 16 )
324- if err != nil {
325- return nil , fmt .Errorf ("unable to convert subsystem device string to uint16: %v" , subsystemDeviceStr )
331+ subsystemDeviceID , err := readPCIField (devicePath , "subsystem_device" , 16 )
332+ if err != nil {
333+ if ! errors .Is (err , os .ErrNotExist ) {
334+ return nil , err
326335 }
327- case os .IsNotExist (err ):
328336 p .logger .Warningf ("subsystem_device file not found for %s" , address )
329- default :
330- return nil , fmt .Errorf ("unable to read PCI subsystem device id for %s: %v" , address , err )
331337 }
332338
333339 driver , err := getDriver (devicePath )
@@ -346,16 +352,6 @@ func (p *nvpci) getNvidiaDeviceByPciBusID(address string, cache map[string]*Nvid
346352 p .logger .Warningf ("unable to detect IOMMU FD for %s: %v" , address , err )
347353 }
348354
349- numa , err := os .ReadFile (path .Join (devicePath , "numa_node" ))
350- if err != nil {
351- return nil , fmt .Errorf ("unable to read PCI NUMA node for %s: %v" , address , err )
352- }
353- numaStr := strings .TrimSpace (string (numa ))
354- numaNode , err := strconv .ParseInt (numaStr , 0 , 64 )
355- if err != nil {
356- return nil , fmt .Errorf ("unable to convert NUMA node string to int64: %v" , numaNode )
357- }
358-
359355 config := & ConfigSpace {
360356 Path : path .Join (devicePath , "config" ),
361357 }
@@ -532,32 +528,20 @@ func (p *nvpci) GetGPUByIndex(i int) (*NvidiaPCIDevice, error) {
532528}
533529
534530func (p * nvpci ) getSriovInfoForPhysicalFunction (devicePath string ) (sriovInfo SriovInfo , err error ) {
535- totalVfsPath := filepath .Join (devicePath , "sriov_totalvfs" )
536- numVfsPath := filepath .Join (devicePath , "sriov_numvfs" )
537-
538531 // No file for sriov_totalvfs exists? Not an SRIOV device, return nil
539- _ , err = os .Stat (totalVfsPath )
532+ _ , err = os .Stat (filepath . Join ( devicePath , "sriov_totalvfs" ) )
540533 if err != nil && os .IsNotExist (err ) {
541534 return sriovInfo , nil
542535 }
543- sriovTotalVfs , err := os .ReadFile (totalVfsPath )
544- if err != nil {
545- return sriovInfo , fmt .Errorf ("unable to read sriov_totalvfs: %v" , err )
546- }
547- totalVfsStr := strings .TrimSpace (string (sriovTotalVfs ))
548- totalVfsInt , err := strconv .ParseUint (totalVfsStr , 10 , 16 )
549- if err != nil {
550- return sriovInfo , fmt .Errorf ("unable to convert sriov_totalvfs to uint64: %v" , err )
551- }
552536
553- sriovNumVfs , err := os . ReadFile ( numVfsPath )
537+ totalVfsInt , err := readPCIField ( devicePath , "sriov_totalvfs" , 16 )
554538 if err != nil {
555- return sriovInfo , fmt . Errorf ( "unable to read sriov_numvfs for: %v" , err )
539+ return sriovInfo , err
556540 }
557- numVfsStr := strings . TrimSpace ( string ( sriovNumVfs ))
558- numVfsInt , err := strconv . ParseUint ( numVfsStr , 10 , 16 )
541+
542+ numVfsInt , err := readPCIField ( devicePath , "sriov_numvfs" , 16 )
559543 if err != nil {
560- return sriovInfo , fmt . Errorf ( "unable to convert sriov_numvfs to uint64: %v" , err )
544+ return sriovInfo , err
561545 }
562546
563547 sriovInfo = SriovInfo {
0 commit comments