Linux Audio

Check our new training course

Loading...
v6.8
  1// SPDX-License-Identifier: GPL-2.0-only
  2/*
  3 * VFIO platform devices interrupt handling
  4 *
  5 * Copyright (C) 2013 - Virtual Open Systems
  6 * Author: Antonios Motakis <a.motakis@virtualopensystems.com>
  7 */
  8
  9#include <linux/eventfd.h>
 10#include <linux/interrupt.h>
 11#include <linux/slab.h>
 12#include <linux/types.h>
 13#include <linux/vfio.h>
 14#include <linux/irq.h>
 15
 16#include "vfio_platform_private.h"
 17
 18static void vfio_platform_mask(struct vfio_platform_irq *irq_ctx)
 19{
 20	unsigned long flags;
 21
 22	spin_lock_irqsave(&irq_ctx->lock, flags);
 23
 24	if (!irq_ctx->masked) {
 25		disable_irq_nosync(irq_ctx->hwirq);
 26		irq_ctx->masked = true;
 27	}
 28
 29	spin_unlock_irqrestore(&irq_ctx->lock, flags);
 30}
 31
 32static int vfio_platform_mask_handler(void *opaque, void *unused)
 33{
 34	struct vfio_platform_irq *irq_ctx = opaque;
 35
 36	vfio_platform_mask(irq_ctx);
 37
 38	return 0;
 39}
 40
 41static int vfio_platform_set_irq_mask(struct vfio_platform_device *vdev,
 42				      unsigned index, unsigned start,
 43				      unsigned count, uint32_t flags,
 44				      void *data)
 45{
 46	if (start != 0 || count != 1)
 47		return -EINVAL;
 48
 49	if (!(vdev->irqs[index].flags & VFIO_IRQ_INFO_MASKABLE))
 50		return -EINVAL;
 51
 52	if (flags & VFIO_IRQ_SET_DATA_EVENTFD) {
 53		int32_t fd = *(int32_t *)data;
 54
 55		if (fd >= 0)
 56			return vfio_virqfd_enable((void *) &vdev->irqs[index],
 57						  vfio_platform_mask_handler,
 58						  NULL, NULL,
 59						  &vdev->irqs[index].mask, fd);
 60
 61		vfio_virqfd_disable(&vdev->irqs[index].mask);
 62		return 0;
 63	}
 64
 65	if (flags & VFIO_IRQ_SET_DATA_NONE) {
 66		vfio_platform_mask(&vdev->irqs[index]);
 67
 68	} else if (flags & VFIO_IRQ_SET_DATA_BOOL) {
 69		uint8_t mask = *(uint8_t *)data;
 70
 71		if (mask)
 72			vfio_platform_mask(&vdev->irqs[index]);
 73	}
 74
 75	return 0;
 76}
 77
 78static void vfio_platform_unmask(struct vfio_platform_irq *irq_ctx)
 79{
 80	unsigned long flags;
 81
 82	spin_lock_irqsave(&irq_ctx->lock, flags);
 83
 84	if (irq_ctx->masked) {
 85		enable_irq(irq_ctx->hwirq);
 86		irq_ctx->masked = false;
 87	}
 88
 89	spin_unlock_irqrestore(&irq_ctx->lock, flags);
 90}
 91
 92static int vfio_platform_unmask_handler(void *opaque, void *unused)
 93{
 94	struct vfio_platform_irq *irq_ctx = opaque;
 95
 96	vfio_platform_unmask(irq_ctx);
 97
 98	return 0;
 99}
