Skip to content

Commit 675daf4

Browse files
committed
vfio/platform: Create persistent IRQ handlers
The vfio-platform SET_IRQS ioctl currently allows loopback triggering of an interrupt before a signaling eventfd has been configured by the user, which thereby allows a NULL pointer dereference. Rather than register the IRQ relative to a valid trigger, register all IRQs in a disabled state in the device open path. This allows mask operations on the IRQ to nest within the overall enable state governed by a valid eventfd signal. This decouples @Masked, protected by the @locked spinlock from @trigger, protected via the @igate mutex. In doing so, it's guaranteed that changes to @trigger cannot race the IRQ handlers because the IRQ handler is synchronously disabled before modifying the trigger, and loopback triggering of the IRQ via ioctl is safe due to serialization with trigger changes via igate. For compatibility, request_irq() failures are maintained to be local to the SET_IRQS ioctl rather than a fatal error in the open device path. This allows, for example, a userspace driver with polling mode support to continue to work regardless of moving the request_irq() call site. This necessarily blocks all SET_IRQS access to the failed index. Cc: Eric Auger <eric.auger@redhat.com> Cc: <stable@vger.kernel.org> Fixes: 57f972e ("vfio/platform: trigger an interrupt via eventfd") Reviewed-by: Kevin Tian <kevin.tian@intel.com> Reviewed-by: Eric Auger <eric.auger@redhat.com> Link: https://lore.kernel.org/r/20240308230557.805580-7-alex.williamson@redhat.com Signed-off-by: Alex Williamson <alex.williamson@redhat.com>
1 parent fcdc0d3 commit 675daf4

File tree

1 file changed

+68
-32
lines changed

1 file changed

+68
-32
lines changed

drivers/vfio/platform/vfio_platform_irq.c

Lines changed: 68 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,16 @@ static int vfio_platform_set_irq_unmask(struct vfio_platform_device *vdev,
136136
return 0;
137137
}
138138

139+
/*
140+
* The trigger eventfd is guaranteed valid in the interrupt path
141+
* and protected by the igate mutex when triggered via ioctl.
142+
*/
143+
static void vfio_send_eventfd(struct vfio_platform_irq *irq_ctx)
144+
{
145+
if (likely(irq_ctx->trigger))
146+
eventfd_signal(irq_ctx->trigger);
147+
}
148+
139149
static irqreturn_t vfio_automasked_irq_handler(int irq, void *dev_id)
140150
{
141151
struct vfio_platform_irq *irq_ctx = dev_id;
@@ -155,7 +165,7 @@ static irqreturn_t vfio_automasked_irq_handler(int irq, void *dev_id)
155165
spin_unlock_irqrestore(&irq_ctx->lock, flags);
156166

157167
if (ret == IRQ_HANDLED)
158-
eventfd_signal(irq_ctx->trigger);
168+
vfio_send_eventfd(irq_ctx);
159169

160170
return ret;
161171
}
@@ -164,52 +174,40 @@ static irqreturn_t vfio_irq_handler(int irq, void *dev_id)
164174
{
165175
struct vfio_platform_irq *irq_ctx = dev_id;
166176

167-
eventfd_signal(irq_ctx->trigger);
177+
vfio_send_eventfd(irq_ctx);
168178

169179
return IRQ_HANDLED;
170180
}
171181

172182
static int vfio_set_trigger(struct vfio_platform_device *vdev, int index,
173-
int fd, irq_handler_t handler)
183+
int fd)
174184
{
175185
struct vfio_platform_irq *irq = &vdev->irqs[index];
176186
struct eventfd_ctx *trigger;
177-
int ret;
178187

179188
if (irq->trigger) {
180-
irq_clear_status_flags(irq->hwirq, IRQ_NOAUTOEN);
181-
free_irq(irq->hwirq, irq);
182-
kfree(irq->name);
189+
disable_irq(irq->hwirq);
183190
eventfd_ctx_put(irq->trigger);
184191
irq->trigger = NULL;
185192
}
186193

187194
if (fd < 0) /* Disable only */
188195
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;
193196

194197
trigger = eventfd_ctx_fdget(fd);
195-
if (IS_ERR(trigger)) {
196-
kfree(irq->name);
198+
if (IS_ERR(trigger))
197199
return PTR_ERR(trigger);
198-
}
199200

200201
irq->trigger = trigger;
201202

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);
203+
/*
204+
* irq->masked effectively provides nested disables within the overall
205+
* enable relative to trigger. Specifically request_irq() is called
206+
* with NO_AUTOEN, therefore the IRQ is initially disabled. The user
207+
* may only further disable the IRQ with a MASK operations because
208+
* irq->masked is initially false.
209+
*/
210+
enable_irq(irq->hwirq);
213211

