@@ -19,36 +19,124 @@ package types
1919import (
2020 "fmt"
2121 "strconv"
22+ "strings"
2223)
2324
2425// DeviceID represents a GPU Device ID as read from a GPUs PCIe config space.
25- type DeviceID uint32
26+ type DeviceID struct {
27+ Device uint16
28+ Vendor uint16
29+ SubsystemDevice uint16
30+ SubsystemVendor uint16
31+ HasSubsystem bool
32+ }
2633
2734// NewDeviceID constructs a new 'DeviceID' from the device and vendor values pulled from a GPUs PCIe config space.
2835func NewDeviceID (device , vendor uint16 ) DeviceID {
29- return DeviceID ((uint32 (device ) << 16 ) | uint32 (vendor ))
36+ return DeviceID {
37+ Device : device ,
38+ Vendor : vendor ,
39+ }
40+ }
41+
42+ // NewDeviceIDWithSubsystem constructs a new 'DeviceID' with subsystem values.
43+ func NewDeviceIDWithSubsystem (device , vendor , subDevice , subVendor uint16 ) DeviceID {
44+ return DeviceID {
45+ Device : device ,
46+ Vendor : vendor ,
47+ SubsystemDevice : subDevice ,
48+ SubsystemVendor : subVendor ,
49+ HasSubsystem : true ,
50+ }
51+ }
52+
53+ // NewDeviceIDFromPacked constructs a 'DeviceID' from a combined 32-bit device+vendor
54+ // value, as reported in a GPUs PCIe config space or by NVML.
55+ func NewDeviceIDFromPacked (packed uint32 ) DeviceID {
56+ device , vendor := splitRawDeviceID (uint64 (packed ))
57+ return NewDeviceID (device , vendor )
3058}
3159
3260// NewDeviceIDFromString constructs a 'DeviceID' from its string representation.
3361func NewDeviceIDFromString (str string ) (DeviceID , error ) {
34- deviceID , err := strconv .ParseInt (str , 0 , 32 )
62+ parts := strings .Split (str , ":" )
63+ if len (parts ) > 2 {
64+ return DeviceID {}, fmt .Errorf (
65+ "invalid DeviceID format '%v': expected '<devicevendor>' or '<devicevendor>:<subdevicevendor>'" ,
66+ str ,
67+ )
68+ }
69+
70+ deviceIDRaw , err := strconv .ParseUint (parts [0 ], 0 , 32 )
3571 if err != nil {
36- return 0 , fmt .Errorf ("unable to create DeviceID from string '%v': %v" , str , err )
72+ return DeviceID {}, fmt .Errorf ("unable to create DeviceID from string '%v': %v" , str , err )
73+ }
74+
75+ device , vendor := splitRawDeviceID (deviceIDRaw )
76+
77+ deviceID := DeviceID {
78+ Device : device ,
79+ Vendor : vendor ,
80+ }
81+
82+ if len (parts ) == 2 {
83+ subIDRaw , err := strconv .ParseUint (parts [1 ], 0 , 32 )
84+ if err != nil {
85+ return DeviceID {}, fmt .Errorf ("unable to create Subsystem from string '%v': %v" , str , err )
86+ }
87+
88+ subsystemDevice , subsystemVendor := splitRawDeviceID (subIDRaw )
89+
90+ deviceID .SubsystemDevice = subsystemDevice
91+ deviceID .SubsystemVendor = subsystemVendor
92+ deviceID .HasSubsystem = true
3793 }
38- return DeviceID (deviceID ), nil
94+
95+ return deviceID , nil
3996}
4097
4198// String returns a 'DeviceID' as a string.
4299func (d DeviceID ) String () string {
43- return fmt .Sprintf ("0x%X" , uint32 (d ))
100+ primary := fmt .Sprintf ("0x%04X%04X" , d .Device , d .Vendor )
101+ if d .HasSubsystem {
102+ return fmt .Sprintf ("%s:0x%04X%04X" , primary , d .SubsystemDevice , d .SubsystemVendor )
103+ }
104+ return primary
44105}
45106
46107// GetVendor returns the 'vendor' portion of a 'DeviceID'.
47108func (d DeviceID ) GetVendor () uint16 {
48- return uint16 ( d )
109+ return d . Vendor
49110}
50111
51112// GetDevice returns the 'device' portion of a 'DeviceID'.
52113func (d DeviceID ) GetDevice () uint16 {
53- return uint16 (d >> 16 )
114+ return d .Device
115+ }
116+
117+ // Primary returns a copy of the 'DeviceID' with any subsystem qualification removed.
118+ func (d DeviceID ) Primary () DeviceID {
119+ return NewDeviceID (d .Device , d .Vendor )
120+ }
121+
122+ // Matches checks if a hardware GPU matches the DeviceID filter.
123+ // If the filter has a subsystem defined, it requires an exact match on all 4 components.
124+ // Otherwise, it only matches on the primary device and vendor IDs.
125+ func (filter DeviceID ) Matches (hardware DeviceID ) bool {
126+ if filter .Device != hardware .Device || filter .Vendor != hardware .Vendor {
127+ return false
128+ }
129+
130+ if filter .HasSubsystem {
131+ if filter .SubsystemDevice != hardware .SubsystemDevice || filter .SubsystemVendor != hardware .SubsystemVendor {
132+ return false
133+ }
134+ }
135+
136+ return true
137+ }
138+
139+ func splitRawDeviceID (raw uint64 ) (uint16 , uint16 ) {
140+ // nolint:gosec // raw is parsed as a 32-bit value, so both halves fit in a uint16
141+ return uint16 (raw >> 16 ), uint16 (raw & 0xFFFF )
54142}
0 commit comments