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:
parent
0682d114c1
commit
7f0ace6291
4 changed files with 96 additions and 25 deletions
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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];
|
||||
};
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Add table
Reference in a new issue