diff --git a/include/gpu.h b/include/gpu.h new file mode 100644 index 0000000..33057e9 --- /dev/null +++ b/include/gpu.h @@ -0,0 +1,41 @@ +#ifndef GPU_H +#define GPU_H + +#include + +struct gpu_sensors_t { + uint64_t vram_used; + // uint64_t vram_total = 0; + + float vcore; + + uint16_t clock_core; + uint16_t clock_mem; + + // uint8_t fan_pwm = 0; + uint16_t fan_speed; + + uint16_t power_avg; + uint16_t power_cap; + // uint16_t power_cap_max = 0; + + uint8_t temp_edge; + uint8_t temp_hotspot; + uint8_t temp_mem; + uint8_t temp_vrm_gfx; + uint8_t temp_vrm_soc; + uint8_t temp_vrm_mem; + + bool has_gpu_metrics; + + uint8_t usage_core; + uint8_t usage_media; + uint8_t usage_umc; + + char hwmon[128]; +}; + +void gpu_detect(struct gpu_sensors_t* t, const char* pci); +void gpu_update(struct gpu_sensors_t* g); + +#endif \ No newline at end of file diff --git a/src/gpu.c b/src/gpu.c new file mode 100644 index 0000000..ea2e5bb --- /dev/null +++ b/src/gpu.c @@ -0,0 +1,123 @@ +#include "gpu.h" + +#include +#include +#include "gpu_metrics.h" + +#include "utils.h" +#include "log.h" + +constexpr char rel_metrics_path[] = "/../../gpu_metrics"; // gpu_metrics path relative to hwmon path +constexpr uint16_t max_metrics_length = 512; // length of the metrics buffer + +void gpu_detect(struct gpu_sensors_t *t, const char *pci) { + snprintf(t->hwmon, sizeof(t->hwmon), "/sys/bus/pci/devices/%s/hwmon", pci); + const int ret = get_hwmon_dir(t->hwmon); + if (ret != 0) { + t->hwmon[0] = '\0'; + applog(LOG_WRN, "Detected GPU hwmon"); + return; + } + + char buf[128]; + char *nt = stpncpy(buf, t->hwmon, sizeof(buf)); + stpcpy(nt, rel_metrics_path); // write at the null terminator address + if (file_exists(buf)) { + char raw_metrics[max_metrics_length]; + if (!read_file(buf, raw_metrics, sizeof(raw_metrics))) + return; + const struct metrics_table_header* header = (struct metrics_table_header*)raw_metrics; + applog(LOG_INF, "Found gpu_metrics_v%d_%d", header->format_revision, header->content_revision); + t->has_gpu_metrics = true; + } +} + +/* some metrics are missing in gpu_metrics, we always fill them from fs, unused metrics are commented out */ +void fill_missing_metrics(struct gpu_sensors_t *g) { + char buf[32]; + fast_read_num_f(g->hwmon, "/in0_input", buf, &g->vcore, 1000); + // g->fan_pwm = std::stoi(readFile(g->hwmon + "/pwm1", 4)) * 100 / 256; + fast_read_num_u16(g->hwmon, "/fan1_input", buf, &g->fan_speed, 1); + fast_read_num_u16(g->hwmon, "/power1_cap", buf, &g->power_cap, 1000000); + // utils::fastReadNum(g->hwmon + "/power1_cap_max", g->power_cap_max) / 1000000; + fast_read_num_u64(g->hwmon, "/../../mem_info_vram_used", buf, &g->vram_used, 1); + // utils::fastReadNum(g->hwmon + "/../../mem_info_vram_total", g->vram_total); +} + +/* get sensors for pre-vega cards (rx480/580) */ +void update_from_fs(struct gpu_sensors_t *g) { + char buf[32]; + fast_read_num_u16(g->hwmon, "/freq1_input", buf, &g->clock_core, 1000000); + fast_read_num_u16(g->hwmon, "/freq2_input", buf, &g->clock_mem, 1000000); + fast_read_num_u16(g->hwmon, "/power1_input", buf, &g->power_avg, 1000000); + fast_read_num_u8(g->hwmon, "/temp1_input", buf, &g->temp_edge, 1000); + fast_read_num_u8(g->hwmon, "/../../gpu_busy_percent", buf, &g->usage_core, 1); +} + +void update_from_metrics(struct gpu_sensors_t *g) { + char buf[128]; + char raw_metrics[max_metrics_length]; + char *nt = stpncpy(buf, g->hwmon, sizeof(buf)); + stpcpy(nt, rel_metrics_path); // write at the null terminator address + + if (!read_file(buf, raw_metrics, sizeof(raw_metrics))) + return; + + const struct metrics_table_header *header = (struct metrics_table_header *)raw_metrics; + + switch (header->format_revision * 10 + header->content_revision) { + + /* rx5700xt, rx7800xtx rx9070xt */ + case 13: { + const struct gpu_metrics_v1_3* metrics = (struct gpu_metrics_v1_3*)raw_metrics; + g->usage_core = metrics->average_gfx_activity; + g->usage_umc = metrics->average_umc_activity; + g->usage_media = metrics->average_mm_activity; + + g->temp_edge = metrics->temperature_edge; + g->temp_hotspot = metrics->temperature_hotspot; + g->temp_mem = metrics->temperature_mem; + + g->temp_vrm_gfx = metrics->temperature_vrgfx; + g->temp_vrm_soc = metrics->temperature_vrsoc; + g->temp_vrm_mem = metrics->temperature_vrmem; + g->power_avg = metrics->average_socket_power; + g->fan_speed = metrics->current_fan_speed; + + g->clock_core = metrics->average_gfxclk_frequency; + g->clock_mem = metrics->average_uclk_frequency; // mem controller clock, needs to be 2x on 5700xt for some reason + + // hacks for rx7800xt + // g->clock_core = metrics->average_vclk1_frequency; // why not gfxclock? + // g->clock_mem = metrics->average_gfxclk_frequency; // why gfxclock? min mem clock seems to be 96mhz, this is probably including time sleeping + break; + } + + /* ryzen 5 5600g */ + case 22: { + const struct gpu_metrics_v2_2* metrics = (struct gpu_metrics_v2_2*)raw_metrics; + g->temp_edge = metrics->temperature_gfx / 100; + break; + } + + /* werid hardware i probably dont like, maybe fallback to reading from fs? */ + default: { + applog(LOG_WRN, "Unsupported gpu_metrics_v%d_%d", header->format_revision, header->content_revision); + break; + } + } +} + + /* reads values from system and updates the target gpu_sensors_t struct */ +void gpu_update(struct gpu_sensors_t *g) { + const bool hwmon_valid = g->hwmon[0] != '\0'; + + if (hwmon_valid) + fill_missing_metrics(g); + if (g->has_gpu_metrics) + update_from_metrics(g); + else if (hwmon_valid) + update_from_fs(g); + if (g->fan_speed > 9999) + g->fan_speed = 9999; // sanity check fan speed +} \ No newline at end of file