369 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * VFIO platform devices interrupt handling4 *5 * Copyright (C) 2013 - Virtual Open Systems6 * 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 139/*140 * The trigger eventfd is guaranteed valid in the interrupt path141 * and protected by the igate mutex when triggered via ioctl.142 */143static 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 149static irqreturn_t vfio_automasked_irq_handler(int irq, void *dev_id)150{151 struct vfio_platform_irq *irq_ctx = dev_id;152 unsigned long flags;153 int ret = IRQ_NONE;154 155 spin_lock_irqsave(&irq_ctx->lock, flags);156 157 if (!irq_ctx->masked) {158 ret = IRQ_HANDLED;159 160 /* automask maskable interrupts */161 disable_irq_nosync(irq_ctx->hwirq);162 irq_ctx->masked = true;163 }164 165 spin_unlock_irqrestore(&irq_ctx->lock, flags);166 167 if (ret == IRQ_HANDLED)168 vfio_send_eventfd(irq_ctx);169 170 return ret;171}172 173static irqreturn_t vfio_irq_handler(int irq, void *dev_id)174{175 struct vfio_platform_irq *irq_ctx = dev_id;176 177 vfio_send_eventfd(irq_ctx);178 179 return IRQ_HANDLED;180}181 182static int vfio_set_trigger(struct vfio_platform_device *vdev, int index,183 int fd)184{185 struct vfio_platform_irq *irq = &vdev->irqs[index];186 struct eventfd_ctx *trigger;187 188 if (irq->trigger) {189 disable_irq(irq->hwirq);190 eventfd_ctx_put(irq->trigger);191 irq->trigger = NULL;192 }193 194 if (fd < 0) /* Disable only */195 return 0;196 197 trigger = eventfd_ctx_fdget(fd);198 if (IS_ERR(trigger))199 return PTR_ERR(trigger);200 201 irq->trigger = trigger;202 203 /*204 * irq->masked effectively provides nested disables within the overall205 * enable relative to trigger. Specifically request_irq() is called206 * with NO_AUTOEN, therefore the IRQ is initially disabled. The user207 * may only further disable the IRQ with a MASK operations because208 * irq->masked is initially false.209 */210 enable_irq(irq->hwirq);211 212 return 0;213}214 215static int vfio_platform_set_irq_trigger(struct vfio_platform_device *vdev,216 unsigned index, unsigned start,217 unsigned count, uint32_t flags,218 void *data)219{220 struct vfio_platform_irq *irq = &vdev->irqs[index];221 irq_handler_t handler;222 223 if (vdev->irqs[index].flags & VFIO_IRQ_INFO_AUTOMASKED)224 handler = vfio_automasked_irq_handler;225 else226 handler = vfio_irq_handler;227 228 if (!count && (flags & VFIO_IRQ_SET_DATA_NONE))229 return vfio_set_trigger(vdev, index, -1);230 231 if (start != 0 || count != 1)232 return -EINVAL;233 234 if (flags & VFIO_IRQ_SET_DATA_EVENTFD) {235 int32_t fd = *(int32_t *)data;236 237 return vfio_set_trigger(vdev, index, fd);238 }239 240 if (flags & VFIO_IRQ_SET_DATA_NONE) {241 handler(irq->hwirq, irq);242 243 } else if (flags & VFIO_IRQ_SET_DATA_BOOL) {244 uint8_t trigger = *(uint8_t *)data;245 246 if (trigger)247 handler(irq->hwirq, irq);248 }249 250 return 0;251}252 253int vfio_platform_set_irqs_ioctl(struct vfio_platform_device *vdev,254 uint32_t flags, unsigned index, unsigned start,255 unsigned count, void *data)256{257 int (*func)(struct vfio_platform_device *vdev, unsigned index,258 unsigned start, unsigned count, uint32_t flags,259 void *data) = NULL;260 261 /*262 * For compatibility, errors from request_irq() are local to the263 * 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 269 switch (flags & VFIO_IRQ_SET_ACTION_TYPE_MASK) {270 case VFIO_IRQ_SET_ACTION_MASK:271 func = vfio_platform_set_irq_mask;272 break;273 case VFIO_IRQ_SET_ACTION_UNMASK:274 func = vfio_platform_set_irq_unmask;275 break;276 case VFIO_IRQ_SET_ACTION_TRIGGER:277 func = vfio_platform_set_irq_trigger;278 break;279 }280 281 if (!func)282 return -ENOTTY;283 284 return func(vdev, index, start, count, flags, data);285}286 287int vfio_platform_irq_init(struct vfio_platform_device *vdev)288{289 int cnt = 0, i, ret = 0;290 291 while (vdev->get_irq(vdev, cnt) >= 0)292 cnt++;293 294 vdev->irqs = kcalloc(cnt, sizeof(struct vfio_platform_irq),295 GFP_KERNEL_ACCOUNT);296 if (!vdev->irqs)297 return -ENOMEM;298 299 for (i = 0; i < cnt; i++) {300 int hwirq = vdev->get_irq(vdev, i);301 irq_handler_t handler = vfio_irq_handler;302 303 if (hwirq < 0) {304 ret = -EINVAL;305 goto err;306 }307 308 spin_lock_init(&vdev->irqs[i].lock);309 310 vdev->irqs[i].flags = VFIO_IRQ_INFO_EVENTFD;311 312 if (irq_get_trigger_type(hwirq) & IRQ_TYPE_LEVEL_MASK) {313 vdev->irqs[i].flags |= VFIO_IRQ_INFO_MASKABLE314 | VFIO_IRQ_INFO_AUTOMASKED;315 handler = vfio_automasked_irq_handler;316 }317 318 vdev->irqs[i].count = 1;319 vdev->irqs[i].hwirq = hwirq;320 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 }335 }336 337 vdev->num_irqs = cnt;338 339 return 0;340err: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 }347 kfree(vdev->irqs);348 return ret;349}350 351void vfio_platform_irq_cleanup(struct vfio_platform_device *vdev)352{353 int i;354 355 for (i = 0; i < vdev->num_irqs; i++) {356 vfio_virqfd_disable(&vdev->irqs[i].mask);357 vfio_virqfd_disable(&vdev->irqs[i].unmask);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 }364 }365 366 vdev->num_irqs = 0;367 kfree(vdev->irqs);368}369