diff --git a/common/include/villas/kernel/vfio_device.hpp b/common/include/villas/kernel/vfio_device.hpp index ca49c2e1f..0996c2eef 100644 --- a/common/include/villas/kernel/vfio_device.hpp +++ b/common/include/villas/kernel/vfio_device.hpp @@ -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 initEventFds(); + // Enable memory accesses and bus mastering for PCI device bool pciEnable(); @@ -86,7 +94,7 @@ private: struct vfio_device_info info; - std::vector irqs; + std::vector info_irq_vectors; std::vector regions; std::vector mappings; diff --git a/common/lib/kernel/vfio_device.cpp b/common/lib/kernel/vfio_device.cpp index ff70f3cb7..6a60b9ffd 100644 --- a/common/lib/kernel/vfio_device.cpp +++ b/common/lib/kernel/vfio_device.cpp @@ -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::initEventFds() { + std::vector 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(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; diff --git a/fpga/include/villas/fpga/ips/intc.hpp b/fpga/include/villas/fpga/ips/intc.hpp index cddf93744..a682582d0 100644 --- a/fpga/include/villas/fpga/ips/intc.hpp +++ b/fpga/include/villas/fpga/ips/intc.hpp @@ -53,8 +53,7 @@ private: bool polling; // Polled or not }; - int num_irqs; // Number of available MSI vectors - int efds[maxIrqs]; + std::vector irq_vectors; int nos[maxIrqs]; bool polling[maxIrqs]; }; diff --git a/fpga/lib/ips/intc.cpp b/fpga/lib/ips/intc.cpp index 238a3827d..8b3e6a12f 100644 --- a/fpga/lib/ips/intc.cpp +++ b/fpga/lib/ips/intc.cpp @@ -6,6 +6,7 @@ */ #include +#include #include #include @@ -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(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(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;