2025-01-14 14:42:39 +00:00
|
|
|
/* Interface related functions.
|
2014-06-05 09:35:40 +00:00
|
|
|
*
|
2025-01-14 14:42:39 +00: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
|
|
|
|
*/
|
2014-06-05 09:35:40 +00:00
|
|
|
|
2019-06-23 16:57:00 +02:00
|
|
|
#include <cstdio>
|
|
|
|
#include <cstdlib>
|
2014-06-05 09:35:40 +00:00
|
|
|
#include <dirent.h>
|
|
|
|
|
2015-08-22 17:42:02 +02:00
|
|
|
#include <netlink/route/link.h>
|
|
|
|
|
2025-01-14 14:42:39 +00:00
|
|
|
#include <villas/cpuset.hpp>
|
2020-07-04 16:22:10 +02:00
|
|
|
#include <villas/exceptions.hpp>
|
2025-01-14 14:42:39 +00:00
|
|
|
#include <villas/node/config.hpp>
|
2020-09-13 11:01:20 +02:00
|
|
|
#include <villas/super_node.hpp>
|
2025-01-14 14:42:39 +00:00
|
|
|
#include <villas/utils.hpp>
|
2016-06-14 01:17:58 +02:00
|
|
|
|
2020-09-13 11:01:20 +02:00
|
|
|
#include <villas/kernel/if.hpp>
|
2025-01-14 14:42:39 +00:00
|
|
|
#include <villas/kernel/kernel.hpp>
|
|
|
|
#include <villas/kernel/nl.hpp>
|
2020-09-13 11:01:20 +02:00
|
|
|
#include <villas/kernel/tc.hpp>
|
|
|
|
#include <villas/kernel/tc_netem.hpp>
|
2016-06-14 01:17:58 +02:00
|
|
|
|
2019-04-23 00:12:31 +02:00
|
|
|
#include <villas/nodes/socket.hpp>
|
2014-06-05 09:35:40 +00:00
|
|
|
|
2020-07-04 16:22:10 +02:00
|
|
|
using namespace villas;
|
2020-09-13 11:01:20 +02:00
|
|
|
using namespace villas::node;
|
2019-06-04 16:55:38 +02:00
|
|
|
using namespace villas::utils;
|
2020-09-13 11:01:20 +02:00
|
|
|
using namespace villas::kernel;
|
2019-06-04 16:55:38 +02:00
|
|
|
|
2025-01-14 14:42:39 +00:00
|
|
|
Interface::Interface(struct rtnl_link *link, int aff)
|
|
|
|
: nl_link(link), tc_qdisc(nullptr), affinity(aff) {
|
|
|
|
logger = Log::get(fmt::format("kernel:if:{}", getName()));
|
2020-09-10 11:11:42 +02:00
|
|
|
|
2025-01-14 14:42:39 +00:00
|
|
|
int n = getIRQs();
|
|
|
|
if (n)
|
|
|
|
logger->warn("Did not found any interrupts");
|
2014-06-05 09:35:40 +00:00
|
|
|
|
2025-01-14 14:42:39 +00:00
|
|
|
logger->debug("Found {} IRQs", irqs.size());
|
2014-12-05 12:39:52 +01:00
|
|
|
}
|
2014-06-05 09:35:40 +00:00
|
|
|
|
2025-01-14 14:42:39 +00:00
|
|
|
Interface::~Interface() {
|
|
|
|
if (tc_qdisc)
|
|
|
|
rtnl_qdisc_put(tc_qdisc);
|
2015-03-21 15:23:57 +01:00
|
|
|
}
|
|
|
|
|
2025-01-14 14:42:39 +00:00
|
|
|
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;
|
2014-12-05 12:39:52 +01:00
|
|
|
}
|
|
|
|
|
2025-01-14 14:42:39 +00:00
|
|
|
int Interface::stop() {
|
|
|
|
logger->info("Stopping interface");
|
2014-12-05 12:39:52 +01:00
|
|
|
|
2025-01-14 14:42:39 +00:00
|
|
|
if (affinity)
|
|
|
|
setAffinity(-1L);
|
2015-08-07 01:11:43 +02:00
|
|
|
|
2025-01-14 14:42:39 +00:00
|
|
|
if (tc_qdisc)
|
|
|
|
tc::reset(this);
|
2014-12-05 12:39:52 +01:00
|
|
|
|
2025-01-14 14:42:39 +00:00
|
|
|
return 0;
|
2014-12-05 12:39:52 +01:00
|
|
|
}
|
|
|
|
|
2025-01-14 14:42:39 +00:00
|
|
|
std::string Interface::getName() const {
|
|
|
|
auto str = rtnl_link_get_name(nl_link);
|
2020-09-13 11:01:20 +02:00
|
|
|
|
2025-01-14 14:42:39 +00:00
|
|
|
return std::string(str);
|
2019-01-21 15:49:14 +01:00
|
|
|
}
|
|
|
|
|
2025-01-14 14:42:39 +00:00
|
|
|
Interface *Interface::getEgress(struct sockaddr *sa, SuperNode *sn) {
|
|
|
|
struct rtnl_link *link;
|
2019-01-21 15:50:18 +01:00
|
|
|
|
2025-01-14 14:42:39 +00:00
|
|
|
Logger logger = Log::get("kernel:if");
|
2019-01-21 15:50:18 +01:00
|
|
|
|
2025-01-14 14:42:39 +00:00
|
|
|
auto &interfaces = sn->getInterfaces();
|
|
|
|
auto affinity = sn->getAffinity();
|
2019-01-21 15:50:18 +01:00
|
|
|
|
2025-01-14 14:42:39 +00:00
|
|
|
// Determine outgoing interface
|
|
|
|
link = nl::get_egress_link(sa);
|
|
|
|
if (!link)
|
|
|
|
throw RuntimeError("Failed to get interface for socket address '{}'",
|
|
|
|
socket_print_addr(sa));
|
2019-01-21 15:50:18 +01:00
|
|
|
|
2025-01-14 14:42:39 +00:00
|
|
|
// 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;
|
|
|
|
}
|
2019-01-21 15:50:18 +01:00
|
|
|
|
2025-01-14 14:42:39 +00:00
|
|
|
// If not found, create a new interface
|
|
|
|
auto *i = new Interface(link, affinity);
|
|
|
|
if (!i)
|
|
|
|
throw MemoryAllocationError();
|
2019-01-21 15:50:18 +01:00
|
|
|
|
2025-01-14 14:42:39 +00:00
|
|
|
interfaces.push_back(i);
|
2019-01-21 23:00:55 +01:00
|
|
|
|
2025-01-14 14:42:39 +00:00
|
|
|
return i;
|
2019-01-21 15:50:18 +01:00
|
|
|
}
|
|
|
|
|
2025-01-14 14:42:39 +00:00
|
|
|
int Interface::getIRQs() {
|
|
|
|
int irq;
|
2014-06-05 09:35:40 +00:00
|
|
|
|
2025-01-14 14:42:39 +00:00
|
|
|
auto dirname = fmt::format("/sys/class/net/{}/device/msi_irqs/", getName());
|
|
|
|
DIR *dir = opendir(dirname.c_str());
|
|
|
|
if (dir) {
|
|
|
|
irqs.clear();
|
2014-06-05 09:35:40 +00:00
|
|
|
|
2025-01-14 14:42:39 +00:00
|
|
|
struct dirent *entry;
|
|
|
|
while ((entry = readdir(dir))) {
|
|
|
|
irq = atoi(entry->d_name);
|
|
|
|
if (irq)
|
|
|
|
irqs.push_back(irq);
|
|
|
|
}
|
2014-06-25 01:53:46 +00:00
|
|
|
|
2025-01-14 14:42:39 +00:00
|
|
|
closedir(dir);
|
|
|
|
}
|
2014-06-25 01:53:46 +00:00
|
|
|
|
2025-01-14 14:42:39 +00:00
|
|
|
return 0;
|
2014-06-25 01:53:46 +00:00
|
|
|
}
|
|
|
|
|
2025-01-14 14:42:39 +00:00
|
|
|
int Interface::setAffinity(int affinity) {
|
|
|
|
assert(affinity != 0);
|
2020-09-13 11:01:20 +02:00
|
|
|
|
2025-01-14 14:42:39 +00:00
|
|
|
if (getuid() != 0) {
|
|
|
|
logger->warn("Failed to tune IRQ affinity. Please run as super-user");
|
|
|
|
return 0;
|
|
|
|
}
|
2020-09-13 11:01:20 +02:00
|
|
|
|
2025-01-14 14:42:39 +00:00
|
|
|
FILE *file;
|
2014-06-25 01:53:46 +00:00
|
|
|
|
2025-01-14 14:42:39 +00:00
|
|
|
CpuSet cset_pin(affinity);
|
2020-09-13 11:01:20 +02:00
|
|
|
|
2025-01-14 14:42:39 +00:00
|
|
|
for (int irq : irqs) {
|
|
|
|
std::string filename = fmt::format("/proc/irq/{}/smp_affinity", irq);
|
2014-06-25 01:53:46 +00:00
|
|
|
|
2025-01-14 14:42:39 +00:00
|
|
|
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());
|
2014-06-05 09:35:40 +00:00
|
|
|
|
2025-01-14 14:42:39 +00:00
|
|
|
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());
|
|
|
|
}
|
2014-06-05 09:35:40 +00:00
|
|
|
|
2025-01-14 14:42:39 +00:00
|
|
|
return 0;
|
2014-06-05 09:35:40 +00:00
|
|
|
}
|