214212
return 0;
215213
}
@@ -228,15 +226,15 @@ static int vfio_platform_set_irq_trigger(struct vfio_platform_device *vdev,
228226
handler = vfio_irq_handler;
229227

230228
if (!count && (flags & VFIO_IRQ_SET_DATA_NONE))
231-
return vfio_set_trigger(vdev, index, -1, handler);
229+
return vfio_set_trigger(vdev, index, -1);
232230

233231
if (start != 0 || count != 1)
234232
return -EINVAL;
235233

236234
if (flags & VFIO_IRQ_SET_DATA_EVENTFD) {
237235
int32_t fd = *(int32_t *)data;
238236

239-
return vfio_set_trigger(vdev, index, fd, handler);
237+
return vfio_set_trigger(vdev, index, fd);
240238
}
241239

242240
if (flags & VFIO_IRQ_SET_DATA_NONE) {
@@ -260,6 +258,14 @@ int vfio_platform_set_irqs_ioctl(struct vfio_platform_device *vdev,
260258
unsigned start, unsigned count, uint32_t flags,
261259
void *data) = NULL;
262260

261+
/*
262+
* For compatibility, errors from request_irq() are local to the
263+
* SET_IRQS path and reflected in the name pointer. This allows,
264+
* for example, polling mode fallback for an exclusive IRQ failure.
265+
*/
266+
if (IS_ERR(vdev->irqs[index].name))
267+
return PTR_ERR(vdev->irqs[index].name);
268+
263269
switch (flags & VFIO_IRQ_SET_ACTION_TYPE_MASK) {
264270
case VFIO_IRQ_SET_ACTION_MASK:
265271
func = vfio_platform_set_irq_mask;
@@ -280,7 +286,7 @@ int vfio_platform_set_irqs_ioctl(struct vfio_platform_device *vdev,
280286

281287
int vfio_platform_irq_init(struct vfio_platform_device *vdev)
282288
{
283-
int cnt = 0, i;
289+
int cnt = 0, i, ret = 0;
284290

285291
while (vdev->get_irq(vdev, cnt) >= 0)
286292
cnt++;
@@ -292,29 +298,54 @@ int vfio_platform_irq_init(struct vfio_platform_device *vdev)
292298

293299
for (i = 0; i < cnt; i++) {
294300
int hwirq = vdev->get_irq(vdev, i);
301+
irq_handler_t handler = vfio_irq_handler;
295302

296-
if (hwirq < 0)
303+
if (hwirq < 0) {
304+
ret = -EINVAL;
297305
goto err;
306+
}
298307

299308
spin_lock_init(&vdev->irqs[i].lock);
300309

301310
vdev->irqs[i].flags = VFIO_IRQ_INFO_EVENTFD;
302311

303-
if (irq_get_trigger_type(hwirq) & IRQ_TYPE_LEVEL_MASK)
312+
if (irq_get_trigger_type(hwirq) & IRQ_TYPE_LEVEL_MASK) {
304313
vdev->irqs[i].flags |= VFIO_IRQ_INFO_MASKABLE
305314
| VFIO_IRQ_INFO_AUTOMASKED;
315+
handler = vfio_automasked_irq_handler;
316+
}
306317

307318
vdev->irqs[i].count = 1;
308319
vdev->irqs[i].hwirq = hwirq;
309320
vdev->irqs[i].masked = false;
321+
vdev->irqs[i].name = kasprintf(GFP_KERNEL_ACCOUNT,
322+
"vfio-irq[%d](%s)", hwirq,
323+
vdev->name);
324+
if (!vdev->irqs[i].name) {
325+
ret = -ENOMEM;
326+
goto err;
327+
}
328+
329+
ret = request_irq(hwirq, handler, IRQF_NO_AUTOEN,
330+
vdev->irqs[i].name, &vdev->irqs[i]);
331+
if (ret) {
332+
kfree(vdev->irqs[i].name);
333+
vdev->irqs[i].name = ERR_PTR(ret);
334+
}
310335
}
311336

312337
vdev->num_irqs = cnt;
313338

314339
return 0;
315340
err:
341+
for (--i; i >= 0; i--) {
342+
if (!IS_ERR(vdev->irqs[i].name)) {
343+
free_irq(vdev->irqs[i].hwirq, &vdev->irqs[i]);
344+
kfree(vdev->irqs[i].name);
345+
}
346+
}
316347
kfree(vdev->irqs);
317-
return -EINVAL;
348+
return ret;
318349
}
319350

320351
void vfio_platform_irq_cleanup(struct vfio_platform_device *vdev)
@@ -324,7 +355,12 @@ void vfio_platform_irq_cleanup(struct vfio_platform_device *vdev)
324355
for (i = 0; i < vdev->num_irqs; i++) {
325356
vfio_virqfd_disable(&vdev->irqs[i].mask);
326357
vfio_virqfd_disable(&vdev->irqs[i].unmask);
327-
vfio_set_trigger(vdev, i, -1, NULL);
358+
if (!IS_ERR(vdev->irqs[i].name)) {
359+
free_irq(vdev->irqs[i].hwirq, &vdev->irqs[i]);
360+
if (vdev->irqs[i].trigger)
361+
eventfd_ctx_put(vdev->irqs[i].trigger);
362+
kfree(vdev->irqs[i].name);
363+
}
328364
}
329365

330366
vdev->num_irqs = 0;

0 commit comments

Comments
 (0)