mirror of
https://github.com/openwrt/openwrt.git
synced 2024-12-23 15:32:33 +00:00
269 lines
6.8 KiB
Diff
269 lines
6.8 KiB
Diff
|
From 8f0239c9385028a0c15306966c66a56315b11dbc Mon Sep 17 00:00:00 2001
|
||
|
From: Diana Craciun <diana.craciun@nxp.com>
|
||
|
Date: Tue, 1 Oct 2019 16:44:04 +0300
|
||
|
Subject: [PATCH] vfio/fsl-mc: trigger an interrupt via eventfd
|
||
|
|
||
|
This patch allows to set an eventfd for fsl-mc device interrupt
|
||
|
and also to trigger the interrupt eventfd from userspace for testing.
|
||
|
|
||
|
All fsl-mc device interrupts are MSI type. This does not yet handle
|
||
|
correctly DPRC container interrupt where re-scanning on container is
|
||
|
required.
|
||
|
|
||
|
Signed-off-by: Bharat Bhushan <Bharat.Bhushan@nxp.com>
|
||
|
Signed-off-by: Diana Craciun <diana.craciun@nxp.com>
|
||
|
---
|
||
|
drivers/vfio/fsl-mc/vfio_fsl_mc.c | 20 +++-
|
||
|
drivers/vfio/fsl-mc/vfio_fsl_mc_intr.c | 165 +++++++++++++++++++++++++++++-
|
||
|
drivers/vfio/fsl-mc/vfio_fsl_mc_private.h | 10 ++
|
||
|
3 files changed, 193 insertions(+), 2 deletions(-)
|
||
|
|
||
|
--- a/drivers/vfio/fsl-mc/vfio_fsl_mc.c
|
||
|
+++ b/drivers/vfio/fsl-mc/vfio_fsl_mc.c
|
||
|
@@ -144,12 +144,30 @@ err_reg_init:
|
||
|
static void vfio_fsl_mc_release(void *device_data)
|
||
|
{
|
||
|
struct vfio_fsl_mc_device *vdev = device_data;
|
||
|
+ int ret;
|
||
|
|
||
|
mutex_lock(&vdev->reflck->lock);
|
||
|
|
||
|
- if (!(--vdev->refcnt))
|
||
|
+ if (!(--vdev->refcnt)) {
|
||
|
+ struct fsl_mc_device *mc_dev = vdev->mc_dev;
|
||
|
+ struct device *cont_dev = fsl_mc_cont_dev(&mc_dev->dev);
|
||
|
+ struct fsl_mc_device *mc_cont = to_fsl_mc_device(cont_dev);
|
||
|
+ struct fsl_mc_bus *mc_bus;
|
||
|
+
|
||
|
+ mc_bus = to_fsl_mc_bus(mc_cont);
|
||
|
+
|
||
|
vfio_fsl_mc_regions_cleanup(vdev);
|
||
|
|
||
|
+ /* reset the device before cleaning up the interrupts */
|
||
|
+ ret = dprc_reset_container(mc_dev->mc_io, 0,
|
||
|
+ mc_dev->mc_handle,
|
||
|
+ mc_dev->obj_desc.id);
|
||
|
+
|
||
|
+ vfio_fsl_mc_irqs_cleanup(vdev);
|
||
|
+
|
||
|
+ fsl_mc_cleanup_irq_pool(mc_bus);
|
||
|
+ }
|
||
|
+
|
||
|
mutex_unlock(&vdev->reflck->lock);
|
||
|
|
||
|
module_put(THIS_MODULE);
|
||
|
--- a/drivers/vfio/fsl-mc/vfio_fsl_mc_intr.c
|
||
|
+++ b/drivers/vfio/fsl-mc/vfio_fsl_mc_intr.c
|
||
|
@@ -29,12 +29,154 @@ static int vfio_fsl_mc_irq_unmask(struct
|
||
|
return -EINVAL;
|
||
|
}
|
||
|
|
||
|
+int vfio_fsl_mc_irqs_allocate(struct vfio_fsl_mc_device *vdev)
|
||
|
+{
|
||
|
+ struct fsl_mc_device *mc_dev = vdev->mc_dev;
|
||
|
+ struct vfio_fsl_mc_irq *mc_irq;
|
||
|
+ int irq_count;
|
||
|
+ int ret, i;
|
||
|
+
|
||
|
+ /* Device does not support any interrupt */
|
||
|
+ if (mc_dev->obj_desc.irq_count == 0)
|
||
|
+ return 0;
|
||
|
+
|
||
|
+ /* interrupts were already allocated for this device */
|
||
|
+ if (vdev->mc_irqs)
|
||
|
+ return 0;
|
||
|
+
|
||
|
+ irq_count = mc_dev->obj_desc.irq_count;
|
||
|
+
|
||
|
+ mc_irq = kcalloc(irq_count, sizeof(*mc_irq), GFP_KERNEL);
|
||
|
+ if (mc_irq == NULL)
|
||
|
+ return -ENOMEM;
|
||
|
+
|
||
|
+ /* Allocate IRQs */
|
||
|
+ ret = fsl_mc_allocate_irqs(mc_dev);
|
||
|
+ if (ret) {
|
||
|
+ kfree(mc_irq);
|
||
|
+ return ret;
|
||
|
+ }
|
||
|
+
|
||
|
+ for (i = 0; i < irq_count; i++) {
|
||
|
+ mc_irq[i].count = 1;
|
||
|
+ mc_irq[i].flags = VFIO_IRQ_INFO_EVENTFD;
|
||
|
+ }
|
||
|
+
|
||
|
+ vdev->mc_irqs = mc_irq;
|
||
|
+
|
||
|
+ return 0;
|
||
|
+}
|
||
|
+static irqreturn_t vfio_fsl_mc_irq_handler(int irq_num, void *arg)
|
||
|
+{
|
||
|
+ struct vfio_fsl_mc_irq *mc_irq = (struct vfio_fsl_mc_irq *)arg;
|
||
|
+
|
||
|
+ eventfd_signal(mc_irq->trigger, 1);
|
||
|
+ return IRQ_HANDLED;
|
||
|
+}
|
||
|
+
|
||
|
+static int vfio_set_trigger(struct vfio_fsl_mc_device *vdev,
|
||
|
+ int index, int fd)
|
||
|
+{
|
||
|
+ struct vfio_fsl_mc_irq *irq = &vdev->mc_irqs[index];
|
||
|
+ struct eventfd_ctx *trigger;
|
||
|
+ int hwirq;
|
||
|
+ int ret;
|
||
|
+
|
||
|
+ hwirq = vdev->mc_dev->irqs[index]->msi_desc->irq;
|
||
|
+ if (irq->trigger) {
|
||
|
+ free_irq(hwirq, irq);
|
||
|
+ kfree(irq->name);
|
||
|
+ eventfd_ctx_put(irq->trigger);
|
||
|
+ irq->trigger = NULL;
|
||
|
+ }
|
||
|
+
|
||
|
+ if (fd < 0) /* Disable only */
|
||
|
+ return 0;
|
||
|
+
|
||
|
+ irq->name = kasprintf(GFP_KERNEL, "vfio-irq[%d](%s)",
|
||
|
+ hwirq, dev_name(&vdev->mc_dev->dev));
|
||
|
+ if (!irq->name)
|
||
|
+ return -ENOMEM;
|
||
|
+
|
||
|
+ trigger = eventfd_ctx_fdget(fd);
|
||
|
+ if (IS_ERR(trigger)) {
|
||
|
+ kfree(irq->name);
|
||
|
+ return PTR_ERR(trigger);
|
||
|
+ }
|
||
|
+
|
||
|
+ irq->trigger = trigger;
|
||
|
+
|
||
|
+ ret = request_irq(hwirq, vfio_fsl_mc_irq_handler, 0,
|
||
|
+ irq->name, irq);
|
||
|
+ if (ret) {
|
||
|
+ kfree(irq->name);
|
||
|
+ eventfd_ctx_put(trigger);
|
||
|
+ irq->trigger = NULL;
|
||
|
+ return ret;
|
||
|
+ }
|
||
|
+
|
||
|
+ return 0;
|
||
|
+}
|
||
|
+
|
||
|
static int vfio_fsl_mc_set_irq_trigger(struct vfio_fsl_mc_device *vdev,
|
||
|
unsigned int index, unsigned int start,
|
||
|
unsigned int count, uint32_t flags,
|
||
|
void *data)
|
||
|
{
|
||
|
- return -EINVAL;
|
||
|
+ struct fsl_mc_device *mc_dev = vdev->mc_dev;
|
||
|
+ struct fsl_mc_bus *mc_bus;
|
||
|
+ int ret, hwirq;
|
||
|
+ struct vfio_fsl_mc_irq *irq;
|
||
|
+ struct device *cont_dev = fsl_mc_cont_dev(&mc_dev->dev);
|
||
|
+ struct fsl_mc_device *mc_cont = to_fsl_mc_device(cont_dev);
|
||
|
+
|
||
|
+ if (start != 0 || count != 1)
|
||
|
+ return -EINVAL;
|
||
|
+
|
||
|
+ mc_bus = to_fsl_mc_bus(mc_cont);
|
||
|
+
|
||
|
+ mutex_lock(&vdev->reflck->lock);
|
||
|
+ if (!mc_bus->irq_resources) {
|
||
|
+
|
||
|
+ ret = fsl_mc_populate_irq_pool(mc_bus,
|
||
|
+ FSL_MC_IRQ_POOL_MAX_TOTAL_IRQS);
|
||
|
+ if (ret)
|
||
|
+ goto unlock;
|
||
|
+ }
|
||
|
+
|
||
|
+ ret = vfio_fsl_mc_irqs_allocate(vdev);
|
||
|
+ if (ret)
|
||
|
+ goto unlock;
|
||
|
+ mutex_unlock(&vdev->reflck->lock);
|
||
|
+
|
||
|
+ if (!count && (flags & VFIO_IRQ_SET_DATA_NONE))
|
||
|
+ return vfio_set_trigger(vdev, index, -1);
|
||
|
+
|
||
|
+ if (flags & VFIO_IRQ_SET_DATA_EVENTFD) {
|
||
|
+ int32_t fd = *(int32_t *)data;
|
||
|
+
|
||
|
+ return vfio_set_trigger(vdev, index, fd);
|
||
|
+ }
|
||
|
+
|
||
|
+ hwirq = vdev->mc_dev->irqs[index]->msi_desc->irq;
|
||
|
+
|
||
|
+ irq = &vdev->mc_irqs[index];
|
||
|
+
|
||
|
+ if (flags & VFIO_IRQ_SET_DATA_NONE) {
|
||
|
+ vfio_fsl_mc_irq_handler(hwirq, irq);
|
||
|
+
|
||
|
+ } else if (flags & VFIO_IRQ_SET_DATA_BOOL) {
|
||
|
+ uint8_t trigger = *(uint8_t *)data;
|
||
|
+
|
||
|
+ if (trigger)
|
||
|
+ vfio_fsl_mc_irq_handler(hwirq, irq);
|
||
|
+ }
|
||
|
+
|
||
|
+ return 0;
|
||
|
+
|
||
|
+unlock:
|
||
|
+ mutex_unlock(&vdev->reflck->lock);
|
||
|
+ return ret;
|
||
|
}
|
||
|
int vfio_fsl_mc_set_irqs_ioctl(struct vfio_fsl_mc_device *vdev,
|
||
|
uint32_t flags, unsigned int index,
|
||
|
@@ -60,3 +202,24 @@ int vfio_fsl_mc_set_irqs_ioctl(struct vf
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
+
|
||
|
+/* Free All IRQs for the given MC object */
|
||
|
+void vfio_fsl_mc_irqs_cleanup(struct vfio_fsl_mc_device *vdev)
|
||
|
+{
|
||
|
+ struct fsl_mc_device *mc_dev = vdev->mc_dev;
|
||
|
+ int irq_count = mc_dev->obj_desc.irq_count;
|
||
|
+ int i;
|
||
|
+
|
||
|
+ /* Device does not support any interrupt or the interrupts
|
||
|
+ * were not configured
|
||
|
+ */
|
||
|
+ if (mc_dev->obj_desc.irq_count == 0 || !vdev->mc_irqs)
|
||
|
+ return;
|
||
|
+
|
||
|
+ for (i = 0; i < irq_count; i++)
|
||
|
+ vfio_set_trigger(vdev, i, -1);
|
||
|
+
|
||
|
+ fsl_mc_free_irqs(mc_dev);
|
||
|
+ kfree(vdev->mc_irqs);
|
||
|
+ vdev->mc_irqs = NULL;
|
||
|
+}
|
||
|
--- a/drivers/vfio/fsl-mc/vfio_fsl_mc_private.h
|
||
|
+++ b/drivers/vfio/fsl-mc/vfio_fsl_mc_private.h
|
||
|
@@ -15,6 +15,13 @@
|
||
|
#define VFIO_FSL_MC_INDEX_TO_OFFSET(index) \
|
||
|
((u64)(index) << VFIO_FSL_MC_OFFSET_SHIFT)
|
||
|
|
||
|
+struct vfio_fsl_mc_irq {
|
||
|
+ u32 flags;
|
||
|
+ u32 count;
|
||
|
+ struct eventfd_ctx *trigger;
|
||
|
+ char *name;
|
||
|
+};
|
||
|
+
|
||
|
struct vfio_fsl_mc_reflck {
|
||
|
struct kref kref;
|
||
|
struct mutex lock;
|
||
|
@@ -33,6 +40,7 @@ struct vfio_fsl_mc_device {
|
||
|
u32 num_regions;
|
||
|
struct vfio_fsl_mc_region *regions;
|
||
|
struct vfio_fsl_mc_reflck *reflck;
|
||
|
+ struct vfio_fsl_mc_irq *mc_irqs;
|
||
|
};
|
||
|
|
||
|
int vfio_fsl_mc_set_irqs_ioctl(struct vfio_fsl_mc_device *vdev,
|
||
|
@@ -40,4 +48,6 @@ int vfio_fsl_mc_set_irqs_ioctl(struct vf
|
||
|
unsigned int start, unsigned int count,
|
||
|
void *data);
|
||
|
|
||
|
+void vfio_fsl_mc_irqs_cleanup(struct vfio_fsl_mc_device *vdev);
|
||
|
+
|
||
|
#endif /* VFIO_PCI_PRIVATE_H */
|