100
101static int vfio_platform_set_irq_unmask(struct vfio_platform_device *vdev,
102					unsigned index, unsigned start,
103					unsigned count, uint32_t flags,
104					void *data)
105{
106	if (start != 0 || count != 1)
107		return -EINVAL;
108
109	if (!(vdev->irqs[index].flags & VFIO_IRQ_INFO_MASKABLE))
110		return -EINVAL;
111
112	if (flags & VFIO_IRQ_SET_DATA_EVENTFD) {
113		int32_t fd = *(int32_t *)data;
114
115		if (fd >= 0)
116			return vfio_virqfd_enable((void *) &vdev->irqs[index],
117						  vfio_platform_unmask_handler,
118						  NULL, NULL,
119						  &vdev->irqs[index].unmask,
120						  fd);
121
122		vfio_virqfd_disable(&vdev->irqs[index].unmask);
123		return 0;
124	}
125
126	if (flags & VFIO_IRQ_SET_DATA_NONE) {
127		vfio_platform_unmask(&vdev->irqs[index]);
128
129	} else if (flags & VFIO_IRQ_SET_DATA_BOOL) {
130		uint8_t unmask = *(uint8_t *)data;
131
132		if (unmask)
133			vfio_platform_unmask(&vdev->irqs[index]);
134	}
135
136	return 0;
137}
138
139static irqreturn_t vfio_automasked_irq_handler(int irq, void *dev_id)
140{
141	struct vfio_platform_irq *irq_ctx = dev_id;
142	unsigned long flags;
143	int ret = IRQ_NONE;
144
145	spin_lock_irqsave(&irq_ctx->lock, flags);
146
147	if (!irq_ctx->masked) {
148		ret = IRQ_HANDLED;
149
150		/* automask maskable interrupts */
151		disable_irq_nosync(irq_ctx->hwirq);
152		irq_ctx->masked = true;
153	}
154
155	spin_unlock_irqrestore(&irq_ctx->lock, flags);
156
157	if (ret == IRQ_HANDLED)
158		eventfd_signal(irq_ctx->trigger);
159
160	return ret;
161}
162
163static irqreturn_t vfio_irq_handler(int irq, void *dev_id)
164{
165	struct vfio_platform_irq *irq_ctx = dev_id;
166
167	eventfd_signal(irq_ctx->trigger);
168
169	return IRQ_HANDLED;
170}
171
172static int vfio_set_trigger(struct vfio_platform_device *vdev, int index,
173			    int fd, irq_handler_t handler)
174{
175	struct vfio_platform_irq *irq = &vdev->irqs[index];
176	struct eventfd_ctx *trigger;
177	int ret;
178
179	if (irq->trigger) {
180		irq_clear_status_flags(irq->hwirq, IRQ_NOAUTOEN);
181		free_irq(irq->hwirq, irq);
182		kfree(irq->name);
183		eventfd_ctx_put(irq->trigger);
184		irq->trigger = NULL;
185	}
186
187	if (fd < 0) /* Disable only */
188		return 0;
189	irq->name = kasprintf(GFP_KERNEL_ACCOUNT, "vfio-irq[%d](%s)",
190			      irq->hwirq, vdev->name);
 
191	if (!irq->name)
192		return -ENOMEM;
193
194	trigger = eventfd_ctx_fdget(fd);
195	if (IS_ERR(trigger)) {
196		kfree(irq->name);
197		return PTR_ERR(trigger);
198	}
199
200	irq->trigger = trigger;
201
202	irq_set_status_flags(irq->hwirq, IRQ_NOAUTOEN);
203	ret = request_irq(irq->hwirq, handler, 0, irq->name, irq);
204	if (ret) {
205		kfree(irq->name);
206		eventfd_ctx_put(trigger);
207		irq->trigger = NULL;
208		return ret;
209	}
210
211	if (!irq->masked)
212		enable_irq(irq->hwirq);
213
214	return 0;
215}
216
217static int vfio_platform_set_irq_trigger(struct vfio_platform_device *vdev,
218					 unsigned index, unsigned start,
219					 unsigned count, uint32_t flags,
220					 void *data)
221{
222	struct vfio_platform_irq *irq = &vdev->irqs[index];
223	irq_handler_t handler;
224
225	if (vdev->irqs[index].flags & VFIO_IRQ_INFO_AUTOMASKED)
226		handler = vfio_automasked_irq_handler;
227	else
228		handler = vfio_irq_handler;
229
230	if (!count && (flags & VFIO_IRQ_SET_DATA_NONE))
231		return vfio_set_trigger(vdev, index, -1, handler);
232
233	if (start != 0 || count != 1)
234		return -EINVAL;
235
236	if (flags & VFIO_IRQ_SET_DATA_EVENTFD) {
237		int32_t fd = *(int32_t *)data;
238
239		return vfio_set_trigger(vdev, index, fd, handler);
240	}
241
242	if (flags & VFIO_IRQ_SET_DATA_NONE) {
243		handler(irq->hwirq, irq);
244
245	} else if (flags & VFIO_IRQ_SET_DATA_BOOL) {
246		uint8_t trigger = *(uint8_t *)data;
247
248		if (trigger)
249			handler(irq->hwirq, irq);
250	}
251
252	return 0;
253}
254
255int vfio_platform_set_irqs_ioctl(struct vfio_platform_device *vdev,
256				 uint32_t flags, unsigned index, unsigned start,
257				 unsigned count, void *data)
258{
259	int (*func)(struct vfio_platform_device *vdev, unsigned index,
260		    unsigned start, unsigned count, uint32_t flags,
261		    void *data) = NULL;
262
263	switch (flags & VFIO_IRQ_SET_ACTION_TYPE_MASK) {
264	case VFIO_IRQ_SET_ACTION_MASK:
265		func = vfio_platform_set_irq_mask;
266		break;
267	case VFIO_IRQ_SET_ACTION_UNMASK:
268		func = vfio_platform_set_irq_unmask;
269		break;
270	case VFIO_IRQ_SET_ACTION_TRIGGER:
271		func = vfio_platform_set_irq_trigger;
272		break;
273	}
274
275	if (!func)
276		return -ENOTTY;
277
278	return func(vdev, index, start, count, flags, data);
279}
280
281int vfio_platform_irq_init(struct vfio_platform_device *vdev)
282{
283	int cnt = 0, i;
284
285	while (vdev->get_irq(vdev, cnt) >= 0)
286		cnt++;
287
288	vdev->irqs = kcalloc(cnt, sizeof(struct vfio_platform_irq),
289			     GFP_KERNEL_ACCOUNT);
290	if (!vdev->irqs)
291		return -ENOMEM;
292
293	for (i = 0; i < cnt; i++) {
294		int hwirq = vdev->get_irq(vdev, i);
295
296		if (hwirq < 0)
297			goto err;
298
299		spin_lock_init(&vdev->irqs[i].lock);
300
301		vdev->irqs[i].flags = VFIO_IRQ_INFO_EVENTFD;
302
303		if (irq_get_trigger_type(hwirq) & IRQ_TYPE_LEVEL_MASK)
304			vdev->irqs[i].flags |= VFIO_IRQ_INFO_MASKABLE
305						| VFIO_IRQ_INFO_AUTOMASKED;
306
307		vdev->irqs[i].count = 1;
308		vdev->irqs[i].hwirq = hwirq;
309		vdev->irqs[i].masked = false;
310	}
311
312	vdev->num_irqs = cnt;
313
314	return 0;
315err:
316	kfree(vdev->irqs);
317	return -EINVAL;
318}
319
320void vfio_platform_irq_cleanup(struct vfio_platform_device *vdev)
321{
322	int i;
323
324	for (i = 0; i < vdev->num_irqs; i++)
325		vfio_set_trigger(vdev, i, -1, NULL);
326
327	vdev->num_irqs = 0;
328	kfree(vdev->irqs);
329}
v6.2
  1// SPDX-License-Identifier: GPL-2.0-only
  2/*
  3 * VFIO platform devices interrupt handling
  4 *
  5 * Copyright (C) 2013 - Virtual Open Systems
  6 * Author: Antonios Motakis <a.motakis@virtualopensystems.com>
  7 */
  8
  9#include <linux/eventfd.h>
 10#include <linux/interrupt.h>
 11#include <linux/slab.h>
 12#include <linux/types.h>
 13#include <linux/vfio.h>
 14#include <linux/irq.h>
 15
 16#include "vfio_platform_private.h"
 17
 18static void vfio_platform_mask(struct vfio_platform_irq *irq_ctx)
 19{
 20	unsigned long flags;
 21
 22	spin_lock_irqsave(&irq_ctx->lock, flags);
 23
 24	if (!irq_ctx->masked) {
 25		disable_irq_nosync(irq_ctx->hwirq);
 26		irq_ctx->masked = true;
 27	}
 28
 29	spin_unlock_irqrestore(&irq_ctx->lock, flags);
 30}
 31
 32static int vfio_platform_mask_handler(void *opaque, void *unused)
 33{
 34	struct vfio_platform_irq *irq_ctx = opaque;
 35
 36	vfio_platform_mask(irq_ctx);
 37
 38	return 0;
 39}
 40
 41static int vfio_platform_set_irq_mask(struct vfio_platform_device *vdev,
 42				      unsigned index, unsigned start,
 43				      unsigned count, uint32_t flags,
 44				      void *data)
 45{
 46	if (start != 0 || count != 1)
 47		return -EINVAL;
 48
 49	if (!(vdev->irqs[index].flags & VFIO_IRQ_INFO_MASKABLE))
 50		return -EINVAL;
 51
 52	if (flags & VFIO_IRQ_SET_DATA_EVENTFD) {
 53		int32_t fd = *(int32_t *)data;
 54
 55		if (fd >= 0)
 56			return vfio_virqfd_enable((void *) &vdev->irqs[index],
 57						  vfio_platform_mask_handler,
 58						  NULL, NULL,
 59						  &vdev->irqs[index].mask, fd);
 60
 61		vfio_virqfd_disable(&vdev->irqs[index].mask);
 62		return 0;
 63	}
 64
 65	if (flags & VFIO_IRQ_SET_DATA_NONE) {
 66		vfio_platform_mask(&vdev->irqs[index]);
 67
 68	} else if (flags & VFIO_IRQ_SET_DATA_BOOL) {
 69		uint8_t mask = *(uint8_t *)data;
 70
 71		if (mask)
 72			vfio_platform_mask(&vdev->irqs[index]);
 73	}
 74
 75	return 0;
 76}
 77
 78static void vfio_platform_unmask(struct vfio_platform_irq *irq_ctx)
 79{
 80	unsigned long flags;
 81
 82	spin_lock_irqsave(&irq_ctx->lock, flags);
 83
 84	if (irq_ctx->masked) {
 85		enable_irq(irq_ctx->hwirq);
 86		irq_ctx->masked = false;
 87	}
 88
 89	spin_unlock_irqrestore(&irq_ctx->lock, flags);
 90}
 91
 92static int vfio_platform_unmask_handler(void *opaque, void *unused)
 93{
 94	struct vfio_platform_irq *irq_ctx = opaque;
 95
 96	vfio_platform_unmask(irq_ctx);
 97
 98	return 0;
 99}
