1
0
Fork 0
mirror of https://git.rwth-aachen.de/acs/public/villas/node/ synced 2025-03-09 00:00:00 +01:00

feat: vfio automask

Signed-off-by: Pascal Bauer <pascal.bauer@rwth-aachen.de>
This commit is contained in:
Pascal Bauer 2025-02-26 12:47:23 +01:00
parent 0682d114c1
commit 7f0ace6291
4 changed files with 96 additions and 25 deletions

View file

@ -28,6 +28,12 @@ namespace vfio {
class Device {
public:
struct IrqVectorInfo {
int eventFds[32];
size_t numFds;
bool automask;
};
Device(const std::string &name, int groupFileDescriptor,
const kernel::devices::PciDevice *pci_device = nullptr);
~Device();
@ -49,6 +55,8 @@ public:
// Get the size of a device memory region
size_t regionGetSize(size_t index);
std::vector<IrqVectorInfo> initEventFds();
// Enable memory accesses and bus mastering for PCI device
bool pciEnable();
@ -86,7 +94,7 @@ private:
struct vfio_device_info info;
std::vector<struct vfio_irq_info> irqs;
std::vector<struct vfio_irq_info> info_irq_vectors;
std::vector<struct vfio_region_info> regions;
std::vector<void *> mappings;

View file

@ -55,7 +55,7 @@ static const char *vfio_pci_irq_names[] = {
Device::Device(const std::string &name, int groupFileDescriptor,
const kernel::devices::PciDevice *pci_device)
: name(name), fd(-1), attachedToGroup(false), groupFd(groupFileDescriptor),
info(), irqs(), regions(), mappings(), pci_device(pci_device),
info(), info_irq_vectors(), regions(), mappings(), pci_device(pci_device),
log(Log::get("kernel:vfio:device")) {
if (groupFileDescriptor < 0)
throw RuntimeError("Invalid group file descriptor");
@ -81,7 +81,7 @@ Device::Device(const std::string &name, int groupFileDescriptor,
info.num_regions = pci_device != 0 ? VFIO_PCI_CONFIG_REGION_INDEX + 1 : 1;
// Reserve slots already so that we can use the []-operator for access
irqs.resize(info.num_irqs);
info_irq_vectors.resize(info.num_irqs);
regions.resize(info.num_regions);
mappings.resize(info.num_regions);
@ -120,7 +120,7 @@ Device::Device(const std::string &name, int groupFileDescriptor,
log->debug("irq {} info: flags: 0x{:x}, count: {}", irq.index, irq.flags,
irq.count);
irqs[i] = irq;
info_irq_vectors[i] = irq;
}
}
@ -212,7 +212,7 @@ void Device::dump() {
}
for (size_t i = 0; i < info.num_irqs; i++) {
struct vfio_irq_info *irq = &irqs[i];
struct vfio_irq_info *irq = &info_irq_vectors[i];
if (irq->count > 0) {
log->info("IRQ {} {}: count={}, flags={}", irq->index,
@ -247,12 +247,57 @@ bool Device::pciEnable() {
return true;
}
std::vector<Device::IrqVectorInfo> Device::initEventFds() {
std::vector<Device::IrqVectorInfo> vectors;
for (auto info_irq_vector : info_irq_vectors) {
Device::IrqVectorInfo irq = {0};
const size_t irqCount = info_irq_vector.count;
const size_t irqSetSize =
sizeof(struct vfio_irq_set) + sizeof(int) * irqCount;
auto *irqSetBuf = new char[irqSetSize];
if (!irqSetBuf)
throw MemoryAllocationError();
auto *irqSet = reinterpret_cast<struct vfio_irq_set *>(irqSetBuf);
irqSet->argsz = irqSetSize;
// DATA_EVENTFD binds the interrupt to the provided eventfd.
// SET_ACTION_TRIGGER enables kernel->userspace signalling.
irqSet->flags = VFIO_IRQ_SET_DATA_EVENTFD | VFIO_IRQ_SET_ACTION_TRIGGER;
irqSet->index = info_irq_vector.index;
irqSet->start = 0;
irqSet->count = irqCount;
irq.automask = info_irq_vector.flags & VFIO_IRQ_INFO_AUTOMASKED;
irq.numFds = irqCount;
// Now set the new eventfds
for (size_t i = 0; i < irqCount; i++) {
irq.eventFds[i] = eventfd(0, 0);
if (fd < 0) {
delete[] irqSetBuf;
throw std::runtime_error("Event FD could not be created.");
}
eventfdList.push_back(fd);
}
memcpy(irqSet->data, irq.eventFds, sizeof(int) * irqCount);
if (ioctl(fd, VFIO_DEVICE_SET_IRQS, irqSet) != 0) {
delete[] irqSetBuf;
throw std::runtime_error("failed to set irqs");
}
delete[] irqSetBuf;
vectors.push_back(irq);
}
return vectors;
}
int Device::pciMsiInit(int efds[]) {
// Check if this is really a vfio-pci device
if (not isVfioPciDevice())
return -1;
const size_t irqCount = irqs[VFIO_PCI_MSI_IRQ_INDEX].count;
const size_t irqCount = info_irq_vectors[VFIO_PCI_MSI_IRQ_INDEX].count;
const size_t irqSetSize =
sizeof(struct vfio_irq_set) + sizeof(int) * irqCount;
@ -299,7 +344,7 @@ int Device::pciMsiDeinit(int efds[]) {
if (not isVfioPciDevice())
return -1;
const size_t irqCount = irqs[VFIO_PCI_MSI_IRQ_INDEX].count;
const size_t irqCount = info_irq_vectors[VFIO_PCI_MSI_IRQ_INDEX].count;
const size_t irqSetSize =
sizeof(struct vfio_irq_set) + sizeof(int) * irqCount;

View file

@ -53,8 +53,7 @@ private:
bool polling; // Polled or not
};
int num_irqs; // Number of available MSI vectors
int efds[maxIrqs];
std::vector<kernel::vfio::Device::IrqVectorInfo> irq_vectors;
int nos[maxIrqs];
bool polling[maxIrqs];
};

View file

@ -6,6 +6,7 @@
*/
#include <errno.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <villas/config.hpp>
@ -22,28 +23,30 @@ using namespace villas::fpga::ip;
InterruptController::~InterruptController() {}
bool InterruptController::stop() {
return this->vfioDevice->pciMsiDeinit(this->efds) > 0;
return this->vfioDevice->pciMsiDeinit(irq_vectors[0].eventFds) > 0;
}
bool InterruptController::init() {
const uintptr_t base = getBaseAddr(registerMemory);
PCIeCard *pciecard = dynamic_cast<PCIeCard *>(card);
this->vfioDevice = pciecard->vfioDevice;
num_irqs = this->vfioDevice->pciMsiInit(efds);
if (num_irqs < 0)
return false;
if (not this->vfioDevice->pciMsiFind(nos)) {
return false;
}
const uintptr_t base = getBaseAddr(registerMemory);
kernel::vfio::Device::IrqVectorInfo irq_vector = {0};
irq_vector.numFds = this->vfioDevice->pciMsiInit(irq_vector.eventFds);
irq_vector.automask = true;
irq_vectors.push_back(irq_vector);
if (irq_vector.numFds < 0)
return false;
// For each IRQ
for (int i = 0; i < num_irqs; i++) {
for (size_t i = 0; i < irq_vector.numFds; i++) {
// Try pinning to core
PCIeCard *pciecard = dynamic_cast<PCIeCard *>(card);
int ret = kernel::setIRQAffinity(nos[i], pciecard->affinity, nullptr);
switch (ret) {
@ -89,7 +92,7 @@ bool InterruptController::enableInterrupt(InterruptController::IrqMaskType mask,
// Clear pending IRQs
XIntc_Out32(base + XIN_IAR_OFFSET, mask);
for (int i = 0; i < num_irqs; i++) {
for (size_t i = 0; i < irq_vectors[0].numFds; i++) {
if (mask & (1 << i))
this->polling[i] = polling;
}
@ -120,6 +123,7 @@ bool InterruptController::disableInterrupt(
return true;
}
// Assuming only one interrupt vector
ssize_t InterruptController::waitForInterrupt(int irq) {
assert(irq < maxIrqs);
@ -144,22 +148,37 @@ ssize_t InterruptController::waitForInterrupt(int irq) {
fd_set rfds;
struct timeval tv = {.tv_sec = 1, .tv_usec = 0};
FD_ZERO(&rfds);
FD_SET(efds[irq], &rfds);
FD_SET(irq_vectors[0].eventFds[irq], &rfds);
logger->debug("Waiting for interrupt fd {}", irq_vectors[0].eventFds[irq]);
sret = select(efds[irq] + 1, &rfds, NULL, NULL, &tv);
sret = select(irq_vectors[0].eventFds[irq] + 1, &rfds, NULL, NULL, &tv);
if (sret == -1) {
logger->error("select() failed: {}", strerror(errno));
return -1;
} else if (sret == 0) {
logger->warn("timeout waiting for interrupt {}", irq);
return -1;
}
// Block until there has been an interrupt, read number of interrupts
ssize_t ret = read(efds[irq], &count, sizeof(count));
// Block until there has been an interrupt, read number of interruptsvfio_device
ssize_t ret = read(irq_vectors[0].eventFds[irq], &count, sizeof(count));
if (ret != sizeof(count)) {
logger->error("Failed to read from eventfd: {}", strerror(errno));
logger->error("Read failure on interrupt {}, {}", irq, ret);
return -1;
} else {
logger->debug("Automask: {}", irq_vectors[0].automask);
if (irq_vectors[0].automask) {
struct vfio_irq_set irqSet = {0};
irqSet.argsz = sizeof(struct vfio_irq_set);
irqSet.flags = (VFIO_IRQ_SET_DATA_NONE | VFIO_IRQ_SET_ACTION_UNMASK);
irqSet.index = 0;
irqSet.start = irq;
irqSet.count = 1;
int ret = ioctl(this->vfioDevice->getFileDescriptor(),
VFIO_DEVICE_SET_IRQS, &irqSet);
if (ret < 0) {
logger->error("Failed to unmask IRQ {}", 0);
}
}
}
return count;