Compare commits
5
Commits
v2.1.2
...
7b80a78763
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7b80a78763 | ||
|
|
4bb85c461e | ||
|
|
5319945f08 | ||
|
|
edd575042e | ||
|
|
60caeaf0ef |
@@ -1,7 +1,7 @@
|
||||
.PHONY : clean debug
|
||||
|
||||
OBJS = evga-icx.o evga-card.o icx3.o
|
||||
LDLIBS = -li2c
|
||||
OBJS = evga-icx.o evga-card.o icx3.o board-sensors.o zen3-rapl.o
|
||||
LDLIBS = -li2c -lm
|
||||
CFLAGS = -MD
|
||||
|
||||
ifdef USE_NVML
|
||||
@@ -18,7 +18,7 @@ endif
|
||||
|
||||
evga-icx : $(OBJS)
|
||||
|
||||
debug : CFLAGS += -g -O0
|
||||
debug : CFLAGS += -g -Og
|
||||
debug : evga-icx
|
||||
|
||||
clean :
|
||||
|
||||
@@ -55,6 +55,8 @@ Available options:
|
||||
--watch N : Keep printing output every N seconds
|
||||
--overwrite : Overwrite previously displayed info with --watch and --compact instead of continuously logging new lines
|
||||
--color : Print headers in color in --compact mode for better readability
|
||||
--no-reasons : Do not query NVML for clock reasons (can cause stuttering)
|
||||
--board : Also print temperatures from the CPU, motherboard, and other sensors
|
||||
```
|
||||
|
||||
### Examples:
|
||||
|
||||
+122
@@ -0,0 +1,122 @@
|
||||
#include <dirent.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "board-sensors.h"
|
||||
|
||||
int find_board_sensors(struct hwmon_avail_sensor *board_sensors, int max_sensors)
|
||||
{
|
||||
const char *hwmon_path = "/sys/class/hwmon/";
|
||||
char device_path[NAME_MAX];
|
||||
char sensor_path[NAME_MAX];
|
||||
|
||||
char driver_name[256];
|
||||
|
||||
FILE *file;
|
||||
DIR *dir;
|
||||
struct dirent *ent;
|
||||
|
||||
int num_sensors = 0;
|
||||
|
||||
/* Start looking for hwmon devices in /sys/class/hwmon/ */
|
||||
dir = opendir(hwmon_path);
|
||||
|
||||
/* make sure we can open the device directory */
|
||||
if(dir == NULL)
|
||||
return 0;
|
||||
|
||||
/* loop over all hwmon devices */
|
||||
while((ent = readdir(dir)) != NULL)
|
||||
{
|
||||
/* Ignore any non-hwmon dirs */
|
||||
if(strncmp(ent->d_name, "hwmon", 5) != 0)
|
||||
continue;
|
||||
|
||||
strcpy(device_path, hwmon_path);
|
||||
strcat(device_path, ent->d_name);
|
||||
|
||||
/* Read in the name of the device */
|
||||
strcpy(sensor_path, device_path);
|
||||
strcat(sensor_path, "/name");
|
||||
|
||||
file = fopen(sensor_path, "r");
|
||||
if (file == NULL)
|
||||
continue;
|
||||
|
||||
if (fgets(driver_name, sizeof(driver_name), file) == NULL) {
|
||||
fclose(file);
|
||||
continue;
|
||||
}
|
||||
|
||||
fclose(file);
|
||||
|
||||
/* Driver names have a linebreak at the end so let's remove that for comparison*/
|
||||
driver_name[strlen(driver_name) - 1] = '\0';
|
||||
|
||||
/* Loop through all supported sensors and see if any are present in this device */
|
||||
for (int i = 0; i < (sizeof(hwmon_sensor_info) / sizeof(struct hwmon_sensor)); i++) {
|
||||
if (strcmp(driver_name, hwmon_sensor_info[i].driver_name) == 0) {
|
||||
/* We matched the driver name, try to open the files */
|
||||
strcpy(sensor_path, device_path);
|
||||
strcat(sensor_path, "/");
|
||||
strcat(sensor_path, hwmon_sensor_info[i].sensor_file_name);
|
||||
strcat(sensor_path, "_input");
|
||||
|
||||
file = fopen(sensor_path, "r");
|
||||
if (file != NULL) {
|
||||
fclose(file);
|
||||
/* Good open of the sensor file */
|
||||
board_sensors[num_sensors].file = calloc(NAME_MAX, sizeof(char));
|
||||
strcpy(board_sensors[num_sensors].file, sensor_path);
|
||||
board_sensors[num_sensors].sort_index = i;
|
||||
board_sensors[num_sensors].sensor_info = &hwmon_sensor_info[i];
|
||||
|
||||
/* Read in the sensor name */
|
||||
board_sensors[num_sensors].sensor_name = calloc(MAX_SENSOR_NAME_LENGTH, sizeof(char));
|
||||
strcpy(sensor_path, device_path);
|
||||
strcat(sensor_path, "/");
|
||||
strcat(sensor_path, hwmon_sensor_info[i].sensor_file_name);
|
||||
strcat(sensor_path, "_label");
|
||||
|
||||
file = fopen(sensor_path, "r");
|
||||
if (file != NULL)
|
||||
fgets(board_sensors[num_sensors].sensor_name, MAX_SENSOR_NAME_LENGTH, file);
|
||||
|
||||
/* Sensor name seems to always have a trailing newline we don't want */
|
||||
size_t len_without_newline = strcspn(board_sensors[num_sensors].sensor_name, "\n");
|
||||
board_sensors[num_sensors].sensor_name[len_without_newline] = '\0';
|
||||
|
||||
if (num_sensors == max_sensors)
|
||||
return num_sensors;
|
||||
num_sensors++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return num_sensors;
|
||||
}
|
||||
|
||||
/* Returns 0 on a bad read or missing sensor, 1 on OK */
|
||||
int get_sensor_reading(struct hwmon_avail_sensor *sensor, float *reading) {
|
||||
char buf[256] = {0};
|
||||
long int raw;
|
||||
FILE *file;
|
||||
|
||||
file = fopen(sensor->file, "r");
|
||||
if (file == NULL)
|
||||
return 0;
|
||||
|
||||
fgets(buf, 256, file);
|
||||
raw = strtol(buf, NULL, 10);
|
||||
|
||||
fclose(file);
|
||||
|
||||
*reading = (float)raw / sensor->sensor_info->divisor;
|
||||
|
||||
if (*reading == sensor->sensor_info->bad_value)
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
#ifndef BOARD_SENSORS_H
|
||||
#define BOARD_SENSORS_H
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#define MAX_SENSOR_NAME_LENGTH 256
|
||||
|
||||
struct hwmon_sensor {
|
||||
char *driver_name; /* Contents of /sys/class/hwmon/hwmonX/name */
|
||||
char *sensor_file_name; /* Sysfs file to read */
|
||||
char *name_prefix; /* Prefix to attach to temp*_label for clarity */
|
||||
char *short_name; /* 'Category' name when using compact mode */
|
||||
char *units; /* Units string */
|
||||
float divisor; /* Divisor to convert temp* to units */
|
||||
float bad_value; /* Raw value that indicates a bad (missing) sensor TODO: verfiy most of these*/
|
||||
};
|
||||
|
||||
/* Note the order here matters, it's the order these will be printed in */
|
||||
static struct hwmon_sensor hwmon_sensor_info[] =
|
||||
{
|
||||
{"zenpower", "temp1", "CPU ", "CPU", "°C", 1000.0, -40.0 }, /* Tdie */
|
||||
{"asusec", "temp2", "Motherboard ", "CPU", "°C", 1000.0, -40.0 }, /* CPU */
|
||||
{"zenpower", "temp3", "CPU ", "CCX", "°C", 1000.0, -40.0 }, /* Tccd1 */
|
||||
{"zenpower", "temp4", "CPU ", "CCX", "°C", 1000.0, -40.0 }, /* Tccd2 */
|
||||
{"zenpower", "temp5", "CPU ", "CCX", "°C", 1000.0, -40.0 }, /* Tccd3 */
|
||||
{"zenpower", "temp6", "CPU ", "CCX", "°C", 1000.0, -40.0 }, /* Tccd4 */
|
||||
{"zenpower", "temp7", "CPU ", "CCX", "°C", 1000.0, -40.0 }, /* Tccd5 */
|
||||
{"zenpower", "temp8", "CPU ", "CCX", "°C", 1000.0, -40.0 }, /* Tccd6 */
|
||||
{"zenpower", "temp9", "CPU ", "CCX", "°C", 1000.0, -40.0 }, /* Tccd7 */
|
||||
{"zenpower", "temp10", "CPU ", "CCX", "°C", 1000.0, -40.0 }, /* Tccd8 */
|
||||
{"zen-rapl", "", "CPU ", "POW", " W", 0.0, 0.0 }, /* Zen RAPL placeholder */
|
||||
{"asusec", "temp1", "Motherboard ", "CHIP", "°C", 1000.0, -40.0 }, /* Chipset */
|
||||
{"asusec", "temp5", "Motherboard ", "VRM", "°C", 1000.0, -40.0 }, /* VRM */
|
||||
{"asusec", "temp3", "", "MOBO", "°C", 1000.0, -40.0 }, /* Motherboard */
|
||||
{"asusec", "temp4", "Motherboard ", "SENS", "°C", 1000.0, -40.0 }, /* T_Sensor */
|
||||
{"asusec", "temp6", "Motherboard ", "H2O", "°C", 1000.0, -40.0 }, /* Water_In */
|
||||
{"asusec", "temp7", "Motherboard ", "H2O", "°C", 1000.0, -40.0 }, /* Water_Out */
|
||||
{"nvme", "temp1", "NVMe ", "NVME", "°C", 1000.0, -40.0 }, /* NVME Composite */
|
||||
{"nct6798", "fan2", "CPU fan", "CPU", "%", 15.0, 0.0 }, /* cpu_fan */
|
||||
{"asusec", "fan1", "", "CPU", "%", 15.0, 0.0 }, /* cpu_opt */
|
||||
{"nct6798", "fan1", "Chassis 1", "CHA", "%", 12.0, 0.0 }, /* cha1? */
|
||||
{"nct6798", "fan3", "Chassis 2", "CHA", "%", 12.0, 0.0 }, /* cha2? */
|
||||
{"nct6798", "fan4", "Chassis 3", "CHA", "%", 13.0, 0.0 }, /* cha3 */
|
||||
{"nct6798", "fan5", "H amp", "CHA", "%", 12.0, 0.0 }, /* h_amp? */
|
||||
{"nct6798", "fan6", "AIO pump", "CHA", "%", 12.0, 0.0 }, /* aio_pump? */
|
||||
{"nct6798", "fan7", "W pump", "CHA", "%", 12.0, 0.0 }, /* w_pump+? */
|
||||
{"asusec", "fan2", "", "CHIP", "%", 35.0, 0.0 }, /* chipset */
|
||||
};
|
||||
|
||||
struct hwmon_avail_sensor {
|
||||
char *sensor_name; /* Sensor name as read from the sysfs file */
|
||||
int sort_index; /* Sort index for order to display in */
|
||||
char *file; /* File to read from */
|
||||
struct hwmon_sensor *sensor_info; /* Associated sensor info struct */
|
||||
};
|
||||
|
||||
int find_board_sensors(struct hwmon_avail_sensor *board_sensors, int max_sensors);
|
||||
int get_sensor_reading(struct hwmon_avail_sensor *sensor, float *reading);
|
||||
|
||||
#endif
|
||||
+132
-24
@@ -15,13 +15,20 @@
|
||||
|
||||
#include "icx3.h"
|
||||
#include "evga-card.h"
|
||||
#include "board-sensors.h"
|
||||
#include "zen3-rapl.h"
|
||||
|
||||
#define MAX_GPUS 16
|
||||
#define MAX_BOARD_SENSORS 256
|
||||
#define HEADER_COLOR_START "\x1b[36m"
|
||||
#define HEADER_COLOR_END "\x1b[39m"
|
||||
|
||||
char *header_start = "";
|
||||
char *header_end = "";
|
||||
static char *header_start = "";
|
||||
static char *header_end = "";
|
||||
|
||||
static int zen3_rapl_sensor = -1; /* Board sensor number for the RAPL sensor */
|
||||
static int compact = 0; /* Compact one-line per GPU display */
|
||||
static int no_reasons = 0; /* Don't probe or display NVML clock reasons */
|
||||
|
||||
static const char helpstring[] = "Available options:\n"
|
||||
"--i2c N : Only probe I2C bus N instead of all (may help with stuttering or freezing when probing I2C devices)\n"
|
||||
@@ -37,20 +44,24 @@ static const char helpstring[] = "Available options:\n"
|
||||
"--compact : Print sensor reading in a compact one-line per card format\n"
|
||||
"--watch N : Keep printing output every N seconds\n"
|
||||
"--overwrite : Overwrite previously displayed info with --watch and --compact instead of continuously logging\n"
|
||||
"--color : Print headers in color in --compact mode for better readability\n";
|
||||
"--color : Print headers in color in --compact mode for better readability\n"
|
||||
"--no-reasons : Do not query NVML for clock reasons (can cause stuttering)\n"
|
||||
"--board : Also print temperatures from the CPU, motherboard, and other sensors";
|
||||
|
||||
void print_gpu_info(int gpu_num, struct card_info gpus[], int compact);
|
||||
void print_gpu_info(int gpu_num, struct card_info gpus[]);
|
||||
void print_board_info(struct hwmon_avail_sensor *board_sensors, int num_sensors);
|
||||
|
||||
int main (int argc, char **argv)
|
||||
{
|
||||
struct card_info gpus[MAX_GPUS];
|
||||
int gpu_count;
|
||||
struct hwmon_avail_sensor board_sensors[MAX_BOARD_SENSORS];
|
||||
int gpu_count, board_sensor_count;
|
||||
int print_info = 0;
|
||||
int compact = 0;
|
||||
int gpu_num = -1; /* Card to control */
|
||||
int i2c_bus = -1;
|
||||
int overwrite = 0;
|
||||
unsigned int watch = 0;
|
||||
int gpu_num = -1; /* Card to control */
|
||||
int i2c_bus = -1; /* Specific i2c bus to probe instead of all */
|
||||
int overwrite = 0; /* Overwrite printed console info in compact mode */
|
||||
unsigned int watch = 0; /* Refresh display every this many seconds */
|
||||
int print_board_sensors = 0; /* Print CPU/motherbord/other sensors as well */
|
||||
char *fan_speed[ICX3_MAX_FANS] = {NULL};
|
||||
|
||||
/* Input parsing */
|
||||
@@ -110,6 +121,10 @@ int main (int argc, char **argv)
|
||||
} else if (strcmp(argv[i], "--color") == 0) {
|
||||
header_start = HEADER_COLOR_START;
|
||||
header_end = HEADER_COLOR_END;
|
||||
} else if (strcmp(argv[i], "--no-reasons") == 0) {
|
||||
no_reasons = 1;
|
||||
} else if (strcmp(argv[i], "--board") == 0) {
|
||||
print_board_sensors = 1;
|
||||
} else {
|
||||
printf(helpstring);
|
||||
return 0;
|
||||
@@ -129,8 +144,10 @@ int main (int argc, char **argv)
|
||||
if (overwrite && !compact)
|
||||
overwrite = 0;
|
||||
|
||||
/* Scan for supported GPUs */
|
||||
gpu_count = find_evga_gpu_i2cs(gpus, MAX_GPUS, i2c_bus);
|
||||
|
||||
/* Check for no GPUs found or other errors */
|
||||
if (gpu_count == -1) {
|
||||
printf("Error scanning I2C devices\n");
|
||||
return -1;
|
||||
@@ -138,12 +155,21 @@ int main (int argc, char **argv)
|
||||
printf("No supported GPUs found.\nAre you root or do you have udev access to i2c devices?\nDo you need to run `modprobe i2c-dev`?\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Check for invalid GPUs */
|
||||
if (gpu_num > gpu_count - 1) {
|
||||
printf("Invalid GPU number specified (%d, max %d)\n", gpu_num, gpu_count - 1);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Scan for motherboard/CPU/other sensors */
|
||||
if (print_board_sensors) {
|
||||
board_sensor_count = find_board_sensors(board_sensors, MAX_BOARD_SENSORS);
|
||||
if (init_rapl() && board_sensor_count < MAX_BOARD_SENSORS) {
|
||||
board_sensors[board_sensor_count] = rapl_sensor;
|
||||
zen3_rapl_sensor = board_sensor_count;
|
||||
board_sensor_count++;
|
||||
}
|
||||
}
|
||||
|
||||
/* execute fan commands */
|
||||
if (gpu_num == -1) {
|
||||
@@ -163,8 +189,10 @@ int main (int argc, char **argv)
|
||||
/* NVML init */
|
||||
#ifdef USE_NVML
|
||||
init_nvml();
|
||||
for (int i = 0; i < gpu_count; i++)
|
||||
for (int i = 0; i < gpu_count; i++) {
|
||||
gpus[i].nvml_device = malloc(sizeof(nvmlDevice_t));
|
||||
get_nvml_handle(&gpus[i]);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* PCI init for VRAM/hotspot temps */
|
||||
@@ -179,15 +207,18 @@ int main (int argc, char **argv)
|
||||
if (overwrite)
|
||||
printf("\x1b[K"); /* Clear current console line */
|
||||
|
||||
if (print_board_sensors)
|
||||
print_board_info(board_sensors, board_sensor_count);
|
||||
|
||||
if (gpu_num == -1) {
|
||||
/* No GPU specified on command line, loop over all supported GPUs */
|
||||
for (int i = 0; i < gpu_count; i++){
|
||||
if (i > 0)
|
||||
printf("\n");
|
||||
print_gpu_info(i, &gpus[i], compact);
|
||||
print_gpu_info(i, &gpus[i]);
|
||||
}
|
||||
} else if (gpu_num <= gpu_count - 1) {
|
||||
print_gpu_info(gpu_num, &gpus[gpu_num], compact);
|
||||
print_gpu_info(gpu_num, &gpus[gpu_num]);
|
||||
}
|
||||
|
||||
if (!overwrite)
|
||||
@@ -195,8 +226,8 @@ int main (int argc, char **argv)
|
||||
|
||||
if (overwrite && compact) {
|
||||
printf("\x1b[1G"); /* Move cursor back to column 1 */
|
||||
if (gpu_count > 1)
|
||||
printf("\x1b[%dA", gpu_count-1); /* Move cursor back up to the top of gpu list */
|
||||
if (gpu_count > 1 || print_board_sensors)
|
||||
printf("\x1b[%dA", gpu_count-1+print_board_sensors); /* Move cursor back up to the top of gpu list */
|
||||
}
|
||||
|
||||
fflush(stdout);
|
||||
@@ -209,7 +240,81 @@ int main (int argc, char **argv)
|
||||
#endif
|
||||
}
|
||||
|
||||
void print_gpu_info(int gpu_num, struct card_info *gpu, int compact) {
|
||||
void print_board_info(struct hwmon_avail_sensor *board_sensors, int num_sensors)
|
||||
{
|
||||
int printed_sensors = 0;
|
||||
int current_sort_index = 0;
|
||||
float current_reading = 0.0;
|
||||
int good_reading = 0;
|
||||
/* These allow us to 'summarize' units and categories by only printing them when they change */
|
||||
char *last_short_name = NULL;
|
||||
char *last_units = NULL;
|
||||
|
||||
float rapl_power;
|
||||
|
||||
for (int i = 0; i < (sizeof(hwmon_sensor_info) / sizeof(struct hwmon_sensor)); i++) {
|
||||
|
||||
/* Inject our Zen RAPL power reading here */
|
||||
if (strcmp(hwmon_sensor_info[i].driver_name, "zen-rapl") == 0) {
|
||||
board_sensors[zen3_rapl_sensor].sort_index = current_sort_index;
|
||||
board_sensors[zen3_rapl_sensor].sensor_info = &hwmon_sensor_info[i];
|
||||
}
|
||||
|
||||
for (int j=0; j < num_sensors; j++) {
|
||||
struct hwmon_avail_sensor *current_sensor = &board_sensors[j];
|
||||
|
||||
/* Loop over all sensors, but only output those with the current sort index so they come out sort of sorted
|
||||
Duplicates (e.g.) multiple NVMe will come out in whatever sort of order the directory listing happened to */
|
||||
if (current_sensor->sort_index == current_sort_index) {
|
||||
printed_sensors++;
|
||||
|
||||
if (j == zen3_rapl_sensor) {
|
||||
good_reading = 1;
|
||||
current_reading = get_rapl_package_power();
|
||||
} else {
|
||||
good_reading = get_sensor_reading(current_sensor, ¤t_reading);
|
||||
}
|
||||
|
||||
if (!good_reading)
|
||||
continue;
|
||||
|
||||
if (compact) {
|
||||
/* Print units if needed */
|
||||
if (last_units != NULL && strcmp(current_sensor->sensor_info->units, last_units))
|
||||
printf("%s", last_units);
|
||||
|
||||
/* Print new section header if needed */
|
||||
if (last_short_name == NULL || strcmp(current_sensor->sensor_info->short_name, last_short_name)) {
|
||||
if (last_short_name != NULL) /* Spacer for all headings not the first one */
|
||||
printf(" ");
|
||||
printf("%s%s%s", header_start, current_sensor->sensor_info->short_name, header_end);
|
||||
}
|
||||
printf(" %3.0f", current_reading);
|
||||
|
||||
last_short_name = current_sensor->sensor_info->short_name;
|
||||
last_units = current_sensor->sensor_info->units;
|
||||
|
||||
} else {
|
||||
printf("%s%s: %+.1f%s\n",
|
||||
current_sensor->sensor_info->name_prefix,
|
||||
current_sensor->sensor_name,
|
||||
current_reading,
|
||||
current_sensor->sensor_info->units);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
current_sort_index++;
|
||||
}
|
||||
|
||||
if (compact && last_units != NULL)
|
||||
printf("%s", last_units);
|
||||
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
void print_gpu_info(int gpu_num, struct card_info *gpu)
|
||||
{
|
||||
if (compact) {
|
||||
/* One line per GPU */
|
||||
printf("%s#%d FAN%s", header_start, gpu_num, header_end);
|
||||
@@ -241,8 +346,10 @@ void print_gpu_info(int gpu_num, struct card_info *gpu, int compact) {
|
||||
#ifdef USE_NVML
|
||||
printf("%s MEM %s", header_start, header_end);
|
||||
printf("%3d%%", get_nvml_mem_util(gpu));
|
||||
printf("%s CLK %s", header_start, header_end);
|
||||
print_nvml_clock_reason(1, gpu);
|
||||
if (!no_reasons) {
|
||||
printf("%s CLK %s", header_start, header_end);
|
||||
print_nvml_clock_reason(1, gpu);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
@@ -262,8 +369,8 @@ void print_gpu_info(int gpu_num, struct card_info *gpu, int compact) {
|
||||
printf("VRAM: +%.0f°C\n", get_vram_temp(gpu)); /* Print the VRAM temp before the rest of the memory sensors */
|
||||
#endif
|
||||
printf("%s: %+.1f°C\n",
|
||||
icx3_temp_sensor_names[i],
|
||||
icx_temp_sensors[i]);
|
||||
icx3_temp_sensor_names[i],
|
||||
icx_temp_sensors[i]);
|
||||
}
|
||||
|
||||
#ifdef USE_LIBPCI
|
||||
@@ -272,11 +379,12 @@ void print_gpu_info(int gpu_num, struct card_info *gpu, int compact) {
|
||||
|
||||
#ifdef USE_NVML
|
||||
printf("Mem util: %d%%\n", get_nvml_mem_util(gpu));
|
||||
printf("Clock reasons: ");
|
||||
print_nvml_clock_reason(0, gpu);
|
||||
if (!no_reasons) {
|
||||
printf("Clock reasons: ");
|
||||
print_nvml_clock_reason(0, gpu);
|
||||
}
|
||||
printf("\n");
|
||||
#endif
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
+112
@@ -0,0 +1,112 @@
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <cpuid.h>
|
||||
#include <math.h>
|
||||
#include <string.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#include "zen3-rapl.h"
|
||||
|
||||
#define AMD_STRING "AuthenticAMD"
|
||||
#define ZEN_FAMILY 0x17
|
||||
#define ZEN3_FAMILY 0x19
|
||||
|
||||
#define MEASUREMENT_TIME 0.25
|
||||
|
||||
static float energy_unit = 0;
|
||||
|
||||
static int msr_file = -1;
|
||||
|
||||
static float package_power = 0.0;
|
||||
|
||||
static int check_zen()
|
||||
{
|
||||
unsigned int eax = 0, ebx = 0, ecx = 0, edx = 0, ext_family;
|
||||
char vendor[13];
|
||||
|
||||
__get_cpuid(0, &eax, &ebx, &ecx, &edx);
|
||||
|
||||
memcpy(vendor, &ebx, 4);
|
||||
memcpy(vendor+4, &edx, 4);
|
||||
memcpy(vendor+8, &ecx, 4);
|
||||
vendor[12] = 0;
|
||||
|
||||
if (strcmp(vendor, AMD_STRING) != 0){
|
||||
return 0;
|
||||
}
|
||||
|
||||
__get_cpuid(1, &eax, &ebx, &ecx, &edx);
|
||||
|
||||
ext_family = ((eax >> 8) & 0xF) + ((eax >> 20) & 0xFF);
|
||||
if (ext_family != ZEN_FAMILY && ext_family != ZEN3_FAMILY){
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int read_msr(int file, unsigned int index, unsigned long long *data)
|
||||
{
|
||||
if (file < 0)
|
||||
return 0;
|
||||
|
||||
return pread(file, data, sizeof *data, index) == sizeof *data;
|
||||
}
|
||||
|
||||
static float get_energy_unit()
|
||||
{
|
||||
unsigned long long data;
|
||||
// AMD OSRR: page 139 - MSRC001_0299
|
||||
if (!read_msr(msr_file, 0xC0010299, &data))
|
||||
return 0.0;
|
||||
|
||||
return pow(1.0/2.0, (float)((data >> 8) & 0x1F));
|
||||
}
|
||||
|
||||
static unsigned long get_package_energy()
|
||||
{
|
||||
unsigned long long data;
|
||||
// AMD OSRR: page 139 - MSRC001_029B
|
||||
if (!read_msr(msr_file, 0xC001029B, &data))
|
||||
return 0;
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
int init_rapl()
|
||||
{
|
||||
/* Check for supported Zen CPU */
|
||||
if (!check_zen())
|
||||
return 0;
|
||||
|
||||
/* Open MSR file */
|
||||
char *msr_path = "/dev/cpu/0/msr";
|
||||
msr_file = open(msr_path, O_RDONLY);
|
||||
if (msr_file < 0)
|
||||
return 0;
|
||||
|
||||
/* Energy unit */
|
||||
energy_unit = get_energy_unit();
|
||||
if (energy_unit == 0)
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
float get_rapl_package_power()
|
||||
{
|
||||
unsigned long package_eng_b = 0;
|
||||
unsigned long package_eng_a = 0;
|
||||
|
||||
package_eng_b = get_package_energy();
|
||||
|
||||
usleep(MEASUREMENT_TIME*1000000);
|
||||
|
||||
package_eng_a = get_package_energy();
|
||||
|
||||
/* Only update if we computed a good power reading, otherwise return the most recent measurement */
|
||||
if (package_eng_a >= package_eng_b)
|
||||
package_power = (package_eng_a - package_eng_b) * energy_unit / MEASUREMENT_TIME;
|
||||
|
||||
return package_power;
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
#include "board-sensors.h"
|
||||
|
||||
static struct hwmon_avail_sensor rapl_sensor = {"Package power", -1, NULL, NULL};
|
||||
|
||||
int init_rapl();
|
||||
float get_rapl_package_power();
|
||||
Reference in New Issue
Block a user