Skip to content

Commit 560cd77

Browse files
authored
Merge pull request #29 from threefoldtech/development_gpu_specs
Add more specs to gpu
2 parents 534345d + db755da commit 560cd77

6 files changed

Lines changed: 108 additions & 0 deletions

File tree

client/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -370,6 +370,7 @@ type GPU struct {
370370
ID string `json:"id"`
371371
Vendor string `json:"vendor"`
372372
Device string `json:"device"`
373+
Vram uint64 `json:"vram"`
373374
Contract uint64 `json:"contract"`
374375
}
375376
```

client/node.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,7 @@ type GPU struct {
132132
ID string `json:"id"`
133133
Vendor string `json:"vendor"`
134134
Device string `json:"device"`
135+
Vram uint64 `json:"vram"`
135136
Contract uint64 `json:"contract"`
136137
}
137138

docs/manual/api.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -248,6 +248,7 @@ GPU {
248248
"id": "string"
249249
"vendor": "string"
250250
"device": "string",
251+
"vram": "uint64",
251252
"contract": "uint64",
252253
}
253254
```

pkg/capacity/capacity.go

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,18 @@
11
package capacity
22

33
import (
4+
"encoding/xml"
5+
"fmt"
46
"os"
57
"os/exec"
8+
"strconv"
69
"strings"
710
"syscall"
811

912
"github.com/pkg/errors"
1013
"github.com/rs/zerolog/log"
1114
"github.com/shirou/gopsutil/host"
15+
"github.com/threefoldtech/zosbase/pkg"
1216
"github.com/threefoldtech/zosbase/pkg/capacity/dmi"
1317
"github.com/threefoldtech/zosbase/pkg/capacity/smartctl"
1418
"github.com/threefoldtech/zosbase/pkg/gridtypes"
@@ -182,3 +186,101 @@ func (r *ResourceOracle) GPUs() ([]PCI, error) {
182186
}
183187
return ListPCI(GPU)
184188
}
189+
190+
// normalizeBusID converts a bus ID from format "00000000:01:00.0" to "0000:01:00.0"
191+
func normalizeBusID(busID string) string {
192+
parts := strings.Split(busID, ":")
193+
if len(parts) != 3 {
194+
return busID
195+
}
196+
domain := strings.TrimLeft(parts[0], "0")
197+
if domain == "" {
198+
domain = "0000"
199+
}
200+
domain = fmt.Sprintf("%0*s", 4, domain)
201+
return fmt.Sprintf("%s:%s:%s", domain, parts[1], parts[2])
202+
}
203+
204+
// DisplayNode represents a display device from lshw XML output
205+
type DisplayNode struct {
206+
Class string `xml:"class,attr"`
207+
BusInfo string `xml:"businfo"`
208+
Product string `xml:"product"`
209+
Vendor string `xml:"vendor"`
210+
Resources struct {
211+
Memory []struct {
212+
Value string `xml:"value,attr"`
213+
} `xml:"resource"`
214+
} `xml:"resources"`
215+
}
216+
217+
// DisplayList represents the root XML structure from lshw
218+
type DisplayList struct {
219+
Nodes []DisplayNode `xml:"node"`
220+
}
221+
222+
// GetGpuDevice gets the GPU information using lshw command
223+
func GetGpuDevice(p *PCI) (pkg.GPUInfo, error) {
224+
cmd := exec.Command("lshw", "-C", "display", "-xml")
225+
output, err := cmd.Output()
226+
if err != nil {
227+
return pkg.GPUInfo{}, fmt.Errorf("failed to run lshw command: %w", err)
228+
}
229+
230+
var displayList DisplayList
231+
err = xml.Unmarshal(output, &displayList)
232+
if err != nil {
233+
return pkg.GPUInfo{}, fmt.Errorf("failed to parse lshw XML output: %w", err)
234+
}
235+
236+
for _, node := range displayList.Nodes {
237+
if node.Class != "display" {
238+
continue
239+
}
240+
241+
busInfo := node.BusInfo
242+
if !strings.HasPrefix(busInfo, "pci@") {
243+
continue
244+
}
245+
246+
busID := strings.TrimPrefix(busInfo, "pci@")
247+
normalizedBusID := normalizeBusID(busID)
248+
249+
if normalizedBusID != p.Slot {
250+
continue
251+
}
252+
253+
var vram uint64 = 0
254+
for _, resource := range node.Resources.Memory {
255+
if strings.Contains(resource.Value, "-") {
256+
parts := strings.Split(resource.Value, "-")
257+
if len(parts) == 2 {
258+
start := strings.TrimSpace(parts[0])
259+
end := strings.TrimSpace(parts[1])
260+
if startVal, err1 := strconv.ParseUint(start, 16, 64); err1 == nil {
261+
if endVal, err2 := strconv.ParseUint(end, 16, 64); err2 == nil {
262+
size := (endVal - startVal + 1) / (1024 * 1024)
263+
if size > vram {
264+
vram = size
265+
}
266+
}
267+
}
268+
}
269+
}
270+
}
271+
272+
vendor, device, ok := p.GetDevice()
273+
if !ok {
274+
return pkg.GPUInfo{}, fmt.Errorf("failed to get vendor and device info")
275+
}
276+
277+
return pkg.GPUInfo{
278+
ID: p.ShortID(),
279+
Vendor: vendor.Name,
280+
Device: device.Name,
281+
Vram: vram,
282+
}, nil
283+
}
284+
285+
return pkg.GPUInfo{}, fmt.Errorf("gpu not found in lshw output")
286+
}

pkg/primitives/statistics.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -315,10 +315,12 @@ func (s *statsStream) ListGPUs() ([]pkg.GPUInfo, error) {
315315

316316
for _, pciDevice := range devices {
317317
id := pciDevice.ShortID()
318+
gpu, _ := capacity.GetGpuDevice(&pciDevice)
318319
info := pkg.GPUInfo{
319320
ID: id,
320321
Vendor: "unknown",
321322
Device: "unknown",
323+
Vram: gpu.Vram,
322324
Contract: used[id],
323325
}
324326

pkg/provision.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,5 +58,6 @@ type GPUInfo struct {
5858
ID string `json:"id"`
5959
Vendor string `json:"vendor"`
6060
Device string `json:"device"`
61+
Vram uint64 `json:"vram"`
6162
Contract uint64 `json:"contract"`
6263
}

0 commit comments

Comments
 (0)