mirror of
https://git.rwth-aachen.de/acs/public/villas/node/
synced 2025-03-30 00:00:11 +01:00
feat: vfio automask
Signed-off-by: Pascal Bauer <pascal.bauer@rwth-aachen.de>
This commit is contained in:
parent
9989f8ffa3
commit
cd2851679c
4 changed files with 96 additions and 25 deletions
|
@ -28,6 +28,12 @@ namespace vfio {
|
||||||
|
|
||||||
class Device {
|
class Device {
|
||||||
public:
|
public:
|
||||||
|
struct IrqVectorInfo {
|
||||||
|
int eventFds[32];
|
||||||
|
size_t numFds;
|
||||||
|
bool automask;
|
||||||
|
};
|
||||||
|
|
||||||
Device(const std::string &name, int groupFileDescriptor,
|
Device(const std::string &name, int groupFileDescriptor,
|
||||||
const kernel::devices::PciDevice *pci_device = nullptr);
|
const kernel::devices::PciDevice *pci_device = nullptr);
|
||||||
~Device();
|
~Device();
|
||||||
|
@ -49,6 +55,8 @@ public:
|
||||||
// Get the size of a device memory region
|
// Get the size of a device memory region
|
||||||
size_t regionGetSize(size_t index);
|
size_t regionGetSize(size_t index);
|
||||||
|
|
||||||
|
std::vector<IrqVectorInfo> initEventFds();
|
||||||
|
|
||||||
// Enable memory accesses and bus mastering for PCI device
|
// Enable memory accesses and bus mastering for PCI device
|
||||||
bool pciEnable();
|
bool pciEnable();
|
||||||
|
|
||||||
|
@ -86,7 +94,7 @@ private:
|
||||||
|
|
||||||
struct vfio_device_info info;
|
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<struct vfio_region_info> regions;
|
||||||
std::vector<void *> mappings;
|
std::vector<void *> mappings;
|
||||||
|
|
||||||
|
|
|
@ -55,7 +55,7 @@ static const char *vfio_pci_irq_names[] = {
|
||||||
Device::Device(const std::string &name, int groupFileDescriptor,
|
Device::Device(const std::string &name, int groupFileDescriptor,
|
||||||
const kernel::devices::PciDevice *pci_device)
|
const kernel::devices::PciDevice *pci_device)
|
||||||
: name(name), fd(-1), attachedToGroup(false), groupFd(groupFileDescriptor),
|
: 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")) {
|
log(Log::get("kernel:vfio:device")) {
|
||||||
if (groupFileDescriptor < 0)
|
if (groupFileDescriptor < 0)
|
||||||
throw RuntimeError("Invalid group file descriptor");
|
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;
|
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
|
// 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);
|
regions.resize(info.num_regions);
|
||||||
mappings.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,
|
log->debug("irq {} info: flags: 0x{:x}, count: {}", irq.index, irq.flags,
|
||||||
irq.count);
|
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++) {
|
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) {
|
if (irq->count > 0) {
|
||||||
log->info("IRQ {} {}: count={}, flags={}", irq->index,
|
log->info("IRQ {} {}: count={}, flags={}", irq->index,
|
||||||
|
@ -247,12 +247,57 @@ bool Device::pciEnable() {
|
||||||
return true;
|
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[]) {
|
int Device::pciMsiInit(int efds[]) {
|
||||||
// Check if this is really a vfio-pci device
|
// Check if this is really a vfio-pci device
|
||||||
if (not isVfioPciDevice())
|
if (not isVfioPciDevice())
|
||||||
return -1;
|
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 =
|
const size_t irqSetSize =
|
||||||
sizeof(struct vfio_irq_set) + sizeof(int) * irqCount;
|
sizeof(struct vfio_irq_set) + sizeof(int) * irqCount;
|
||||||
|
|
||||||
|
@ -299,7 +344,7 @@ int Device::pciMsiDeinit(int efds[]) {
|
||||||
if (not isVfioPciDevice())
|
if (not isVfioPciDevice())
|
||||||
return -1;
|
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 =
|
const size_t irqSetSize =
|
||||||
sizeof(struct vfio_irq_set) + sizeof(int) * irqCount;
|
sizeof(struct vfio_irq_set) + sizeof(int) * irqCount;
|
||||||
|
|
||||||
|
|
|
@ -53,8 +53,7 @@ private:
|
||||||
bool polling; // Polled or not
|
bool polling; // Polled or not
|
||||||
};
|
};
|
||||||
|
|
||||||
int num_irqs; // Number of available MSI vectors
|
std::vector<kernel::vfio::Device::IrqVectorInfo> irq_vectors;
|
||||||
int efds[maxIrqs];
|
|
||||||
int nos[maxIrqs];
|
int nos[maxIrqs];
|
||||||
bool polling[maxIrqs];
|
bool polling[maxIrqs];
|
||||||
};
|
};
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
|
#include <sys/ioctl.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
#include <villas/config.hpp>
|
#include <villas/config.hpp>
|
||||||
|
@ -22,28 +23,30 @@ using namespace villas::fpga::ip;
|
||||||
InterruptController::~InterruptController() {}
|
InterruptController::~InterruptController() {}
|
||||||
|
|
||||||
bool InterruptController::stop() {
|
bool InterruptController::stop() {
|
||||||
return this->vfioDevice->pciMsiDeinit(this->efds) > 0;
|
return this->vfioDevice->pciMsiDeinit(irq_vectors[0].eventFds) > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool InterruptController::init() {
|
bool InterruptController::init() {
|
||||||
const uintptr_t base = getBaseAddr(registerMemory);
|
|
||||||
|
|
||||||
PCIeCard *pciecard = dynamic_cast<PCIeCard *>(card);
|
PCIeCard *pciecard = dynamic_cast<PCIeCard *>(card);
|
||||||
this->vfioDevice = pciecard->vfioDevice;
|
this->vfioDevice = pciecard->vfioDevice;
|
||||||
|
|
||||||
num_irqs = this->vfioDevice->pciMsiInit(efds);
|
|
||||||
if (num_irqs < 0)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (not this->vfioDevice->pciMsiFind(nos)) {
|
if (not this->vfioDevice->pciMsiFind(nos)) {
|
||||||
return false;
|
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 each IRQ
|
||||||
for (int i = 0; i < num_irqs; i++) {
|
for (size_t i = 0; i < irq_vector.numFds; i++) {
|
||||||
|
|
||||||
// Try pinning to core
|
// Try pinning to core
|
||||||
PCIeCard *pciecard = dynamic_cast<PCIeCard *>(card);
|
|
||||||
int ret = kernel::setIRQAffinity(nos[i], pciecard->affinity, nullptr);
|
int ret = kernel::setIRQAffinity(nos[i], pciecard->affinity, nullptr);
|
||||||
|
|
||||||
switch (ret) {
|
switch (ret) {
|
||||||
|
@ -89,7 +92,7 @@ bool InterruptController::enableInterrupt(InterruptController::IrqMaskType mask,
|
||||||
// Clear pending IRQs
|
// Clear pending IRQs
|
||||||
XIntc_Out32(base + XIN_IAR_OFFSET, mask);
|
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))
|
if (mask & (1 << i))
|
||||||
this->polling[i] = polling;
|
this->polling[i] = polling;
|
||||||
}
|
}
|
||||||
|
@ -120,6 +123,7 @@ bool InterruptController::disableInterrupt(
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Assuming only one interrupt vector
|
||||||
ssize_t InterruptController::waitForInterrupt(int irq) {
|
ssize_t InterruptController::waitForInterrupt(int irq) {
|
||||||
assert(irq < maxIrqs);
|
assert(irq < maxIrqs);
|
||||||
|
|
||||||
|
@ -144,22 +148,37 @@ ssize_t InterruptController::waitForInterrupt(int irq) {
|
||||||
fd_set rfds;
|
fd_set rfds;
|
||||||
struct timeval tv = {.tv_sec = 1, .tv_usec = 0};
|
struct timeval tv = {.tv_sec = 1, .tv_usec = 0};
|
||||||
FD_ZERO(&rfds);
|
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) {
|
if (sret == -1) {
|
||||||
logger->error("select() failed: {}", strerror(errno));
|
logger->error("select() failed: {}", strerror(errno));
|
||||||
return -1;
|
return -1;
|
||||||
} else if (sret == 0) {
|
} else if (sret == 0) {
|
||||||
logger->warn("timeout waiting for interrupt {}", irq);
|
logger->warn("timeout waiting for interrupt {}", irq);
|
||||||
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
// Block until there has been an interrupt, read number of interrupts
|
// Block until there has been an interrupt, read number of interruptsvfio_device
|
||||||
ssize_t ret = read(efds[irq], &count, sizeof(count));
|
ssize_t ret = read(irq_vectors[0].eventFds[irq], &count, sizeof(count));
|
||||||
if (ret != 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;
|
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;
|
return count;
|
||||||
|
|
Loading…
Add table
Reference in a new issue