Hardware list: AMD cards are counted from the kernel's KFD files when there is no setup module and no rocm-smi (ROCm from a Python venv), instead of not at all

This commit is contained in:
Niko1221 committed 2026-10-30 19:45:23 +01:00
1 parent ab083a693f
commit bc694233bf
2 files changed
+91

No files matched your search

+39
View File
@@ -976,6 +976,8 @@ class Strata:
amd = []
if not WIN and not amd and shutil.which("rocm-smi"):
amd = rocm_smi_gpus()
if not WIN and not amd: # no setup module, no rocm-smi (ROCm from a Python venv): the kernel's own list
amd = kfd_gpus()
# A PC whose only NVIDIA cards are Pascal / Volta (two GTX 1080s) and that has no AMD card Strata can use: setup runs them with the
# CUDA 12 engine (old_gpus_opt_in), so the app must not call it "no supported graphics card" and start both cards Off (#3655).
# A PC with a newer card keeps recommending that one; naming an old card is still the opt-in.
@@ -1879,6 +1881,43 @@ def windows_video_controllers() -> list:
return [{"name": x.get("Name"), "driver": x.get("DriverVersion")} for x in d if isinstance(x, dict)]
KFD_SUPPORTED = ("gfx1100", "gfx1101", "gfx1102", "gfx1200", "gfx1201", "gfx1030", "gfx1031", "gfx1151")
def kfd_gpus(sysfs: str = "/sys") -> list:
"""AMD cards from the kernel's KFD topology and the amdgpu driver's files, with no ROCm tool and no setup module: a PC that has only the
driver plus the engine's own runtime has no rocm-smi, amd-smi or rocminfo, and the cards were then not counted at all (a 4-card PC ran on one).
Numbered as HIP numbers them (the GPU nodes in order, CPU nodes skipped)."""
base = Path(sysfs) / "class/kfd/kfd/topology/nodes"
out = []
try:
nodes = sorted((p for p in base.iterdir() if p.name.isdigit()), key=lambda p: int(p.name))
except OSError:
return out
for node in nodes:
try:
props = dict(line.partition(" ")[::2] for line in (node / "properties").read_text().splitlines())
ver, simd = int(props.get("gfx_target_version") or 0), int(props.get("simd_count") or 0)
except (OSError, ValueError):
continue
if ver == 0 or simd == 0:
continue
arch = f"gfx{ver // 10000}{(ver // 100) % 100:x}{ver % 100:x}"
dev = Path(sysfs) / f"class/drm/renderD{props.get('drm_render_minor', '').strip()}/device"
try:
vram = int((dev / "mem_info_vram_total").read_text()) / 2 ** 30
except (OSError, ValueError):
vram = 0.0
try:
name = (dev / "product_name").read_text().strip() or f"AMD Radeon ({arch})"
except OSError:
name = f"AMD Radeon ({arch})"
ok = arch in KFD_SUPPORTED
out.append({"index": len(out), "name": name, "vram_gb": round(vram, 1), "arch": arch, "driver": "amdgpu", "vendor": "amd",
"usable": ok, "problem": None if ok else f"not supported - Strata's AMD backend does not list {arch}"})
return out
def rocm_smi_gpus() -> list:
s = run_quiet(["rocm-smi", "--showproductname", "--showmeminfo", "vram", "--json"])
try:
+52
View File
@@ -0,0 +1,52 @@
"""A PC with only the amdgpu driver (no rocm-smi, no setup module) still counts every AMD card, from a fake sysfs.
python -m unittest tools.test_kfd_gpus
"""
import sys
import tempfile
import unittest
from pathlib import Path
ROOT = Path(__file__).resolve().parents[1]
sys.path.insert(0, str(ROOT))
sys.path.insert(0, str(ROOT / "tools"))
import strata_mcp as M # noqa: E402
def node(root: Path, i: int, ver: int, simd: int, minor: int, vram: int = 0, name: str | None = None):
n = root / "class/kfd/kfd/topology/nodes" / str(i)
n.mkdir(parents=True)
(n / "properties").write_text(f"cpu_cores_count {0 if simd else 12}\nsimd_count {simd}\ngfx_target_version {ver}\ndrm_render_minor {minor}\n")
if simd:
d = root / f"class/drm/renderD{minor}/device"
d.mkdir(parents=True)
(d / "mem_info_vram_total").write_text(str(vram))
if name:
(d / "product_name").write_text(name + "\n")
class KfdGpus(unittest.TestCase):
def test_four_cards_and_the_cpu_node(self):
with tempfile.TemporaryDirectory() as tmp:
root = Path(tmp)
node(root, 0, 0, 0, 0)
for k in range(4):
node(root, k + 1, 120001, 128, 128 + k, 32 << 30, "AMD Radeon AI PRO R9700")
gpus = M.kfd_gpus(tmp)
self.assertEqual([g["index"] for g in gpus], [0, 1, 2, 3])
self.assertTrue(all(g["usable"] and g["arch"] == "gfx1201" and g["vram_gb"] == 32.0 for g in gpus))
def test_an_unlisted_architecture_is_named_not_hidden(self):
with tempfile.TemporaryDirectory() as tmp:
node(Path(tmp), 0, 90010, 60, 128, 8 << 30)
(g,) = M.kfd_gpus(tmp)
self.assertFalse(g["usable"])
self.assertIn("gfx90a", g["problem"])
def test_no_amd_driver_is_an_empty_list(self):
with tempfile.TemporaryDirectory() as tmp:
self.assertEqual(M.kfd_gpus(tmp), [])
if __name__ == "__main__":
unittest.main()