GPU (Linux): improves PCI GPU name resolution

This commit is contained in:
Carter Li
2026-11-09 00:16:22 +08:00
parent 6843b56f1f
commit ced33a8355
2 changed files with 68 additions and 4 deletions
+3
View File
@@ -1,5 +1,8 @@
# Unreleased
Changes:
* Improved PCI GPU name resolution by checking udev's hardware database and the NVIDIA driver's procfs before falling back to pci.ids. (GPU, Linux)
Bugfixes:
* Fixed libc version detection on FreeBSD without binutils installed, and on DragonFly BSD. (Libc, FreeBSD / DragonFly)
+65 -4
View File
@@ -1,12 +1,10 @@
#include "detection/gpu/gpu.h"
#include "common/debug.h"
#include "common/io.h"
#include "common/FFstrbuf.h"
#include "common/properties.h"
#include "common/strutil.h"
#include "modules/gpu/option.h"
#include <inttypes.h>
#include <stdint.h>
static bool detectDriverFromSysfs(FFstrbuf* result, FFstrbuf* pciDir, FFstrbuf* buffer, [[maybe_unused]] const char* drmKey) {
uint32_t pciDirLength = pciDir->length;
@@ -337,6 +335,64 @@ static inline uint16_t pcieWidthToLanes(const FFstrbuf* buffer) {
return FF_GPU_PCIE_SPEED_UNSET;
}
static bool parseModelName(const char* data, const char* key, bool stripVendorPrefix, FFstrbuf* name) {
ffStrbufClear(name);
if (!ffParsePropLines(data, key, name) || !name->length) {
return false;
}
ffStrbufDecodeHexEscapeSequences(name); // udev hex-escapes characters that are not allowed in property values
ffStrbufTrimSpace(name);
// hwdb names have the form `Codename [Marketing Name]`. Reduce them exactly like
// ffGPUParsePciIdsFile() does, so displaying the name does not change with the source.
if (name->length > 1 && name->chars[name->length - 1] == ']') {
const char* openingBracket = memrchr(name->chars, '[', name->length - 1);
if (openingBracket) {
ffStrbufSubstrAfter(name, (uint32_t) (openingBracket - name->chars));
ffStrbufSubstrBefore(name, name->length - 1);
}
}
// The driver repeats the vendor in its own model line. Dropping it keeps the pci.ids
// shape and lets ffGPUDetectTypeByVendorAndName() see the product family prefix.
if (stripVendorPrefix && ffStrbufStartsWithIgnCaseS(name, "NVIDIA ")) {
ffStrbufSubstrAfter(name, (uint32_t) strlen("NVIDIA ") - 1); // excludes the index itself
}
ffStrbufTrimSpace(name);
return name->length > 0;
}
static bool nameFromFile(const FFstrbuf* path, const char* key, bool stripVendorPrefix, FFstrbuf* buffer, FFstrbuf* name) {
if (!ffReadFileBuffer(path->chars, buffer) || !parseModelName(buffer->chars, key, stripVendorPrefix, name)) {
return false;
}
FF_DEBUG("GPU name resolved from %s: %s", path->chars, name->chars);
return true;
}
// The driver and udev already resolved the model name at probe / boot time. Only `Model:` is
// consumed: the sibling `power` file goes through the RM and can wake a GPU suspended in runtime D3.
static bool resolveNameFromFiles(const char* pciPath, bool isNvidia, FFstrbuf* buffer, FFstrbuf* name) {
FF_STRBUF_AUTO_DESTROY path = ffStrbufCreateA(128);
if (isNvidia) {
ffStrbufSetF(&path, "/proc/driver/nvidia/gpus/%s/information", pciPath);
if (nameFromFile(&path, "Model:", true, buffer, name)) {
return true;
}
}
ffStrbufSetF(&path, "/run/udev/data/+pci:%s", pciPath);
if (nameFromFile(&path, "E:ID_MODEL_FROM_DATABASE=", false, buffer, name)) {
return true;
}
ffStrbufClear(name);
return false;
}
static const char* detectPci(const FFGPUOptions* options, FFlist* gpus, FFstrbuf* buffer, FFstrbuf* deviceDir, const char* drmKey) {
const uint32_t drmDirPathLength = deviceDir->length;
uint32_t vendorId, deviceId, subVendorId, subDeviceId;
@@ -465,7 +521,12 @@ static const char* detectPci(const FFGPUOptions* options, FFlist* gpus, FFstrbuf
}
if (gpu->name.length == 0) {
ffGPUFillVendorAndName(subclassId, (uint16_t) vendorId, (uint16_t) deviceId, gpu);
// Stop at the first hit; unknown vendors are left to pci.ids, which also fills in `vendor`.
bool resolved = gpu->vendor.length > 0 &&
resolveNameFromFiles(pPciPath, gpu->vendor.chars == FF_GPU_VENDOR_NAME_NVIDIA, buffer, &gpu->name);
if (!resolved) {
ffGPUFillVendorAndName(subclassId, (uint16_t) vendorId, (uint16_t) deviceId, gpu);
}
}
ffGPUDetectTypeByVendorAndName(gpu);