1
0
Fork 0
mirror of https://git.rwth-aachen.de/acs/public/villas/node/ synced 2025-03-09 00:00:00 +01:00
VILLASnode/lib/kernel/if.cpp
Steffen Vogel 02a2aa4f94 Apply clang-format changes
Signed-off-by: Steffen Vogel <steffen.vogel@opal-rt.com>
2023-09-08 11:37:42 +02:00

202 lines
5 KiB
C++

/* Interface related functions.
*
* 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
*/
#include <cstdio>
#include <cstdlib>
#include <dirent.h>
#include <netlink/route/link.h>
#include <villas/cpuset.hpp>
#include <villas/exceptions.hpp>
#include <villas/node/config.hpp>
#include <villas/super_node.hpp>
#include <villas/utils.hpp>
#include <villas/kernel/if.hpp>
#include <villas/kernel/kernel.hpp>
#include <villas/kernel/nl.hpp>
#include <villas/kernel/tc.hpp>
#include <villas/kernel/tc_netem.hpp>
#include <villas/nodes/socket.hpp>
using namespace villas;
using namespace villas::node;
using namespace villas::utils;
using namespace villas::kernel;
Interface::Interface(struct rtnl_link *link, int aff)
: nl_link(link), tc_qdisc(nullptr), affinity(aff) {
logger = logging.get(fmt::format("kernel:if:{}", getName()));
int n = getIRQs();
if (n)
logger->warn("Did not found any interrupts");
logger->debug("Found {} IRQs", irqs.size());
}
Interface::~Interface() {
if (tc_qdisc)
rtnl_qdisc_put(tc_qdisc);
}
int Interface::start() {
logger->info("Starting interface which is used by {} nodes", nodes.size());
// Set affinity for network interfaces (skip _loopback_ dev)
if (affinity)
setAffinity(affinity);
// Assign fwmark's to nodes which have netem options
int ret, fwmark = 0;
for (auto *n : nodes) {
if (n->tc_qdisc && n->fwmark < 0)
n->fwmark = 1 + fwmark++;
}
// Abort if no node is using netem
if (fwmark == 0)
return 0;
if (getuid() != 0)
throw RuntimeError("Network emulation requires super-user privileges!");
// Replace root qdisc
ret = tc::prio(this, &tc_qdisc, TC_HANDLE(1, 0), TC_H_ROOT, fwmark);
if (ret)
throw RuntimeError("Failed to setup priority queuing discipline: {}",
nl_geterror(ret));
// Create netem qdisks and appropriate filter per netem node
for (auto *n : nodes) {
if (n->tc_qdisc) {
ret =
tc::mark(this, &n->tc_classifier, TC_HANDLE(1, n->fwmark), n->fwmark);
if (ret)
throw RuntimeError("Failed to setup FW mark classifier: {}",
nl_geterror(ret));
char *buf = tc::netem_print(n->tc_qdisc);
logger->debug("Starting network emulation for FW mark {}: {}", n->fwmark,
buf);
free(buf);
ret = tc::netem(this, &n->tc_qdisc, TC_HANDLE(0x1000 + n->fwmark, 0),
TC_HANDLE(1, n->fwmark));
if (ret)
throw RuntimeError("Failed to setup netem qdisc: {}", nl_geterror(ret));
}
}
return 0;
}
int Interface::stop() {
logger->info("Stopping interface");
if (affinity)
setAffinity(-1L);
if (tc_qdisc)
tc::reset(this);
return 0;
}
std::string Interface::getName() const {
auto str = rtnl_link_get_name(nl_link);
return std::string(str);
}
Interface *Interface::getEgress(struct sockaddr *sa, SuperNode *sn) {
struct rtnl_link *link;
Logger logger = logging.get("kernel:if");
auto &interfaces = sn->getInterfaces();
auto affinity = sn->getAffinity();
// Determine outgoing interface
link = nl::get_egress_link(sa);
if (!link)
throw RuntimeError("Failed to get interface for socket address '{}'",
socket_print_addr(sa));
// Search of existing interface with correct ifindex
for (auto *i : interfaces) {
if (rtnl_link_get_ifindex(i->nl_link) == rtnl_link_get_ifindex(link))
return i;
}
// If not found, create a new interface
auto *i = new Interface(link, affinity);
if (!i)
throw MemoryAllocationError();
interfaces.push_back(i);
return i;
}
int Interface::getIRQs() {
int irq;
auto dirname = fmt::format("/sys/class/net/{}/device/msi_irqs/", getName());
DIR *dir = opendir(dirname.c_str());
if (dir) {
irqs.clear();
struct dirent *entry;
while ((entry = readdir(dir))) {
irq = atoi(entry->d_name);
if (irq)
irqs.push_back(irq);
}
closedir(dir);
}
return 0;
}
int Interface::setAffinity(int affinity) {
assert(affinity != 0);
if (getuid() != 0) {
logger->warn("Failed to tune IRQ affinity. Please run as super-user");
return 0;
}
FILE *file;
CpuSet cset_pin(affinity);
for (int irq : irqs) {
std::string filename = fmt::format("/proc/irq/{}/smp_affinity", irq);
file = fopen(filename.c_str(), "w");
if (file) {
if (fprintf(file, "%8lx", (unsigned long)cset_pin) < 0)
throw SystemError(
"Failed to set affinity for for IRQ {} on interface '{}'", irq,
getName());
fclose(file);
logger->debug("Set affinity of IRQ {} to {} {}", irq,
cset_pin.count() == 1 ? "core" : "cores",
(std::string)cset_pin);
} else
throw SystemError(
"Failed to set affinity for for IRQ {} on interface '{}'", irq,
getName());
}
return 0;
}