diff options
author | Antonios Motakis <a.motakis@virtualopensystems.com> | 2015-03-16 16:08:50 -0400 |
---|---|---|
committer | Alex Williamson <alex.williamson@redhat.com> | 2015-03-16 16:08:50 -0400 |
commit | 57f972e2b341dd6a73533f9293ec55d584a5d833 (patch) | |
tree | a9ff1aa6e1770af3f07e16f84de211015d68790b /drivers/vfio | |
parent | 9a36321c8d3350c4f7befa02adf3ce4583287ad9 (diff) |
vfio/platform: trigger an interrupt via eventfd
This patch allows to set an eventfd for a platform device's interrupt,
and also to trigger the interrupt eventfd from userspace for testing.
Level sensitive interrupts are marked as maskable and are handled in
a later patch. Edge triggered interrupts are not advertised as maskable
and are implemented here using a simple and efficient IRQ handler.
Signed-off-by: Antonios Motakis <a.motakis@virtualopensystems.com>
[Baptiste Reynal: fix masked interrupt initialization]
Signed-off-by: Baptiste Reynal <b.reynal@virtualopensystems.com>
Reviewed-by: Eric Auger <eric.auger@linaro.org>
Tested-by: Eric Auger <eric.auger@linaro.org>
Signed-off-by: Alex Williamson <alex.williamson@redhat.com>
Diffstat (limited to 'drivers/vfio')
-rw-r--r-- | drivers/vfio/platform/vfio_platform_irq.c | 94 | ||||
-rw-r--r-- | drivers/vfio/platform/vfio_platform_private.h | 2 |
2 files changed, 94 insertions, 2 deletions
diff --git a/drivers/vfio/platform/vfio_platform_irq.c b/drivers/vfio/platform/vfio_platform_irq.c index df5c91936a68..611ec80db63a 100644 --- a/drivers/vfio/platform/vfio_platform_irq.c +++ b/drivers/vfio/platform/vfio_platform_irq.c | |||
@@ -39,12 +39,92 @@ static int vfio_platform_set_irq_unmask(struct vfio_platform_device *vdev, | |||
39 | return -EINVAL; | 39 | return -EINVAL; |
40 | } | 40 | } |
41 | 41 | ||
42 | static irqreturn_t vfio_irq_handler(int irq, void *dev_id) | ||
43 | { | ||
44 | struct vfio_platform_irq *irq_ctx = dev_id; | ||
45 | |||
46 | eventfd_signal(irq_ctx->trigger, 1); | ||
47 | |||
48 | return IRQ_HANDLED; | ||
49 | } | ||
50 | |||
51 | static int vfio_set_trigger(struct vfio_platform_device *vdev, int index, | ||
52 | int fd, irq_handler_t handler) | ||
53 | { | ||
54 | struct vfio_platform_irq *irq = &vdev->irqs[index]; | ||
55 | struct eventfd_ctx *trigger; | ||
56 | int ret; | ||
57 | |||
58 | if (irq->trigger) { | ||
59 | free_irq(irq->hwirq, irq); | ||
60 | kfree(irq->name); | ||
61 | eventfd_ctx_put(irq->trigger); | ||
62 | irq->trigger = NULL; | ||
63 | } | ||
64 | |||
65 | if (fd < 0) /* Disable only */ | ||
66 | return 0; | ||
67 | |||
68 | irq->name = kasprintf(GFP_KERNEL, "vfio-irq[%d](%s)", | ||
69 | irq->hwirq, vdev->name); | ||
70 | if (!irq->name) | ||
71 | return -ENOMEM; | ||
72 | |||
73 | trigger = eventfd_ctx_fdget(fd); | ||
74 | if (IS_ERR(trigger)) { | ||
75 | kfree(irq->name); | ||
76 | return PTR_ERR(trigger); | ||
77 | } | ||
78 | |||
79 | irq->trigger = trigger; | ||
80 | |||
81 | ret = request_irq(irq->hwirq, handler, 0, irq->name, irq); | ||
82 | if (ret) { | ||
83 | kfree(irq->name); | ||
84 | eventfd_ctx_put(trigger); | ||
85 | irq->trigger = NULL; | ||
86 | return ret; | ||
87 | } | ||
88 | |||
89 | return 0; | ||
90 | } | ||
91 | |||
42 | static int vfio_platform_set_irq_trigger(struct vfio_platform_device *vdev, | 92 | static int vfio_platform_set_irq_trigger(struct vfio_platform_device *vdev, |
43 | unsigned index, unsigned start, | 93 | unsigned index, unsigned start, |
44 | unsigned count, uint32_t flags, | 94 | unsigned count, uint32_t flags, |
45 | void *data) | 95 | void *data) |
46 | { | 96 | { |
47 | return -EINVAL; | 97 | struct vfio_platform_irq *irq = &vdev->irqs[index]; |
98 | irq_handler_t handler; | ||
99 | |||
100 | if (vdev->irqs[index].flags & VFIO_IRQ_INFO_AUTOMASKED) | ||
101 | return -EINVAL; /* not implemented */ | ||
102 | else | ||
103 | handler = vfio_irq_handler; | ||
104 | |||
105 | if (!count && (flags & VFIO_IRQ_SET_DATA_NONE)) | ||
106 | return vfio_set_trigger(vdev, index, -1, handler); | ||
107 | |||
108 | if (start != 0 || count != 1) | ||
109 | return -EINVAL; | ||
110 | |||
111 | if (flags & VFIO_IRQ_SET_DATA_EVENTFD) { | ||
112 | int32_t fd = *(int32_t *)data; | ||
113 | |||
114 | return vfio_set_trigger(vdev, index, fd, handler); | ||
115 | } | ||
116 | |||
117 | if (flags & VFIO_IRQ_SET_DATA_NONE) { | ||
118 | handler(irq->hwirq, irq); | ||
119 | |||
120 | } else if (flags & VFIO_IRQ_SET_DATA_BOOL) { | ||
121 | uint8_t trigger = *(uint8_t *)data; | ||
122 | |||
123 | if (trigger) | ||
124 | handler(irq->hwirq, irq); | ||
125 | } | ||
126 | |||
127 | return 0; | ||
48 | } | 128 | } |
49 | 129 | ||
50 | int vfio_platform_set_irqs_ioctl(struct vfio_platform_device *vdev, | 130 | int vfio_platform_set_irqs_ioctl(struct vfio_platform_device *vdev, |
@@ -90,7 +170,12 @@ int vfio_platform_irq_init(struct vfio_platform_device *vdev) | |||
90 | if (hwirq < 0) | 170 | if (hwirq < 0) |
91 | goto err; | 171 | goto err; |
92 | 172 | ||
93 | vdev->irqs[i].flags = 0; | 173 | vdev->irqs[i].flags = VFIO_IRQ_INFO_EVENTFD; |
174 | |||
175 | if (irq_get_trigger_type(hwirq) & IRQ_TYPE_LEVEL_MASK) | ||
176 | vdev->irqs[i].flags |= VFIO_IRQ_INFO_MASKABLE | ||
177 | | VFIO_IRQ_INFO_AUTOMASKED; | ||
178 | |||
94 | vdev->irqs[i].count = 1; | 179 | vdev->irqs[i].count = 1; |
95 | vdev->irqs[i].hwirq = hwirq; | 180 | vdev->irqs[i].hwirq = hwirq; |
96 | } | 181 | } |
@@ -105,6 +190,11 @@ err: | |||
105 | 190 | ||
106 | void vfio_platform_irq_cleanup(struct vfio_platform_device *vdev) | 191 | void vfio_platform_irq_cleanup(struct vfio_platform_device *vdev) |
107 | { | 192 | { |
193 | int i; | ||
194 | |||
195 | for (i = 0; i < vdev->num_irqs; i++) | ||
196 | vfio_set_trigger(vdev, i, -1, NULL); | ||
197 | |||
108 | vdev->num_irqs = 0; | 198 | vdev->num_irqs = 0; |
109 | kfree(vdev->irqs); | 199 | kfree(vdev->irqs); |
110 | } | 200 | } |
diff --git a/drivers/vfio/platform/vfio_platform_private.h b/drivers/vfio/platform/vfio_platform_private.h index b119a6c5ac23..aa01cc36af53 100644 --- a/drivers/vfio/platform/vfio_platform_private.h +++ b/drivers/vfio/platform/vfio_platform_private.h | |||
@@ -31,6 +31,8 @@ struct vfio_platform_irq { | |||
31 | u32 flags; | 31 | u32 flags; |
32 | u32 count; | 32 | u32 count; |
33 | int hwirq; | 33 | int hwirq; |
34 | char *name; | ||
35 | struct eventfd_ctx *trigger; | ||
34 | }; | 36 | }; |
35 | 37 | ||
36 | struct vfio_platform_region { | 38 | struct vfio_platform_region { |