xref: /linux/drivers/vfio/cdx/intr.c (revision 39f1c201b93f4ff71631bac72cff6eb155f976a4)
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Copyright (C) 2022-2023, Advanced Micro Devices, Inc.
4  */
5 
6 #include <linux/vfio.h>
7 #include <linux/slab.h>
8 #include <linux/types.h>
9 #include <linux/eventfd.h>
10 #include <linux/msi.h>
11 #include <linux/interrupt.h>
12 
13 #include "linux/cdx/cdx_bus.h"
14 #include "private.h"
15 
16 static irqreturn_t vfio_cdx_msihandler(int irq_no, void *arg)
17 {
18 	struct eventfd_ctx *trigger = arg;
19 
20 	eventfd_signal(trigger);
21 	return IRQ_HANDLED;
22 }
23 
24 static int vfio_cdx_msi_enable(struct vfio_cdx_device *vdev, int nvec)
25 {
26 	struct cdx_device *cdx_dev = to_cdx_device(vdev->vdev.dev);
27 	struct device *dev = vdev->vdev.dev;
28 	int msi_idx, ret;
29 
30 	vdev->cdx_irqs = kzalloc_objs(struct vfio_cdx_irq, nvec);
31 	if (!vdev->cdx_irqs)
32 		return -ENOMEM;
33 
34 	ret = cdx_enable_msi(cdx_dev);
35 	if (ret)
36 		goto err_free;
37 
38 	/* Allocate cdx MSIs */
39 	ret = msi_domain_alloc_irqs(dev, MSI_DEFAULT_DOMAIN, nvec);
40 	if (ret)
41 		goto err_disable;
42 
43 	for (msi_idx = 0; msi_idx < nvec; msi_idx++)
44 		vdev->cdx_irqs[msi_idx].irq_no = msi_get_virq(dev, msi_idx);
45 
46 	vdev->msi_count = nvec;
47 
48 	return 0;
49 
50 err_disable:
51 	cdx_disable_msi(cdx_dev);
52 err_free:
53 	kfree(vdev->cdx_irqs);
54 	vdev->cdx_irqs = NULL;
55 	return ret;
56 }
57 
58 static int vfio_cdx_msi_set_vector_signal(struct vfio_cdx_device *vdev,
59 					  int vector, int fd)
60 {
61 	struct eventfd_ctx *trigger;
62 	int irq_no, ret;
63 
64 	if (vector < 0 || vector >= vdev->msi_count)
65 		return -EINVAL;
66 
67 	irq_no = vdev->cdx_irqs[vector].irq_no;
68 
69 	if (vdev->cdx_irqs[vector].trigger) {
70 		free_irq(irq_no, vdev->cdx_irqs[vector].trigger);
71 		kfree(vdev->cdx_irqs[vector].name);
72 		eventfd_ctx_put(vdev->cdx_irqs[vector].trigger);
73 		vdev->cdx_irqs[vector].trigger = NULL;
74 	}
75 
76 	if (fd < 0)
77 		return 0;
78 
79 	vdev->cdx_irqs[vector].name = kasprintf(GFP_KERNEL, "vfio-msi[%d](%s)",
80 						vector, dev_name(vdev->vdev.dev));
81 	if (!vdev->cdx_irqs[vector].name)
82 		return -ENOMEM;
83 
84 	trigger = eventfd_ctx_fdget(fd);
85 	if (IS_ERR(trigger)) {
86 		kfree(vdev->cdx_irqs[vector].name);
87 		return PTR_ERR(trigger);
88 	}
89 
90 	ret = request_irq(irq_no, vfio_cdx_msihandler, 0,
91 			  vdev->cdx_irqs[vector].name, trigger);
92 	if (ret) {
93 		kfree(vdev->cdx_irqs[vector].name);
94 		eventfd_ctx_put(trigger);
95 		return ret;
96 	}
97 
98 	vdev->cdx_irqs[vector].trigger = trigger;
99 
100 	return 0;
101 }
102 
103 static int vfio_cdx_msi_set_block(struct vfio_cdx_device *vdev,
104 				  unsigned int start, unsigned int count,
105 				  int32_t *fds)
106 {
107 	int i, j, ret = 0;
108 
109 	if (start >= vdev->msi_count || start + count > vdev->msi_count)
110 		return -EINVAL;
111 
112 	for (i = 0, j = start; i < count && !ret; i++, j++) {
113 		int fd = fds ? fds[i] : -1;
114 
115 		ret = vfio_cdx_msi_set_vector_signal(vdev, j, fd);
116 	}
117 
118 	if (ret) {
119 		for (--j; j >= (int)start; j--)
120 			vfio_cdx_msi_set_vector_signal(vdev, j, -1);
121 	}
122 
123 	return ret;
124 }
125 
126 static void vfio_cdx_msi_disable(struct vfio_cdx_device *vdev)
127 {
128 	struct cdx_device *cdx_dev = to_cdx_device(vdev->vdev.dev);
129 	struct device *dev = vdev->vdev.dev;
130 
131 	vfio_cdx_msi_set_block(vdev, 0, vdev->msi_count, NULL);
132 
133 	if (!vdev->cdx_irqs)
134 		return;
135 
136 	msi_domain_free_irqs_all(dev, MSI_DEFAULT_DOMAIN);
137 	cdx_disable_msi(cdx_dev);
138 	kfree(vdev->cdx_irqs);
139 
140 	vdev->cdx_irqs = NULL;
141 	vdev->msi_count = 0;
142 }
143 
144 static int vfio_cdx_set_msi_trigger(struct vfio_cdx_device *vdev,
145 				    unsigned int index, unsigned int start,
146 				    unsigned int count, u32 flags,
147 				    void *data)
148 {
149 	struct cdx_device *cdx_dev = to_cdx_device(vdev->vdev.dev);
150 	int i;
151 
152 	if (start + count > cdx_dev->num_msi)
153 		return -EINVAL;
154 
155 	guard(mutex)(&vdev->cdx_irqs_lock);
156 
157 	if (!count && (flags & VFIO_IRQ_SET_DATA_NONE)) {
158 		vfio_cdx_msi_disable(vdev);
159 		return 0;
160 	}
161 
162 	if (flags & VFIO_IRQ_SET_DATA_EVENTFD) {
163 		s32 *fds = data;
164 		int ret;
165 
166 		if (vdev->cdx_irqs)
167 			return vfio_cdx_msi_set_block(vdev, start, count,
168 						  fds);
169 		ret = vfio_cdx_msi_enable(vdev, cdx_dev->num_msi);
170 		if (ret)
171 			return ret;
172 
173 		ret = vfio_cdx_msi_set_block(vdev, start, count, fds);
174 		if (ret)
175 			vfio_cdx_msi_disable(vdev);
176 
177 		return ret;
178 	}
179 
180 	if (!vdev->cdx_irqs)
181 		return -EINVAL;
182 
183 	for (i = start; i < start + count; i++) {
184 		if (!vdev->cdx_irqs[i].trigger)
185 			continue;
186 		if (flags & VFIO_IRQ_SET_DATA_NONE) {
187 			eventfd_signal(vdev->cdx_irqs[i].trigger);
188 		} else if (flags & VFIO_IRQ_SET_DATA_BOOL) {
189 			u8 *bools = data;
190 
191 			if (bools[i - start])
192 				eventfd_signal(vdev->cdx_irqs[i].trigger);
193 		}
194 	}
195 
196 	return 0;
197 }
198 
199 int vfio_cdx_set_irqs_ioctl(struct vfio_cdx_device *vdev,
200 			    u32 flags, unsigned int index,
201 			    unsigned int start, unsigned int count,
202 			    void *data)
203 {
204 	if (flags & VFIO_IRQ_SET_ACTION_TRIGGER)
205 		return vfio_cdx_set_msi_trigger(vdev, index, start,
206 			  count, flags, data);
207 	else
208 		return -EINVAL;
209 }
210 
211 /* Free All IRQs for the given device */
212 void vfio_cdx_irqs_cleanup(struct vfio_cdx_device *vdev)
213 {
214 	vfio_cdx_set_msi_trigger(vdev, 0, 0, 0, VFIO_IRQ_SET_DATA_NONE, NULL);
215 }
216