brintos

brintos / linux-shallow public Read only

0
0
Text · 8.2 KiB · ef41ece Raw
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