File indexing completed on 2024-05-12 17:00:15

0001 /*
0002  * SPDX-FileCopyrightText: 2020 Arjen Hiemstra <ahiemstra@heimr.nl>
0003  * SPDX-FileCopyrightText: 2021 David Redondo <kde@david-redondo.de>
0004  *
0005  * SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
0006  */
0007 
0008 #include "LinuxAmdGpu.h"
0009 
0010 #include <libudev.h>
0011 #include <linux/pci.h>
0012 #include <sensors/sensors.h>
0013 
0014 #include <systemstats/SysFsSensor.h>
0015 #include <systemstats/SensorsFeatureSensor.h>
0016 
0017 int ppTableGetMax(const QByteArray &table)
0018 {
0019     const auto lines = table.split('\n');
0020     auto line = lines.last();
0021     return std::atoi(line.mid(line.indexOf(':') + 1).data());
0022 }
0023 
0024 int ppTableGetCurrent(const QByteArray &table)
0025 {
0026     const auto lines = table.split('\n');
0027 
0028     int current = 0;
0029     for (const auto &line : lines) {
0030         if (!line.contains('*')) {
0031             continue;
0032         }
0033 
0034         current = std::atoi(line.mid(line.indexOf(':') + 1));
0035     }
0036 
0037     return current;
0038 }
0039 
0040 LinuxAmdGpu::LinuxAmdGpu(const QString& id, const QString& name, udev_device *device)
0041     : GpuDevice(id, name)
0042     , m_device(device)
0043 {
0044     udev_device_ref(m_device);
0045 }
0046 
0047 LinuxAmdGpu::~LinuxAmdGpu()
0048 {
0049     udev_device_unref(m_device);
0050 }
0051 
0052 void LinuxAmdGpu::initialize()
0053 {
0054     GpuDevice::initialize();
0055 
0056     m_nameProperty->setValue(QString::fromLocal8Bit(udev_device_get_property_value(m_device, "ID_MODEL_FROM_DATABASE")));
0057 
0058     auto result = udev_device_get_sysattr_value(m_device, "mem_info_vram_total");
0059     if (result) {
0060         m_totalVramProperty->setValue(std::atoll(result));
0061     }
0062 
0063     m_coreFrequencyProperty->setMax(ppTableGetMax(udev_device_get_sysattr_value(m_device, "pp_dpm_sclk")));
0064     m_memoryFrequencyProperty->setMax(ppTableGetMax(udev_device_get_sysattr_value(m_device, "pp_dpm_mclk")));
0065 
0066     std::for_each(m_sensorsSensors.begin(), m_sensorsSensors.end(), [this] (KSysGuard::SensorProperty *sensor) {
0067         sensor->setPrefix(name());
0068     });
0069 }
0070 
0071 void LinuxAmdGpu::update()
0072 {
0073     for (auto sensor : qAsConst(m_sysFsSensors)) {
0074         sensor->update();
0075     }
0076     for (auto sensor : qAsConst(m_sensorsSensors)) {
0077         sensor->update();
0078     }
0079     m_temperatureProperty->update();
0080 }
0081 
0082 void LinuxAmdGpu::makeSensors()
0083 {
0084     auto devicePath = QString::fromLocal8Bit(udev_device_get_syspath(m_device));
0085 
0086     m_nameProperty = new KSysGuard::SensorProperty(QStringLiteral("name"), this);
0087     m_totalVramProperty = new KSysGuard::SensorProperty(QStringLiteral("totalVram"),  this);
0088 
0089     auto sensor = new KSysGuard::SysFsSensor(QStringLiteral("usage"), devicePath % QStringLiteral("/gpu_busy_percent"), 0, this);
0090     m_usageProperty = sensor;
0091     m_sysFsSensors << sensor;
0092 
0093     sensor = new KSysGuard::SysFsSensor(QStringLiteral("usedVram"), devicePath % QStringLiteral("/mem_info_vram_used"), this);
0094     m_usedVramProperty = sensor;
0095     m_sysFsSensors << sensor;
0096 
0097     sensor = new KSysGuard::SysFsSensor(QStringLiteral("coreFrequency"), devicePath % QStringLiteral("/pp_dpm_sclk"), 0, this);
0098     sensor->setConvertFunction([](const QByteArray &input) {
0099         return ppTableGetCurrent(input);
0100     });
0101     m_coreFrequencyProperty = sensor;
0102     m_sysFsSensors << sensor;
0103 
0104     sensor = new KSysGuard::SysFsSensor(QStringLiteral("memoryFrequency"), devicePath % QStringLiteral("/pp_dpm_mclk"), 0, this);
0105     sensor->setConvertFunction([](const QByteArray &input) {
0106         return ppTableGetCurrent(input);
0107     });
0108     m_memoryFrequencyProperty = sensor;
0109     m_sysFsSensors << sensor;
0110 
0111     discoverSensors();
0112 
0113     // Potentially found by discoverSensors, if not create it so it's not missing
0114     if (!m_temperatureProperty) {
0115         m_temperatureProperty = new KSysGuard::SensorProperty(QStringLiteral("temperature"), this);
0116     }
0117 
0118     // It would normally find this in lmsensors plugin or in another function, but just set it in case anything tries to explode
0119     if (!m_powerProperty) {
0120         m_powerProperty = new KSysGuard::SensorProperty(QStringLiteral("power"), this);
0121     }
0122 }
0123 
0124 void LinuxAmdGpu::discoverSensors()
0125 {
0126     sensors_chip_name match;
0127     sensors_parse_chip_name("amdgpu-*", &match);
0128     int number = 0;
0129     const sensors_chip_name *chip;
0130     while ((chip = sensors_get_detected_chips(&match, &number))) {
0131         int domain, bus, device, function;
0132         if (std::sscanf(udev_device_get_sysname(m_device), "%x:%x:%x.%x", &domain, &bus, &device, &function) == 4 &&
0133             ((domain << 16) + (bus << 8) + PCI_DEVFN(device, function)) == chip->addr) {
0134             break;
0135         }
0136     }
0137     if (!chip) {
0138         return;
0139     }
0140     number = 0;
0141     while (const sensors_feature * const feature = sensors_get_features(chip, &number)) {
0142         // We want a special id for the device temperature
0143         KSysGuard::SensorProperty *sensor;
0144         if (feature->type == SENSORS_FEATURE_TEMP && qstrcmp(feature->name, "temp1") == 0) {
0145             sensor = KSysGuard::makeSensorsFeatureSensor(QStringLiteral("temperature"), chip, feature, this);
0146             m_temperatureProperty = sensor;
0147         }  else {
0148             sensor = makeSensorsFeatureSensor(feature->name, chip, feature, this);
0149         }
0150         if (sensor) {
0151             m_sensorsSensors << sensor;
0152         }
0153     }
0154 }