diff --git a/meson_options.txt b/meson_options.txt index 532d621..0434b44 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -4,3 +4,5 @@ option('daemon', type: 'boolean', value: 'true', description: 'Build daemon') option('plugins', type: 'boolean', value: 'true', description: 'Build plugins') option('library', type: 'boolean', value: 'true', description: 'Build library') option('gui', type: 'boolean', value: 'true', description: 'Build Qt GUI') +option('require-python-hwdata', type: 'boolean', value: 'false', + description: 'Require python-hwdata for prettier AMD GPU names') diff --git a/src/plugins/AMD.cpp b/src/plugins/AMD.cpp index e0c3876..028f38a 100644 --- a/src/plugins/AMD.cpp +++ b/src/plugins/AMD.cpp @@ -13,6 +13,10 @@ #include #include +#ifdef WITH_HWDATA + #include +#endif + #define _(String) gettext(String) extern int errno; @@ -1242,6 +1246,20 @@ std::vector> getUtilizationsRoot(AMDGPUData data) { } std::vector> getGPUName(AMDGPUData data) { +#ifdef WITH_HWDATA + // Get GPU name from hwdata + static auto pciObj = getPciObject(); + auto pciData = fromUeventFile(data.deviceFilename); + if (pciObj.has_value() && pciData.has_value()) { + auto name = hwdataName(*pciObj, *pciData); + if (name.has_value()) + return {DeviceNode{ + .name = *name, + .interface = std::nullopt, + .hash = md5(data.identifier), + }}; + } +#endif auto name = amdgpu_get_marketing_name(data.devHandle); if (name) { return {DeviceNode{ @@ -1319,6 +1337,10 @@ private: }; TreeNode AMDPlugin::deviceRootNode() { +#ifdef WITH_HWDATA + PythonInstance p; +#endif + TreeNode root; auto dataVec = fromFilesystem(); diff --git a/src/plugins/AMDUtils.cpp b/src/plugins/AMDUtils.cpp index e827802..d9d1bf9 100644 --- a/src/plugins/AMDUtils.cpp +++ b/src/plugins/AMDUtils.cpp @@ -192,6 +192,7 @@ std::optional fromRenderDFile(const fs::directory_entry &entry) { .devPath = devPath, .devHandle = dev, .pciId = std::to_string(info.device_id), + .deviceFilename = filename, .identifier = identifier, .ppTableType = tableType, }; diff --git a/src/plugins/AMDUtils.hpp b/src/plugins/AMDUtils.hpp index 23119e8..4eb4902 100644 --- a/src/plugins/AMDUtils.hpp +++ b/src/plugins/AMDUtils.hpp @@ -31,6 +31,8 @@ struct AMDGPUData { amdgpu_device_handle devHandle; // PCIe device ID std::string pciId; + // Eg. renderD128 + std::string deviceFilename; // Device ID + GPU index std::string identifier; std::optional ppTableType; diff --git a/src/plugins/HWData.cpp b/src/plugins/HWData.cpp new file mode 100644 index 0000000..98ca524 --- /dev/null +++ b/src/plugins/HWData.cpp @@ -0,0 +1,63 @@ +#include "HWData.hpp" + +#include +#include + +std::optional fromUeventFile(const std::string &deviceFilename) { + /* + DRIVER=nvidia + PCI_CLASS=30000 + PCI_ID=10DE:1B80 + PCI_SUBSYS_ID=1458:3702 + PCI_SLOT_NAME=0000:01:00.0 + MODALIAS=pci:v000010DEd00001B80sv00001458sd00003702bc03sc00i00 + */ + + char path[64]; + snprintf(path, 64, "/sys/class/drm/%s/device/uevent", deviceFilename.c_str()); + + auto contents = fileContents(path); + if (!contents.has_value()) + return std::nullopt; + + auto isNewline = [](char c) { return c == '\n'; }; + auto lines = fplus::split_by(isNewline, false, *contents); + + if (lines.size() > 5) { + auto idWords = fplus::split_one_of(std::string{"=:"}, false, lines[2]); + auto subsysWords = fplus::split_one_of(std::string{"="}, false, lines[3]); + if (idWords.size() > 2 && subsysWords.size() > 1) { + auto device = fplus::to_lower_case(idWords[2]); + auto subsystem = fplus::to_lower_case(subsysWords[1]); + + return PciData{device, subsystem}; + } + } + return std::nullopt; +} + +std::optional getPciObject() { + auto modName = PyUnicode_FromString("hwdata"); + auto mod = PyImport_Import(modName); + + if (mod) { + auto pciObj = PyObject_GetAttrString(mod, "PCI"); + if (pciObj) + return pciObj; + } + return std::nullopt; +} + +std::optional hwdataName(PyObject *pciObj, PciData data) { + // Vendor is always '1002' aka AMD + auto subsysStr = PyObject_CallMethod( + pciObj, "get_subsystem", "sss", "1002", data.device.c_str(), data.subsystem.c_str()); + if (subsysStr && PyUnicode_Check(subsysStr)) + return PyUnicode_AsUTF8(subsysStr); + + // Try to get device name + auto devStr = PyObject_CallMethod(pciObj, "get_device", "ss", "1002", data.device.c_str()); + if (devStr && PyUnicode_Check(devStr)) + return PyUnicode_AsUTF8(devStr); + return std::nullopt; +} diff --git a/src/plugins/HWData.hpp b/src/plugins/HWData.hpp new file mode 100644 index 0000000..b69b6c6 --- /dev/null +++ b/src/plugins/HWData.hpp @@ -0,0 +1,19 @@ +#include +#include +#include + +// Reduces the places we need to ifdef by destroying automatically +class PythonInstance { +public: + PythonInstance() { Py_Initialize(); } + ~PythonInstance() { Py_Finalize(); } +}; + +struct PciData { + std::string device; + std::string subsystem; +}; + +std::optional fromUeventFile(const std::string &deviceFilename); +std::optional getPciObject(); +std::optional hwdataName(PyObject *pciObj, PciData); diff --git a/src/plugins/meson.build b/src/plugins/meson.build index 12b96c8..ca0b412 100644 --- a/src/plugins/meson.build +++ b/src/plugins/meson.build @@ -1,17 +1,38 @@ -libdrm_amdgpu = cc.find_library('drm_amdgpu', required : false) -libdrm_dep = dependency('libdrm', required : false) - patterns_inc = include_directories('../include/deps/patterns/include/mpark') fplus_inc = include_directories('../include/deps/FunctionalPlus/include') +libdrm_amdgpu = cc.find_library('drm_amdgpu', required : false) +libdrm_dep = dependency('libdrm', required : false) + +python = import('python') +python_with_hwdata = python.find_installation('python3', + modules : ['hwdata'], + required : get_option('require-python-hwdata')) +hwdata_version = run_command('python3', '-c', + 'from importlib.metadata import version; print(version(\'hwdata\'))').stdout().strip() + if libdrm_dep.found() and libdrm_amdgpu.found() - shared_library('amd', 'AMD.cpp', 'AMDUtils.cpp', 'Utils.cpp', + sources = ['AMD.cpp', 'AMDUtils.cpp', 'Utils.cpp'] + cpp_args = [] + deps = [ libdrm_amdgpu, libdrm_dep, boost_dep ] + if (python_with_hwdata.found()) + if (hwdata_version.version_compare('<2.4.1')) + warning('python-hwdata 2.4.1 is recommended for PCI.get_subsystem') + endif + # Makes us link to libpython + deps += python_with_hwdata.dependency(embed : true) + cpp_args += '-DWITH_HWDATA' + sources += 'HWData.cpp' + endif + shared_library('amd', + sources, override_options : ['cpp_std=c++17'], include_directories : [incdir, patterns_inc, fplus_inc], - dependencies : [ libdrm_amdgpu, libdrm_dep, boost_dep ], + dependencies : deps, install_dir : get_option('libdir') / 'tuxclocker' / 'plugins', install : true, - link_with : libtuxclocker) + cpp_args : cpp_args, + link_with : libtuxclocker) endif libnvml = cc.find_library('nvidia-ml', required : false)