1
0
Fork 0
mirror of https://git.rwth-aachen.de/acs/public/villas/node/ synced 2025-03-16 00:00:02 +01:00
VILLASnode/common/lib/kernel/kernel.cpp

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

341 lines
6.2 KiB
C++
Raw Normal View History

/* Linux kernel related functions.
2018-08-27 11:11:28 +02:00
*
* Author: Steffen Vogel <post@steffenvogel.de>
* SPDX-FileCopyrightText: 2014-2023 Institute for Automation of Complex Power Systems, RWTH Aachen University
* SPDX-License-Identifier: Apache-2.0
*/
2018-08-27 11:11:28 +02:00
#include <sys/utsname.h>
#include <cstdio>
#include <cstring>
#include <ctime>
2019-04-07 15:12:32 +02:00
#include <unistd.h>
#include <fcntl.h>
#include <cinttypes>
2019-04-07 15:12:32 +02:00
#include <sys/types.h>
#include <sys/types.h>
#include <sys/wait.h>
2021-02-16 14:15:38 +01:00
#include <villas/log.hpp>
2019-04-23 12:57:51 +02:00
#include <villas/utils.hpp>
2021-08-11 12:40:19 -04:00
#include <villas/config.hpp>
2019-10-27 20:23:47 +01:00
#include <villas/kernel/kernel.hpp>
2018-08-27 11:11:28 +02:00
#include <villas/kernel/kernel.hpp>
#include <villas/exceptions.hpp>
2021-02-16 14:15:38 +01:00
using namespace villas;
2018-08-27 11:11:28 +02:00
using namespace villas::utils;
Version villas::kernel::getVersion()
{
struct utsname uts;
if (uname(&uts) < 0)
throw SystemError("Failed to retrieve system identification");
2018-08-27 11:11:28 +02:00
std::string rel = uts.release;
// Remove release part. E.g. 4.9.93-linuxkit-aufs
2018-08-27 11:11:28 +02:00
auto sep = rel.find('-');
auto ver = rel.substr(0, sep - 1);
return Version(ver);
}
2019-04-07 15:12:32 +02:00
2021-05-20 06:08:12 -04:00
int villas::kernel::getCachelineSize()
2019-04-07 15:12:32 +02:00
{
2020-11-11 21:23:53 +01:00
#if defined(__linux__) && defined(__x86_64__) && defined(__GLIBC__)
2019-04-07 15:12:32 +02:00
return sysconf(_SC_LEVEL1_ICACHE_LINESIZE);
#elif defined(__MACH__)
// Open the command for reading.
2019-04-07 15:12:32 +02:00
FILE *fp = popen("sysctl -n machdep.cpu.cache.linesize", "r");
2019-04-08 10:42:45 +02:00
if (fp == nullptr)
2019-04-07 15:12:32 +02:00
return -1;
int ret, size;
ret = fscanf(fp, "%d", &size);
pclose(fp);
return ret == 1 ? size : -1;
#else
return CACHELINE_SIZE;
#endif
}
2022-03-14 19:55:05 -04:00
#if defined(__linux__)
2021-05-20 06:08:12 -04:00
int villas::kernel::getPageSize()
2019-04-07 15:12:32 +02:00
{
return sysconf(_SC_PAGESIZE);
}
#else
#error "Unsupported platform"
#endif
// There is no sysconf interface to get the hugepage size
2021-05-20 06:08:12 -04:00
int villas::kernel::getHugePageSize()
2019-04-07 15:12:32 +02:00
{
#ifdef __linux__
2019-04-08 10:42:45 +02:00
char *key, *value, *unit, *line = nullptr, *lasts;
2019-04-07 15:12:32 +02:00
int sz = -1;
size_t len = 0;
FILE *f;
f = fopen(PROCFS_PATH "/meminfo", "r");
if (!f)
return -1;
while (getline(&line, &len, f) != -1) {
key = strtok_r(line, ": ", &lasts);
2019-04-08 10:42:45 +02:00
value = strtok_r(nullptr, " ", &lasts);
unit = strtok_r(nullptr, "\n", &lasts);
2019-04-07 15:12:32 +02:00
if (!strcmp(key, "Hugepagesize") && !strcmp(unit, "kB")) {
2019-04-08 10:42:45 +02:00
sz = strtoul(value, nullptr, 10) * 1024;
2019-04-07 15:12:32 +02:00
break;
}
}
free(line);
fclose(f);
return sz;
#elif defined(__x86_64__)
return 1 << 21;
#elif defined(__i386__)
return 1 << 22;
#else
#error "Unsupported architecture"
#endif
}
#ifdef __linux__
2021-05-20 06:08:12 -04:00
int villas::kernel::setModuleParam(const char *module, const char *param, const char *value)
2019-04-07 15:12:32 +02:00
{
FILE *f;
char fn[256];
snprintf(fn, sizeof(fn), "%s/module/%s/parameters/%s", SYSFS_PATH, module, param);
f = fopen(fn, "w");
if (!f)
throw RuntimeError("Failed set parameter {} for kernel module {} to {}", module, param, value);
2019-04-07 15:12:32 +02:00
2021-02-16 14:15:38 +01:00
auto logger = logging.get("kernel");
logger->debug("Set parameter {} of kernel module {} to {}", module, param, value);
2019-04-07 15:12:32 +02:00
fprintf(f, "%s", value);
fclose(f);
return 0;
}
2021-05-20 06:08:12 -04:00
int villas::kernel::loadModule(const char *module)
2019-04-07 15:12:32 +02:00
{
int ret;
2021-05-20 06:08:12 -04:00
ret = isModuleLoaded(module);
2019-04-07 15:12:32 +02:00
if (!ret) {
2021-02-16 14:15:38 +01:00
auto logger = logging.get("kernel");
logger->debug("Kernel module {} already loaded...", module);
2019-04-07 15:12:32 +02:00
return 0;
}
pid_t pid = fork();
switch (pid) {
case -1: // Error
2019-04-07 15:12:32 +02:00
return -1;
case 0: // Child
2019-04-07 15:12:32 +02:00
execlp("modprobe", "modprobe", module, (char *) 0);
exit(EXIT_FAILURE); // exec() never returns
2019-04-07 15:12:32 +02:00
default:
wait(&ret);
2021-05-20 06:08:12 -04:00
return isModuleLoaded(module);
2019-04-07 15:12:32 +02:00
}
}
2021-05-20 06:08:12 -04:00
int villas::kernel::isModuleLoaded(const char *module)
2019-04-07 15:12:32 +02:00
{
FILE *f;
int ret = -1;
2019-04-08 10:42:45 +02:00
char *line = nullptr;
2019-04-07 15:12:32 +02:00
size_t len = 0;
f = fopen(PROCFS_PATH "/modules", "r");
if (!f)
return -1;
while (getline(&line, &len, f) >= 0) {
if (strstr(line, module) == line) {
ret = 0;
break;
}
}
free(line);
fclose(f);
return ret;
}
2021-05-20 06:08:12 -04:00
int villas::kernel::getCmdlineParam(const char *param, char *buf, size_t len)
2019-04-07 15:12:32 +02:00
{
int ret;
char cmdline[512], key[128], value[128], *lasts, *tok;
FILE *f = fopen(PROCFS_PATH "/cmdline", "r");
if (!f)
return -1;
if (!fgets(cmdline, sizeof(cmdline), f))
goto out;
tok = strtok_r(cmdline, " \t", &lasts);
do {
ret = sscanf(tok, "%127[^=]=%127s", key, value);
if (ret >= 1) {
2021-02-16 14:15:38 +01:00
auto logger = logging.get("kernel");
2019-04-07 15:12:32 +02:00
if (ret >= 2)
2021-02-16 14:15:38 +01:00
logger->debug("Found kernel param: {}={}", key, value);
2019-04-07 15:12:32 +02:00
else
2021-02-16 14:15:38 +01:00
logger->debug("Found kernel param: {}", key);
2019-04-07 15:12:32 +02:00
if (strcmp(param, key) == 0) {
if (ret >= 2 && buf)
snprintf(buf, len, "%s", value);
return 0; // Found
2019-04-07 15:12:32 +02:00
}
}
2019-04-08 10:42:45 +02:00
} while ((tok = strtok_r(nullptr, " \t", &lasts)));
2019-04-07 15:12:32 +02:00
out:
fclose(f);
return -1; // Not found or error
2019-04-07 15:12:32 +02:00
}
2021-05-20 06:08:12 -04:00
int villas::kernel::getNrHugepages()
2019-04-07 15:12:32 +02:00
{
FILE *f;
int nr, ret;
f = fopen(PROCFS_PATH "/sys/vm/nr_hugepages", "r");
2021-02-16 14:15:38 +01:00
if (!f) {
auto logger = logging.get("kernel");
logger->error("Failed to open {}: {}", PROCFS_PATH "/sys/vm/nr_hugepages", strerror(errno));
return -1;
}
2019-04-07 15:12:32 +02:00
ret = fscanf(f, "%d", &nr);
if (ret != 1)
nr = -1;
fclose(f);
return nr;
}
2021-05-20 06:08:12 -04:00
int villas::kernel::setNrHugepages(int nr)
2019-04-07 15:12:32 +02:00
{
FILE *f;
f = fopen(PROCFS_PATH "/sys/vm/nr_hugepages", "w");
2021-09-19 19:05:53 +02:00
if (!f)
return -1;
2019-06-24 12:43:49 +00:00
2019-04-07 15:12:32 +02:00
fprintf(f, "%d\n", nr);
fclose(f);
return 0;
}
2021-05-20 06:08:12 -04:00
int villas::kernel::setIRQAffinity(unsigned irq, uintmax_t aff, uintmax_t *old)
2019-04-07 15:12:32 +02:00
{
char fn[64];
FILE *f;
int ret = 0;
snprintf(fn, sizeof(fn), "/proc/irq/%u/smp_affinity", irq);
f = fopen(fn, "w+");
if (!f)
return -1; // IRQ does not exist
2019-04-07 15:12:32 +02:00
if (old)
ret = fscanf(f, "%jx", old);
fprintf(f, "%jx", aff);
fclose(f);
return ret;
}
2019-10-27 20:23:47 +01:00
int villas::kernel::get_cpu_frequency(uint64_t *freq)
2019-04-07 15:12:32 +02:00
{
2019-04-08 10:42:45 +02:00
char *line = nullptr, *sep, *end;
2019-04-07 15:12:32 +02:00
size_t len = 0;
double dfreq;
int ret;
FILE *f;
// Try to get CPU frequency from cpufreq module
2019-04-07 15:12:32 +02:00
f = fopen(SYSFS_PATH "/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq", "r");
if (!f)
goto cpuinfo;
ret = fscanf(f, "%" PRIu64, freq);
fclose(f);
if (ret != 1)
return -1;
// cpufreq reports kHz
2019-04-07 15:12:32 +02:00
*freq = *freq * 1000;
return 0;
cpuinfo:
// Try to read CPU frequency from /proc/cpuinfo
2019-04-07 15:12:32 +02:00
f = fopen(PROCFS_PATH "/cpuinfo", "r");
if (!f)
return -1; // We give up here
2019-04-07 15:12:32 +02:00
ret = -1;
while (getline(&line, &len, f) >= 0) {
if (strstr(line, "cpu MHz") == line) {
ret = 0;
break;
}
}
if (ret)
goto out;
sep = strchr(line, ':');
if (!sep) {
ret = -1;
goto out;
}
dfreq = strtod(sep+1, &end);
if (end == sep+1) {
ret = -1;
goto out;
}
// Frequency is given in MHz
2019-04-07 15:12:32 +02:00
*freq = dfreq * 1e6;
out: fclose(f);
free(line);
return ret;
}
#endif // __linux__