mirror of
https://github.com/nestriness/nestri.git
synced 2025-12-12 08:45:38 +02:00
⭐ feat(maitred): Update maitred - hookup to the API (#198)
## Description We are attempting to hookup maitred to the API Maitred duties will be: - [ ] Hookup to the API - [ ] Wait for signal (from the API) to start Steam - [ ] Stop signal to stop the gaming session, clean up Steam... and maybe do the backup ## Summary by CodeRabbit - **New Features** - Introduced Docker-based deployment configurations for both the main and relay applications. - Added new API endpoints enabling real-time machine messaging and enhanced IoT operations. - Expanded database schema and actor types to support improved machine tracking. - **Improvements** - Enhanced real-time communication and relay management with streamlined room handling. - Upgraded dependencies, logging, and error handling for greater stability and performance. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: DatCaptainHorse <DatCaptainHorse@users.noreply.github.com> Co-authored-by: Kristian Ollikainen <14197772+DatCaptainHorse@users.noreply.github.com>
This commit is contained in:
184
packages/maitred/internal/system/gpu.go
Normal file
184
packages/maitred/internal/system/gpu.go
Normal file
@@ -0,0 +1,184 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const (
|
||||
pciClassVGA = 0x0300 // VGA compatible controller
|
||||
pciClass3D = 0x0302 // 3D controller
|
||||
pciClassDisplay = 0x0380 // Display controller
|
||||
pciClassCoProcessor = 0x0b40 // Co-processor (e.g., NVIDIA Tesla)
|
||||
)
|
||||
|
||||
type infoPair struct {
|
||||
Name string
|
||||
ID int
|
||||
}
|
||||
|
||||
type PCIInfo struct {
|
||||
Slot string
|
||||
Class infoPair
|
||||
Vendor infoPair
|
||||
Device infoPair
|
||||
SVendor infoPair
|
||||
SDevice infoPair
|
||||
Rev string
|
||||
ProgIf string
|
||||
Driver string
|
||||
Modules []string
|
||||
IOMMUGroup string
|
||||
}
|
||||
|
||||
const (
|
||||
VendorIntel = 0x8086
|
||||
VendorNVIDIA = 0x10de
|
||||
VendorAMD = 0x1002
|
||||
)
|
||||
|
||||
func GetAllGPUInfo() ([]PCIInfo, error) {
|
||||
var gpus []PCIInfo
|
||||
|
||||
cmd := exec.Command("lspci", "-mmvvvnnkD")
|
||||
output, err := cmd.Output()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
sections := bytes.Split(output, []byte("\n\n"))
|
||||
for _, section := range sections {
|
||||
var info PCIInfo
|
||||
|
||||
lines := bytes.Split(section, []byte("\n"))
|
||||
for _, line := range lines {
|
||||
parts := bytes.SplitN(line, []byte(":"), 2)
|
||||
if len(parts) < 2 {
|
||||
continue
|
||||
}
|
||||
|
||||
key := strings.TrimSpace(string(parts[0]))
|
||||
value := strings.TrimSpace(string(parts[1]))
|
||||
|
||||
switch key {
|
||||
case "Slot":
|
||||
info.Slot = value
|
||||
case "Class":
|
||||
info.Class, err = parseInfoPair(value)
|
||||
case "Vendor":
|
||||
info.Vendor, err = parseInfoPair(value)
|
||||
case "Device":
|
||||
info.Device, err = parseInfoPair(value)
|
||||
case "SVendor":
|
||||
info.SVendor, err = parseInfoPair(value)
|
||||
case "SDevice":
|
||||
info.SDevice, err = parseInfoPair(value)
|
||||
case "Rev":
|
||||
info.Rev = value
|
||||
case "ProgIf":
|
||||
info.ProgIf = value
|
||||
case "Driver":
|
||||
info.Driver = value
|
||||
case "Module":
|
||||
info.Modules = append(info.Modules, value)
|
||||
case "IOMMUGroup":
|
||||
info.IOMMUGroup = value
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// Check if this is a GPU device
|
||||
if isGPUClass(info.Class.ID) {
|
||||
gpus = append(gpus, info)
|
||||
}
|
||||
}
|
||||
|
||||
return gpus, nil
|
||||
}
|
||||
|
||||
// gets infoPair from "SomeName [SomeID]"
|
||||
// example: "DG2 [Arc A770] [56a0]" -> Name: "DG2 [Arc A770]", ID: "56a0"
|
||||
func parseInfoPair(pair string) (infoPair, error) {
|
||||
parts := strings.Split(pair, "[")
|
||||
if len(parts) < 2 {
|
||||
return infoPair{}, errors.New("invalid info pair")
|
||||
}
|
||||
|
||||
id := strings.TrimSuffix(parts[len(parts)-1], "]")
|
||||
name := strings.TrimSuffix(pair, "["+id)
|
||||
name = strings.TrimSpace(name)
|
||||
id = strings.TrimSpace(id)
|
||||
|
||||
// Remove ID including square brackets from name
|
||||
name = strings.ReplaceAll(name, "["+id+"]", "")
|
||||
name = strings.TrimSpace(name)
|
||||
|
||||
idHex, err := parseHexID(id)
|
||||
if err != nil {
|
||||
return infoPair{}, err
|
||||
}
|
||||
|
||||
return infoPair{
|
||||
Name: name,
|
||||
ID: idHex,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func parseHexID(id string) (int, error) {
|
||||
if strings.HasPrefix(id, "0x") {
|
||||
id = id[2:]
|
||||
}
|
||||
parsed, err := strconv.ParseInt(id, 16, 32)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return int(parsed), nil
|
||||
}
|
||||
|
||||
func isGPUClass(class int) bool {
|
||||
return class == pciClassVGA || class == pciClass3D || class == pciClassDisplay || class == pciClassCoProcessor
|
||||
}
|
||||
|
||||
// GetCardDevices returns the /dev/dri/cardX and /dev/dri/renderDXXX device
|
||||
func (info PCIInfo) GetCardDevices() (cardPath, renderPath string, err error) {
|
||||
busID := strings.ToLower(info.Slot)
|
||||
if !strings.HasPrefix(busID, "0000:") || len(busID) != 12 || busID[4] != ':' || busID[7] != ':' || busID[10] != '.' {
|
||||
return "", "", fmt.Errorf("invalid PCI Bus ID format: %s (expected 0000:XX:YY.Z)", busID)
|
||||
}
|
||||
|
||||
byPathDir := "/dev/dri/by-path/"
|
||||
entries, err := os.ReadDir(byPathDir)
|
||||
if err != nil {
|
||||
return "", "", fmt.Errorf("failed to read %s: %v", byPathDir, err)
|
||||
}
|
||||
|
||||
for _, entry := range entries {
|
||||
name := entry.Name()
|
||||
if strings.HasPrefix(name, "pci-"+busID+"-card") {
|
||||
cardPath, err = filepath.EvalSymlinks(filepath.Join(byPathDir, name))
|
||||
if err != nil {
|
||||
return "", "", fmt.Errorf("failed to resolve card symlink %s: %v", name, err)
|
||||
}
|
||||
}
|
||||
if strings.HasPrefix(name, "pci-"+busID+"-render") {
|
||||
renderPath, err = filepath.EvalSymlinks(filepath.Join(byPathDir, name))
|
||||
if err != nil {
|
||||
return "", "", fmt.Errorf("failed to resolve render symlink %s: %v", name, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if cardPath == "" && renderPath == "" {
|
||||
return "", "", fmt.Errorf("no DRM devices found for PCI Bus ID: %s", busID)
|
||||
}
|
||||
return cardPath, renderPath, nil
|
||||
}
|
||||
Reference in New Issue
Block a user