100
101static int vfio_platform_set_irq_unmask(struct vfio_platform_device *vdev,
102					unsigned index, unsigned start,
103					unsigned count, uint32_t flags,
104					void *data)
105{
106	if (start != 0 || count != 1)
107		return -EINVAL;
108
109	if (!(vdev->irqs[index].flags & VFIO_IRQ_INFO_MASKABLE))
110		return -EINVAL;
111
112	if (flags & VFIO_IRQ_SET_DATA_EVENTFD) {
113		int32_t fd = *(int32_t *)data;
114
115		if (fd >= 0)
116			return vfio_virqfd_enable((void *) &vdev->irqs[index],
117						  vfio_platform_unmask_handler,
118						  NULL, NULL,
119						  &vdev->irqs[index].unmask,
120						  fd);
121
122		vfio_virqfd_disable(&vdev->irqs[index].unmask);
123		return 0;
124	}
125
126	if (flags & VFIO_IRQ_SET_DATA_NONE) {
127		vfio_platform_unmask(&vdev->irqs[index]);
128
129	} else if (flags & VFIO_IRQ_SET_DATA_BOOL) {
130		uint8_t unmask = *(uint8_t *)data;
131
132		if (unmask)
133			vfio_platform_unmask(&vdev->irqs[index]);
134	}
135
136	return 0;
137}
138
139static irqreturn_t vfio_automasked_irq_handler(int irq, void *dev_id)
140{
141	struct vfio_platform_irq *irq_ctx = dev_id;
142	unsigned long flags;
143	int ret = IRQ_NONE;
144
145	spin_lock_irqsave(&irq_ctx->lock, flags);
146
147	if (!irq_ctx->masked) {
148		ret = IRQ_HANDLED;
149
150		/* automask maskable interrupts */
151		disable_irq_nosync(irq_ctx->hwirq);
152		irq_ctx->masked = true;
153	}
154
155	spin_unlock_irqrestore(&irq_ctx->lock, flags);
156
157	if (ret == IRQ_HANDLED)
158		eventfd_signal(irq_ctx->trigger, 1);
159
160	return ret;
161}
162
163static irqreturn_t vfio_irq_handler(int irq, void *dev_id)
164{
165	struct vfio_platform_irq *irq_ctx = dev_id;
166
167	eventfd_signal(irq_ctx->trigger, 1);
168
169	return IRQ_HANDLED;
170}
171
172static int vfio_set_trigger(struct vfio_platform_device *vdev, int index,
173			    int fd, irq_handler_t handler)
174{
175	struct vfio_platform_irq *irq = &vdev->irqs[index];
176	struct eventfd_ctx *trigger;
177	int ret;
178
179	if (irq->trigger) {
180		irq_clear_status_flags(irq->hwirq, IRQ_NOAUTOEN);
181		free_irq(irq->hwirq, irq);
182		kfree(irq->name);
183		eventfd_ctx_put(irq->trigger);
184		irq->trigger = NULL;
185	}
186
187	if (fd < 0) /* Disable only */
188		return 0;
189
190	irq->name = kasprintf(GFP_KERNEL, "vfio-irq[%d](%s)",
191						irq->hwirq, vdev->name);
192	if (!irq->name)
193		return -ENOMEM;
194
195	trigger = eventfd_ctx_fdget(fd);
196	if (IS_ERR(trigger)) {
197		kfree(irq->name);
198		return PTR_ERR(trigger);
199	}
200
201	irq->trigger = trigger;
202
203	irq_set_status_flags(irq->hwirq, IRQ_NOAUTOEN);
204	ret = request_irq(irq->hwirq, handler, 0, irq->name, irq);
205	if (ret) {
206		kfree(irq->name);
207		eventfd_ctx_put(trigger);
208		irq->trigger = NULL;
209		return ret;
210	}
211
212	if (!irq->masked)
213		enable_irq(irq->hwirq);
214
215	return 0;
216}
217
218static int vfio_platform_set_irq_trigger(struct vfio_platform_device *vdev,
219					 unsigned index, unsigned start,
220					 unsigned count, uint32_t flags,
221					 void *data)
222{
223	struct vfio_platform_irq *irq = &vdev->irqs[index];
224	irq_handler_t handler;
225
226	if (vdev->irqs[index].flags & VFIO_IRQ_INFO_AUTOMASKED)
227		handler = vfio_automasked_irq_handler;
228	else
229		handler = vfio_irq_handler;
230
231	if (!count && (flags & VFIO_IRQ_SET_DATA_NONE))
232		return vfio_set_trigger(vdev, index, -1, handler);
233
234	if (start != 0 || count != 1)
235		return -EINVAL;
236
237	if (flags & VFIO_IRQ_SET_DATA_EVENTFD) {
238		int32_t fd = *(int32_t *)data;
239
240		return vfio_set_trigger(vdev, index, fd, handler);
241	}
242
243	if (flags & VFIO_IRQ_SET_DATA_NONE) {
244		handler(irq->hwirq, irq);
245
246	} else if (flags & VFIO_IRQ_SET_DATA_BOOL) {
247		uint8_t trigger = *(uint8_t *)data;
248
249		if (trigger)
250			handler(irq->hwirq, irq);
251	}
252
253	return 0;
254}
255
256int vfio_platform_set_irqs_ioctl(struct vfio_platform_device *vdev,
257				 uint32_t flags, unsigned index, unsigned start,
258				 unsigned count, void *data)
259{
260	int (*func)(struct vfio_platform_device *vdev, unsigned index,
261		    unsigned start, unsigned count, uint32_t flags,
262		    void *data) = NULL;
263
264	switch (flags & VFIO_IRQ_SET_ACTION_TYPE_MASK) {
265	case VFIO_IRQ_SET_ACTION_MASK:
266		func = vfio_platform_set_irq_mask;
267		break;
268	case VFIO_IRQ_SET_ACTION_UNMASK:
269		func = vfio_platform_set_irq_unmask;
270		break;
271	case VFIO_IRQ_SET_ACTION_TRIGGER:
272		func = vfio_platform_set_irq_trigger;
273		break;
274	}
275
276	if (!func)
277		return -ENOTTY;
278
279	return func(vdev, index, start, count, flags, data);
280}
281
282int vfio_platform_irq_init(struct vfio_platform_device *vdev)
283{
284	int cnt = 0, i;
285
286	while (vdev->get_irq(vdev, cnt) >= 0)
287		cnt++;
288
289	vdev->irqs = kcalloc(cnt, sizeof(struct vfio_platform_irq), GFP_KERNEL);
 
290	if (!vdev->irqs)
291		return -ENOMEM;
292
293	for (i = 0; i < cnt; i++) {
294		int hwirq = vdev->get_irq(vdev, i);
295
296		if (hwirq < 0)
297			goto err;
298
299		spin_lock_init(&vdev->irqs[i].lock);
300
301		vdev->irqs[i].flags = VFIO_IRQ_INFO_EVENTFD;
302
303		if (irq_get_trigger_type(hwirq) & IRQ_TYPE_LEVEL_MASK)
304			vdev->irqs[i].flags |= VFIO_IRQ_INFO_MASKABLE
305						| VFIO_IRQ_INFO_AUTOMASKED;
306
307		vdev->irqs[i].count = 1;
308		vdev->irqs[i].hwirq = hwirq;
309		vdev->irqs[i].masked = false;
310	}
311
312	vdev->num_irqs = cnt;
313
314	return 0;
315err:
316	kfree(vdev->irqs);
317	return -EINVAL;
318}
319
320void vfio_platform_irq_cleanup(struct vfio_platform_device *vdev)
321{
322	int i;
323
324	for (i = 0; i < vdev->num_irqs; i++)
325		vfio_set_trigger(vdev, i, -1, NULL);
326
327	vdev->num_irqs = 0;
328	kfree(vdev->irqs);
329}