You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
269 lines
7.2 KiB
269 lines
7.2 KiB
#include "linux/LibSensors.h" |
|
|
|
#include "config.h" |
|
|
|
#ifdef HAVE_SENSORS_SENSORS_H |
|
|
|
#include <assert.h> |
|
#include <dlfcn.h> |
|
#include <errno.h> |
|
#include <limits.h> |
|
#include <math.h> |
|
#include <stdlib.h> |
|
#include <stdio.h> |
|
#include <string.h> |
|
#include <sensors/sensors.h> |
|
|
|
#include "Macros.h" |
|
#include "XUtils.h" |
|
#include "linux/LinuxProcessList.h" |
|
|
|
|
|
#ifdef BUILD_STATIC |
|
|
|
#define sym_sensors_init sensors_init |
|
#define sym_sensors_cleanup sensors_cleanup |
|
#define sym_sensors_get_detected_chips sensors_get_detected_chips |
|
#define sym_sensors_get_features sensors_get_features |
|
#define sym_sensors_get_subfeature sensors_get_subfeature |
|
#define sym_sensors_get_value sensors_get_value |
|
|
|
#else |
|
|
|
static int (*sym_sensors_init)(FILE*); |
|
static void (*sym_sensors_cleanup)(void); |
|
static const sensors_chip_name* (*sym_sensors_get_detected_chips)(const sensors_chip_name*, int*); |
|
static const sensors_feature* (*sym_sensors_get_features)(const sensors_chip_name*, int*); |
|
static const sensors_subfeature* (*sym_sensors_get_subfeature)(const sensors_chip_name*, const sensors_feature*, sensors_subfeature_type); |
|
static int (*sym_sensors_get_value)(const sensors_chip_name*, int, double*); |
|
|
|
static void* dlopenHandle = NULL; |
|
|
|
#endif /* BUILD_STATIC */ |
|
|
|
int LibSensors_init(void) { |
|
#ifdef BUILD_STATIC |
|
|
|
return sym_sensors_init(NULL); |
|
|
|
#else |
|
|
|
if (!dlopenHandle) { |
|
/* Find the unversioned libsensors.so (symlink) and prefer that, but Debian has .so.5 and Fedora .so.4 without |
|
matching symlinks (unless people install the -dev packages) */ |
|
dlopenHandle = dlopen("libsensors.so", RTLD_LAZY); |
|
if (!dlopenHandle) |
|
dlopenHandle = dlopen("libsensors.so.5", RTLD_LAZY); |
|
if (!dlopenHandle) |
|
dlopenHandle = dlopen("libsensors.so.4", RTLD_LAZY); |
|
if (!dlopenHandle) |
|
goto dlfailure; |
|
|
|
/* Clear any errors */ |
|
dlerror(); |
|
|
|
#define resolve(symbolname) do { \ |
|
*(void **)(&sym_##symbolname) = dlsym(dlopenHandle, #symbolname); \ |
|
if (!sym_##symbolname || dlerror() != NULL) \ |
|
goto dlfailure; \ |
|
} while(0) |
|
|
|
resolve(sensors_init); |
|
resolve(sensors_cleanup); |
|
resolve(sensors_get_detected_chips); |
|
resolve(sensors_get_features); |
|
resolve(sensors_get_subfeature); |
|
resolve(sensors_get_value); |
|
|
|
#undef resolve |
|
} |
|
|
|
return sym_sensors_init(NULL); |
|
|
|
|
|
dlfailure: |
|
if (dlopenHandle) { |
|
dlclose(dlopenHandle); |
|
dlopenHandle = NULL; |
|
} |
|
return -1; |
|
|
|
#endif /* BUILD_STATIC */ |
|
} |
|
|
|
void LibSensors_cleanup(void) { |
|
#ifdef BUILD_STATIC |
|
|
|
sym_sensors_cleanup(); |
|
|
|
#else |
|
|
|
if (dlopenHandle) { |
|
sym_sensors_cleanup(); |
|
|
|
dlclose(dlopenHandle); |
|
dlopenHandle = NULL; |
|
} |
|
|
|
#endif /* BUILD_STATIC */ |
|
} |
|
|
|
int LibSensors_reload(void) { |
|
#ifndef BUILD_STATIC |
|
if (!dlopenHandle) { |
|
errno = ENOTSUP; |
|
return -1; |
|
} |
|
#endif /* !BUILD_STATIC */ |
|
|
|
sym_sensors_cleanup(); |
|
return sym_sensors_init(NULL); |
|
} |
|
|
|
static int tempDriverPriority(const sensors_chip_name* chip) { |
|
static const struct TempDriverDefs { |
|
const char* prefix; |
|
int priority; |
|
} tempDrivers[] = { |
|
{ "coretemp", 0 }, |
|
{ "via_cputemp", 0 }, |
|
{ "cpu_thermal", 0 }, |
|
{ "k10temp", 0 }, |
|
{ "zenpower", 0 }, |
|
/* Low priority drivers */ |
|
{ "acpitz", 1 }, |
|
}; |
|
|
|
for (size_t i = 0; i < ARRAYSIZE(tempDrivers); i++) |
|
if (String_eq(chip->prefix, tempDrivers[i].prefix)) |
|
return tempDrivers[i].priority; |
|
|
|
return -1; |
|
} |
|
|
|
void LibSensors_getCPUTemperatures(CPUData* cpus, unsigned int existingCPUs, unsigned int activeCPUs) { |
|
assert(existingCPUs > 0 && existingCPUs < 16384); |
|
double data[existingCPUs + 1]; |
|
for (size_t i = 0; i < existingCPUs + 1; i++) |
|
data[i] = NAN; |
|
|
|
#ifndef BUILD_STATIC |
|
if (!dlopenHandle) |
|
goto out; |
|
#endif /* !BUILD_STATIC */ |
|
|
|
unsigned int coreTempCount = 0; |
|
int topPriority = 99; |
|
|
|
int n = 0; |
|
for (const sensors_chip_name* chip = sym_sensors_get_detected_chips(NULL, &n); chip; chip = sym_sensors_get_detected_chips(NULL, &n)) { |
|
const int priority = tempDriverPriority(chip); |
|
if (priority < 0) |
|
continue; |
|
|
|
if (priority > topPriority) |
|
continue; |
|
|
|
if (priority < topPriority) { |
|
/* Clear data from lower priority sensor */ |
|
for (size_t i = 0; i < existingCPUs + 1; i++) |
|
data[i] = NAN; |
|
} |
|
|
|
topPriority = priority; |
|
|
|
int m = 0; |
|
for (const sensors_feature* feature = sym_sensors_get_features(chip, &m); feature; feature = sym_sensors_get_features(chip, &m)) { |
|
if (feature->type != SENSORS_FEATURE_TEMP) |
|
continue; |
|
|
|
if (!feature->name || !String_startsWith(feature->name, "temp")) |
|
continue; |
|
|
|
unsigned long int tempID = strtoul(feature->name + strlen("temp"), NULL, 10); |
|
if (tempID == 0 || tempID == ULONG_MAX) |
|
continue; |
|
|
|
/* Feature name IDs start at 1, adjust to start at 0 to match data indices */ |
|
tempID--; |
|
|
|
if (tempID > existingCPUs) |
|
continue; |
|
|
|
const sensors_subfeature* subFeature = sym_sensors_get_subfeature(chip, feature, SENSORS_SUBFEATURE_TEMP_INPUT); |
|
if (!subFeature) |
|
continue; |
|
|
|
double temp; |
|
int r = sym_sensors_get_value(chip, subFeature->number, &temp); |
|
if (r != 0) |
|
continue; |
|
|
|
/* If already set, e.g. Ryzen reporting platform temperature for each die, use the bigger one */ |
|
if (isnan(data[tempID])) { |
|
data[tempID] = temp; |
|
if (tempID > 0) |
|
coreTempCount++; |
|
} else { |
|
data[tempID] = MAXIMUM(data[tempID], temp); |
|
} |
|
} |
|
} |
|
|
|
/* Adjust data for chips not providing a platform temperature */ |
|
if (coreTempCount + 1 == activeCPUs || coreTempCount + 1 == activeCPUs / 2) { |
|
memmove(&data[1], &data[0], existingCPUs * sizeof(*data)); |
|
data[0] = NAN; |
|
coreTempCount++; |
|
|
|
/* Check for further adjustments */ |
|
} |
|
|
|
/* Only package temperature - copy to all cores */ |
|
if (coreTempCount == 0 && !isnan(data[0])) { |
|
for (unsigned int i = 1; i <= existingCPUs; i++) |
|
data[i] = data[0]; |
|
|
|
/* No further adjustments */ |
|
goto out; |
|
} |
|
|
|
/* No package temperature - set to max core temperature */ |
|
if (isnan(data[0]) && coreTempCount != 0) { |
|
double maxTemp = NAN; |
|
for (unsigned int i = 1; i <= existingCPUs; i++) { |
|
if (isnan(data[i])) |
|
continue; |
|
|
|
maxTemp = MAXIMUM(maxTemp, data[i]); |
|
} |
|
|
|
data[0] = maxTemp; |
|
|
|
/* Check for further adjustments */ |
|
} |
|
|
|
/* Only temperature for core 0, maybe Ryzen - copy to all other cores */ |
|
if (coreTempCount == 1 && !isnan(data[1])) { |
|
for (unsigned int i = 2; i <= existingCPUs; i++) |
|
data[i] = data[1]; |
|
|
|
/* No further adjustments */ |
|
goto out; |
|
} |
|
|
|
/* Half the temperatures, probably HT/SMT - copy to second half */ |
|
const unsigned int delta = activeCPUs / 2; |
|
if (coreTempCount == delta) { |
|
memcpy(&data[delta + 1], &data[1], delta * sizeof(*data)); |
|
|
|
/* No further adjustments */ |
|
goto out; |
|
} |
|
|
|
out: |
|
for (unsigned int i = 0; i <= existingCPUs; i++) |
|
cpus[i].temperature = data[i]; |
|
} |
|
|
|
#endif /* HAVE_SENSORS_SENSORS_H */
|
|
|