Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at master 215 lines 4.8 kB view raw
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 16static 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 24static 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 50err_disable: 51 cdx_disable_msi(cdx_dev); 52err_free: 53 kfree(vdev->cdx_irqs); 54 vdev->cdx_irqs = NULL; 55 return ret; 56} 57 58static 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 103static 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 126static 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 144static 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 199int 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 */ 212void 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}