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 = kcalloc(nvec, sizeof(struct vfio_cdx_irq), GFP_KERNEL); 31 if (!vdev->cdx_irqs) 32 return -ENOMEM; 33 34 ret = cdx_enable_msi(cdx_dev); 35 if (ret) { 36 kfree(vdev->cdx_irqs); 37 return ret; 38 } 39 40 /* Allocate cdx MSIs */ 41 ret = msi_domain_alloc_irqs(dev, MSI_DEFAULT_DOMAIN, nvec); 42 if (ret) { 43 cdx_disable_msi(cdx_dev); 44 kfree(vdev->cdx_irqs); 45 return ret; 46 } 47 48 for (msi_idx = 0; msi_idx < nvec; msi_idx++) 49 vdev->cdx_irqs[msi_idx].irq_no = msi_get_virq(dev, msi_idx); 50 51 vdev->msi_count = nvec; 52 vdev->config_msi = 1; 53 54 return 0; 55 } 56 57 static int vfio_cdx_msi_set_vector_signal(struct vfio_cdx_device *vdev, 58 int vector, int fd) 59 { 60 struct eventfd_ctx *trigger; 61 int irq_no, ret; 62 63 if (vector < 0 || vector >= vdev->msi_count) 64 return -EINVAL; 65 66 irq_no = vdev->cdx_irqs[vector].irq_no; 67 68 if (vdev->cdx_irqs[vector].trigger) { 69 free_irq(irq_no, vdev->cdx_irqs[vector].trigger); 70 kfree(vdev->cdx_irqs[vector].name); 71 eventfd_ctx_put(vdev->cdx_irqs[vector].trigger); 72 vdev->cdx_irqs[vector].trigger = NULL; 73 } 74 75 if (fd < 0) 76 return 0; 77 78 vdev->cdx_irqs[vector].name = kasprintf(GFP_KERNEL, "vfio-msi[%d](%s)", 79 vector, dev_name(vdev->vdev.dev)); 80 if (!vdev->cdx_irqs[vector].name) 81 return -ENOMEM; 82 83 trigger = eventfd_ctx_fdget(fd); 84 if (IS_ERR(trigger)) { 85 kfree(vdev->cdx_irqs[vector].name); 86 return PTR_ERR(trigger); 87 } 88 89 ret = request_irq(irq_no, vfio_cdx_msihandler, 0, 90 vdev->cdx_irqs[vector].name, trigger); 91 if (ret) { 92 kfree(vdev->cdx_irqs[vector].name); 93 eventfd_ctx_put(trigger); 94 return ret; 95 } 96 97 vdev->cdx_irqs[vector].trigger = trigger; 98 99 return 0; 100 } 101 102 static int vfio_cdx_msi_set_block(struct vfio_cdx_device *vdev, 103 unsigned int start, unsigned int count, 104 int32_t *fds) 105 { 106 int i, j, ret = 0; 107 108 if (start >= vdev->msi_count || start + count > vdev->msi_count) 109 return -EINVAL; 110 111 for (i = 0, j = start; i < count && !ret; i++, j++) { 112 int fd = fds ? fds[i] : -1; 113 114 ret = vfio_cdx_msi_set_vector_signal(vdev, j, fd); 115 } 116 117 if (ret) { 118 for (--j; j >= (int)start; j--) 119 vfio_cdx_msi_set_vector_signal(vdev, j, -1); 120 } 121 122 return ret; 123 } 124 125 static void vfio_cdx_msi_disable(struct vfio_cdx_device *vdev) 126 { 127 struct cdx_device *cdx_dev = to_cdx_device(vdev->vdev.dev); 128 struct device *dev = vdev->vdev.dev; 129 130 vfio_cdx_msi_set_block(vdev, 0, vdev->msi_count, NULL); 131 132 if (!vdev->config_msi) 133 return; 134 135 msi_domain_free_irqs_all(dev, MSI_DEFAULT_DOMAIN); 136 cdx_disable_msi(cdx_dev); 137 kfree(vdev->cdx_irqs); 138 139 vdev->cdx_irqs = NULL; 140 vdev->msi_count = 0; 141 vdev->config_msi = 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 if (!count && (flags & VFIO_IRQ_SET_DATA_NONE)) { 156 vfio_cdx_msi_disable(vdev); 157 return 0; 158 } 159 160 if (flags & VFIO_IRQ_SET_DATA_EVENTFD) { 161 s32 *fds = data; 162 int ret; 163 164 if (vdev->config_msi) 165 return vfio_cdx_msi_set_block(vdev, start, count, 166 fds); 167 ret = vfio_cdx_msi_enable(vdev, cdx_dev->num_msi); 168 if (ret) 169 return ret; 170 171 ret = vfio_cdx_msi_set_block(vdev, start, count, fds); 172 if (ret) 173 vfio_cdx_msi_disable(vdev); 174 175 return ret; 176 } 177 178 for (i = start; i < start + count; i++) { 179 if (!vdev->cdx_irqs[i].trigger) 180 continue; 181 if (flags & VFIO_IRQ_SET_DATA_NONE) { 182 eventfd_signal(vdev->cdx_irqs[i].trigger); 183 } else if (flags & VFIO_IRQ_SET_DATA_BOOL) { 184 u8 *bools = data; 185 186 if (bools[i - start]) 187 eventfd_signal(vdev->cdx_irqs[i].trigger); 188 } 189 } 190 191 return 0; 192 } 193 194 int vfio_cdx_set_irqs_ioctl(struct vfio_cdx_device *vdev, 195 u32 flags, unsigned int index, 196 unsigned int start, unsigned int count, 197 void *data) 198 { 199 if (flags & VFIO_IRQ_SET_ACTION_TRIGGER) 200 return vfio_cdx_set_msi_trigger(vdev, index, start, 201 count, flags, data); 202 else 203 return -EINVAL; 204 } 205 206 /* Free All IRQs for the given device */ 207 void vfio_cdx_irqs_cleanup(struct vfio_cdx_device *vdev) 208 { 209 /* 210 * Device does not support any interrupt or the interrupts 211 * were not configured 212 */ 213 if (!vdev->cdx_irqs) 214 return; 215 216 vfio_cdx_set_msi_trigger(vdev, 0, 0, 0, VFIO_IRQ_SET_DATA_NONE, NULL); 217 } 218