1
0
Fork 0
mirror of https://git.rwth-aachen.de/acs/public/villas/node/ synced 2025-03-30 00:00:11 +01:00
VILLASnode/fpga/lib/ips/intc.cpp
Daniel Krebs 6cb3b77c7a ips/intc: don't fail if setting IRQ affinity is not possible
This is the case when the application is not executed as root which is
now possible, with the drawback that we cannot set the IRQ affinity
anymore.
2018-02-14 14:32:07 +01:00

179 lines
4.7 KiB
C++

/** AXI-PCIe Interrupt controller
*
* @author Steffen Vogel <stvogel@eonerc.rwth-aachen.de>
* @copyright 2017, Steffen Vogel
* @license GNU General Public License (version 3)
*
* VILLASfpga
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*********************************************************************************/
#include <unistd.h>
#include <errno.h>
#include "config.h"
#include "log.h"
#include "plugin.hpp"
#include "kernel/vfio.h"
#include "kernel/kernel.h"
#include "fpga/card.hpp"
#include "fpga/ips/intc.hpp"
namespace villas {
namespace fpga {
namespace ip {
// instantiate factory to make available to plugin infrastructure
static InterruptControllerFactory factory;
InterruptController::~InterruptController()
{
vfio_pci_msi_deinit(&card->vfio_device , this->efds);
}
bool
InterruptController::init()
{
const uintptr_t base = getBaseAddr(registerMemory);
auto logger = getLogger();
num_irqs = vfio_pci_msi_init(&card->vfio_device, efds);
if (num_irqs < 0)
return false;
if(vfio_pci_msi_find(&card->vfio_device, nos) != 0)
return false;
/* For each IRQ */
for (int i = 0; i < num_irqs; i++) {
/* Try pinning to core */
int ret = kernel_irq_setaffinity(nos[i], card->affinity, nullptr);
switch(ret) {
case 0:
// everything is fine
break;
case EACCES:
logger->warn("No permission to change affinity of VFIO-MSI interrupt. "
"This may degrade performance (increasing jitter)");
break;
default:
logger->error("Failed to change affinity of VFIO-MSI interrupt");
return false;
}
/* Setup vector */
XIntc_Out32(base + XIN_IVAR_OFFSET + i * 4, i);
}
XIntc_Out32(base + XIN_IMR_OFFSET, 0); /* Use manual acknowlegement for all IRQs */
XIntc_Out32(base + XIN_IAR_OFFSET, 0xFFFFFFFF); /* Acknowlege all pending IRQs manually */
XIntc_Out32(base + XIN_IMR_OFFSET, 0xFFFFFFFF); /* Use fast acknowlegement for all IRQs */
XIntc_Out32(base + XIN_IER_OFFSET, 0x00000000); /* Disable all IRQs by default */
XIntc_Out32(base + XIN_MER_OFFSET, XIN_INT_HARDWARE_ENABLE_MASK | XIN_INT_MASTER_ENABLE_MASK);
logger->debug("enabled interrupts");
return true;
}
bool
InterruptController::enableInterrupt(InterruptController::IrqMaskType mask, bool polling)
{
auto logger = getLogger();
const uintptr_t base = getBaseAddr(registerMemory);
/* Current state of INTC */
const uint32_t ier = XIntc_In32(base + XIN_IER_OFFSET);
const uint32_t imr = XIntc_In32(base + XIN_IMR_OFFSET);
/* Clear pending IRQs */
XIntc_Out32(base + XIN_IAR_OFFSET, mask);
for (int i = 0; i < num_irqs; i++) {
if (mask & (1 << i))
this->polling[i] = polling;
}
if (polling) {
XIntc_Out32(base + XIN_IMR_OFFSET, imr & ~mask);
XIntc_Out32(base + XIN_IER_OFFSET, ier & ~mask);
}
else {
XIntc_Out32(base + XIN_IER_OFFSET, ier | mask);
XIntc_Out32(base + XIN_IMR_OFFSET, imr | mask);
}
logger->debug("New ier = {:x}", XIntc_In32(base + XIN_IER_OFFSET));
logger->debug("New imr = {:x}", XIntc_In32(base + XIN_IMR_OFFSET));
logger->debug("New isr = {:x}", XIntc_In32(base + XIN_ISR_OFFSET));
logger->debug("Interupts enabled: mask={:x} polling={:d}", mask, polling);
return true;
}
bool
InterruptController::disableInterrupt(InterruptController::IrqMaskType mask)
{
const uintptr_t base = getBaseAddr(registerMemory);
uint32_t ier = XIntc_In32(base + XIN_IER_OFFSET);
XIntc_Out32(base + XIN_IER_OFFSET, ier & ~mask);
return true;
}
int
InterruptController::waitForInterrupt(int irq)
{
assert(irq < maxIrqs);
const uintptr_t base = getBaseAddr(registerMemory);
if (this->polling[irq]) {
uint32_t isr, mask = 1 << irq;
do {
// poll status register
isr = XIntc_In32(base + XIN_ISR_OFFSET);
pthread_testcancel();
} while ((isr & mask) != mask);
// acknowledge interrupt
XIntc_Out32(base + XIN_IAR_OFFSET, mask);
// we can only tell that there has been (at least) one interrupt
return 1;
}
else {
uint64_t count;
// block until there has been an interrupt, read number of interrupts
ssize_t ret = read(efds[irq], &count, sizeof(count));
if (ret != sizeof(count))
return -1;
return static_cast<int>(count);
}
}
} // namespace ip
} // namespace fpga
} // namespace villas