Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Vfio automask #868

Merged
merged 3 commits into from
Mar 13, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion common/include/villas/kernel/vfio_device.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand All @@ -49,6 +55,8 @@ class Device {
// 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();

Expand Down Expand Up @@ -86,7 +94,7 @@ class Device {

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;

Expand Down
57 changes: 51 additions & 6 deletions common/lib/kernel/vfio_device.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Expand All @@ -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);

Expand Down Expand Up @@ -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;
}
}

Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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;

Expand Down Expand Up @@ -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;

Expand Down
3 changes: 1 addition & 2 deletions fpga/include/villas/fpga/ips/intc.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,7 @@ class InterruptController : public Core {
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];
};
Expand Down
47 changes: 33 additions & 14 deletions fpga/lib/ips/intc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
*/

#include <errno.h>
#include <sys/ioctl.h>
#include <unistd.h>

#include <villas/config.hpp>
Expand All @@ -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;
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 (not this->vfioDevice->pciMsiFind(nos)) {
return false;
}

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) {
Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -120,6 +123,7 @@ bool InterruptController::disableInterrupt(
return true;
}

// Assuming only one interrupt vector
ssize_t InterruptController::waitForInterrupt(int irq) {
assert(irq < maxIrqs);

Expand All @@ -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));
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;
Expand Down