18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * VFIO PCI interrupt handling 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2012 Red Hat, Inc. All rights reserved. 68c2ecf20Sopenharmony_ci * Author: Alex Williamson <alex.williamson@redhat.com> 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * Derived from original vfio: 98c2ecf20Sopenharmony_ci * Copyright 2010 Cisco Systems, Inc. All rights reserved. 108c2ecf20Sopenharmony_ci * Author: Tom Lyon, pugs@cisco.com 118c2ecf20Sopenharmony_ci */ 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci#include <linux/device.h> 148c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 158c2ecf20Sopenharmony_ci#include <linux/eventfd.h> 168c2ecf20Sopenharmony_ci#include <linux/msi.h> 178c2ecf20Sopenharmony_ci#include <linux/pci.h> 188c2ecf20Sopenharmony_ci#include <linux/file.h> 198c2ecf20Sopenharmony_ci#include <linux/vfio.h> 208c2ecf20Sopenharmony_ci#include <linux/wait.h> 218c2ecf20Sopenharmony_ci#include <linux/slab.h> 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci#include "vfio_pci_private.h" 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci/* 268c2ecf20Sopenharmony_ci * INTx 278c2ecf20Sopenharmony_ci */ 288c2ecf20Sopenharmony_cistatic void vfio_send_intx_eventfd(void *opaque, void *unused) 298c2ecf20Sopenharmony_ci{ 308c2ecf20Sopenharmony_ci struct vfio_pci_device *vdev = opaque; 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci if (likely(is_intx(vdev) && !vdev->virq_disabled)) 338c2ecf20Sopenharmony_ci eventfd_signal(vdev->ctx[0].trigger, 1); 348c2ecf20Sopenharmony_ci} 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_civoid vfio_pci_intx_mask(struct vfio_pci_device *vdev) 378c2ecf20Sopenharmony_ci{ 388c2ecf20Sopenharmony_ci struct pci_dev *pdev = vdev->pdev; 398c2ecf20Sopenharmony_ci unsigned long flags; 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci spin_lock_irqsave(&vdev->irqlock, flags); 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci /* 448c2ecf20Sopenharmony_ci * Masking can come from interrupt, ioctl, or config space 458c2ecf20Sopenharmony_ci * via INTx disable. The latter means this can get called 468c2ecf20Sopenharmony_ci * even when not using intx delivery. In this case, just 478c2ecf20Sopenharmony_ci * try to have the physical bit follow the virtual bit. 488c2ecf20Sopenharmony_ci */ 498c2ecf20Sopenharmony_ci if (unlikely(!is_intx(vdev))) { 508c2ecf20Sopenharmony_ci if (vdev->pci_2_3) 518c2ecf20Sopenharmony_ci pci_intx(pdev, 0); 528c2ecf20Sopenharmony_ci } else if (!vdev->ctx[0].masked) { 538c2ecf20Sopenharmony_ci /* 548c2ecf20Sopenharmony_ci * Can't use check_and_mask here because we always want to 558c2ecf20Sopenharmony_ci * mask, not just when something is pending. 568c2ecf20Sopenharmony_ci */ 578c2ecf20Sopenharmony_ci if (vdev->pci_2_3) 588c2ecf20Sopenharmony_ci pci_intx(pdev, 0); 598c2ecf20Sopenharmony_ci else 608c2ecf20Sopenharmony_ci disable_irq_nosync(pdev->irq); 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci vdev->ctx[0].masked = true; 638c2ecf20Sopenharmony_ci } 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&vdev->irqlock, flags); 668c2ecf20Sopenharmony_ci} 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci/* 698c2ecf20Sopenharmony_ci * If this is triggered by an eventfd, we can't call eventfd_signal 708c2ecf20Sopenharmony_ci * or else we'll deadlock on the eventfd wait queue. Return >0 when 718c2ecf20Sopenharmony_ci * a signal is necessary, which can then be handled via a work queue 728c2ecf20Sopenharmony_ci * or directly depending on the caller. 738c2ecf20Sopenharmony_ci */ 748c2ecf20Sopenharmony_cistatic int vfio_pci_intx_unmask_handler(void *opaque, void *unused) 758c2ecf20Sopenharmony_ci{ 768c2ecf20Sopenharmony_ci struct vfio_pci_device *vdev = opaque; 778c2ecf20Sopenharmony_ci struct pci_dev *pdev = vdev->pdev; 788c2ecf20Sopenharmony_ci unsigned long flags; 798c2ecf20Sopenharmony_ci int ret = 0; 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci spin_lock_irqsave(&vdev->irqlock, flags); 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci /* 848c2ecf20Sopenharmony_ci * Unmasking comes from ioctl or config, so again, have the 858c2ecf20Sopenharmony_ci * physical bit follow the virtual even when not using INTx. 868c2ecf20Sopenharmony_ci */ 878c2ecf20Sopenharmony_ci if (unlikely(!is_intx(vdev))) { 888c2ecf20Sopenharmony_ci if (vdev->pci_2_3) 898c2ecf20Sopenharmony_ci pci_intx(pdev, 1); 908c2ecf20Sopenharmony_ci } else if (vdev->ctx[0].masked && !vdev->virq_disabled) { 918c2ecf20Sopenharmony_ci /* 928c2ecf20Sopenharmony_ci * A pending interrupt here would immediately trigger, 938c2ecf20Sopenharmony_ci * but we can avoid that overhead by just re-sending 948c2ecf20Sopenharmony_ci * the interrupt to the user. 958c2ecf20Sopenharmony_ci */ 968c2ecf20Sopenharmony_ci if (vdev->pci_2_3) { 978c2ecf20Sopenharmony_ci if (!pci_check_and_unmask_intx(pdev)) 988c2ecf20Sopenharmony_ci ret = 1; 998c2ecf20Sopenharmony_ci } else 1008c2ecf20Sopenharmony_ci enable_irq(pdev->irq); 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci vdev->ctx[0].masked = (ret > 0); 1038c2ecf20Sopenharmony_ci } 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&vdev->irqlock, flags); 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci return ret; 1088c2ecf20Sopenharmony_ci} 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_civoid vfio_pci_intx_unmask(struct vfio_pci_device *vdev) 1118c2ecf20Sopenharmony_ci{ 1128c2ecf20Sopenharmony_ci if (vfio_pci_intx_unmask_handler(vdev, NULL) > 0) 1138c2ecf20Sopenharmony_ci vfio_send_intx_eventfd(vdev, NULL); 1148c2ecf20Sopenharmony_ci} 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_cistatic irqreturn_t vfio_intx_handler(int irq, void *dev_id) 1178c2ecf20Sopenharmony_ci{ 1188c2ecf20Sopenharmony_ci struct vfio_pci_device *vdev = dev_id; 1198c2ecf20Sopenharmony_ci unsigned long flags; 1208c2ecf20Sopenharmony_ci int ret = IRQ_NONE; 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci spin_lock_irqsave(&vdev->irqlock, flags); 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci if (!vdev->pci_2_3) { 1258c2ecf20Sopenharmony_ci disable_irq_nosync(vdev->pdev->irq); 1268c2ecf20Sopenharmony_ci vdev->ctx[0].masked = true; 1278c2ecf20Sopenharmony_ci ret = IRQ_HANDLED; 1288c2ecf20Sopenharmony_ci } else if (!vdev->ctx[0].masked && /* may be shared */ 1298c2ecf20Sopenharmony_ci pci_check_and_mask_intx(vdev->pdev)) { 1308c2ecf20Sopenharmony_ci vdev->ctx[0].masked = true; 1318c2ecf20Sopenharmony_ci ret = IRQ_HANDLED; 1328c2ecf20Sopenharmony_ci } 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&vdev->irqlock, flags); 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci if (ret == IRQ_HANDLED) 1378c2ecf20Sopenharmony_ci vfio_send_intx_eventfd(vdev, NULL); 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci return ret; 1408c2ecf20Sopenharmony_ci} 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_cistatic int vfio_intx_enable(struct vfio_pci_device *vdev) 1438c2ecf20Sopenharmony_ci{ 1448c2ecf20Sopenharmony_ci if (!is_irq_none(vdev)) 1458c2ecf20Sopenharmony_ci return -EINVAL; 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci if (!vdev->pdev->irq) 1488c2ecf20Sopenharmony_ci return -ENODEV; 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci vdev->ctx = kzalloc(sizeof(struct vfio_pci_irq_ctx), GFP_KERNEL); 1518c2ecf20Sopenharmony_ci if (!vdev->ctx) 1528c2ecf20Sopenharmony_ci return -ENOMEM; 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci vdev->num_ctx = 1; 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci /* 1578c2ecf20Sopenharmony_ci * If the virtual interrupt is masked, restore it. Devices 1588c2ecf20Sopenharmony_ci * supporting DisINTx can be masked at the hardware level 1598c2ecf20Sopenharmony_ci * here, non-PCI-2.3 devices will have to wait until the 1608c2ecf20Sopenharmony_ci * interrupt is enabled. 1618c2ecf20Sopenharmony_ci */ 1628c2ecf20Sopenharmony_ci vdev->ctx[0].masked = vdev->virq_disabled; 1638c2ecf20Sopenharmony_ci if (vdev->pci_2_3) 1648c2ecf20Sopenharmony_ci pci_intx(vdev->pdev, !vdev->ctx[0].masked); 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci vdev->irq_type = VFIO_PCI_INTX_IRQ_INDEX; 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci return 0; 1698c2ecf20Sopenharmony_ci} 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_cistatic int vfio_intx_set_signal(struct vfio_pci_device *vdev, int fd) 1728c2ecf20Sopenharmony_ci{ 1738c2ecf20Sopenharmony_ci struct pci_dev *pdev = vdev->pdev; 1748c2ecf20Sopenharmony_ci unsigned long irqflags = IRQF_SHARED; 1758c2ecf20Sopenharmony_ci struct eventfd_ctx *trigger; 1768c2ecf20Sopenharmony_ci unsigned long flags; 1778c2ecf20Sopenharmony_ci int ret; 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci if (vdev->ctx[0].trigger) { 1808c2ecf20Sopenharmony_ci free_irq(pdev->irq, vdev); 1818c2ecf20Sopenharmony_ci kfree(vdev->ctx[0].name); 1828c2ecf20Sopenharmony_ci eventfd_ctx_put(vdev->ctx[0].trigger); 1838c2ecf20Sopenharmony_ci vdev->ctx[0].trigger = NULL; 1848c2ecf20Sopenharmony_ci } 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci if (fd < 0) /* Disable only */ 1878c2ecf20Sopenharmony_ci return 0; 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci vdev->ctx[0].name = kasprintf(GFP_KERNEL, "vfio-intx(%s)", 1908c2ecf20Sopenharmony_ci pci_name(pdev)); 1918c2ecf20Sopenharmony_ci if (!vdev->ctx[0].name) 1928c2ecf20Sopenharmony_ci return -ENOMEM; 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci trigger = eventfd_ctx_fdget(fd); 1958c2ecf20Sopenharmony_ci if (IS_ERR(trigger)) { 1968c2ecf20Sopenharmony_ci kfree(vdev->ctx[0].name); 1978c2ecf20Sopenharmony_ci return PTR_ERR(trigger); 1988c2ecf20Sopenharmony_ci } 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci vdev->ctx[0].trigger = trigger; 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci if (!vdev->pci_2_3) 2038c2ecf20Sopenharmony_ci irqflags = 0; 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci ret = request_irq(pdev->irq, vfio_intx_handler, 2068c2ecf20Sopenharmony_ci irqflags, vdev->ctx[0].name, vdev); 2078c2ecf20Sopenharmony_ci if (ret) { 2088c2ecf20Sopenharmony_ci vdev->ctx[0].trigger = NULL; 2098c2ecf20Sopenharmony_ci kfree(vdev->ctx[0].name); 2108c2ecf20Sopenharmony_ci eventfd_ctx_put(trigger); 2118c2ecf20Sopenharmony_ci return ret; 2128c2ecf20Sopenharmony_ci } 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci /* 2158c2ecf20Sopenharmony_ci * INTx disable will stick across the new irq setup, 2168c2ecf20Sopenharmony_ci * disable_irq won't. 2178c2ecf20Sopenharmony_ci */ 2188c2ecf20Sopenharmony_ci spin_lock_irqsave(&vdev->irqlock, flags); 2198c2ecf20Sopenharmony_ci if (!vdev->pci_2_3 && vdev->ctx[0].masked) 2208c2ecf20Sopenharmony_ci disable_irq_nosync(pdev->irq); 2218c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&vdev->irqlock, flags); 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci return 0; 2248c2ecf20Sopenharmony_ci} 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_cistatic void vfio_intx_disable(struct vfio_pci_device *vdev) 2278c2ecf20Sopenharmony_ci{ 2288c2ecf20Sopenharmony_ci vfio_virqfd_disable(&vdev->ctx[0].unmask); 2298c2ecf20Sopenharmony_ci vfio_virqfd_disable(&vdev->ctx[0].mask); 2308c2ecf20Sopenharmony_ci vfio_intx_set_signal(vdev, -1); 2318c2ecf20Sopenharmony_ci vdev->irq_type = VFIO_PCI_NUM_IRQS; 2328c2ecf20Sopenharmony_ci vdev->num_ctx = 0; 2338c2ecf20Sopenharmony_ci kfree(vdev->ctx); 2348c2ecf20Sopenharmony_ci} 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci/* 2378c2ecf20Sopenharmony_ci * MSI/MSI-X 2388c2ecf20Sopenharmony_ci */ 2398c2ecf20Sopenharmony_cistatic irqreturn_t vfio_msihandler(int irq, void *arg) 2408c2ecf20Sopenharmony_ci{ 2418c2ecf20Sopenharmony_ci struct eventfd_ctx *trigger = arg; 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci eventfd_signal(trigger, 1); 2448c2ecf20Sopenharmony_ci return IRQ_HANDLED; 2458c2ecf20Sopenharmony_ci} 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_cistatic int vfio_msi_enable(struct vfio_pci_device *vdev, int nvec, bool msix) 2488c2ecf20Sopenharmony_ci{ 2498c2ecf20Sopenharmony_ci struct pci_dev *pdev = vdev->pdev; 2508c2ecf20Sopenharmony_ci unsigned int flag = msix ? PCI_IRQ_MSIX : PCI_IRQ_MSI; 2518c2ecf20Sopenharmony_ci int ret; 2528c2ecf20Sopenharmony_ci u16 cmd; 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci if (!is_irq_none(vdev)) 2558c2ecf20Sopenharmony_ci return -EINVAL; 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci vdev->ctx = kcalloc(nvec, sizeof(struct vfio_pci_irq_ctx), GFP_KERNEL); 2588c2ecf20Sopenharmony_ci if (!vdev->ctx) 2598c2ecf20Sopenharmony_ci return -ENOMEM; 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci /* return the number of supported vectors if we can't get all: */ 2628c2ecf20Sopenharmony_ci cmd = vfio_pci_memory_lock_and_enable(vdev); 2638c2ecf20Sopenharmony_ci ret = pci_alloc_irq_vectors(pdev, 1, nvec, flag); 2648c2ecf20Sopenharmony_ci if (ret < nvec) { 2658c2ecf20Sopenharmony_ci if (ret > 0) 2668c2ecf20Sopenharmony_ci pci_free_irq_vectors(pdev); 2678c2ecf20Sopenharmony_ci vfio_pci_memory_unlock_and_restore(vdev, cmd); 2688c2ecf20Sopenharmony_ci kfree(vdev->ctx); 2698c2ecf20Sopenharmony_ci return ret; 2708c2ecf20Sopenharmony_ci } 2718c2ecf20Sopenharmony_ci vfio_pci_memory_unlock_and_restore(vdev, cmd); 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci vdev->num_ctx = nvec; 2748c2ecf20Sopenharmony_ci vdev->irq_type = msix ? VFIO_PCI_MSIX_IRQ_INDEX : 2758c2ecf20Sopenharmony_ci VFIO_PCI_MSI_IRQ_INDEX; 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci if (!msix) { 2788c2ecf20Sopenharmony_ci /* 2798c2ecf20Sopenharmony_ci * Compute the virtual hardware field for max msi vectors - 2808c2ecf20Sopenharmony_ci * it is the log base 2 of the number of vectors. 2818c2ecf20Sopenharmony_ci */ 2828c2ecf20Sopenharmony_ci vdev->msi_qmax = fls(nvec * 2 - 1) - 1; 2838c2ecf20Sopenharmony_ci } 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ci return 0; 2868c2ecf20Sopenharmony_ci} 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_cistatic int vfio_msi_set_vector_signal(struct vfio_pci_device *vdev, 2898c2ecf20Sopenharmony_ci int vector, int fd, bool msix) 2908c2ecf20Sopenharmony_ci{ 2918c2ecf20Sopenharmony_ci struct pci_dev *pdev = vdev->pdev; 2928c2ecf20Sopenharmony_ci struct eventfd_ctx *trigger; 2938c2ecf20Sopenharmony_ci int irq, ret; 2948c2ecf20Sopenharmony_ci u16 cmd; 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_ci if (vector < 0 || vector >= vdev->num_ctx) 2978c2ecf20Sopenharmony_ci return -EINVAL; 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_ci irq = pci_irq_vector(pdev, vector); 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci if (vdev->ctx[vector].trigger) { 3028c2ecf20Sopenharmony_ci irq_bypass_unregister_producer(&vdev->ctx[vector].producer); 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_ci cmd = vfio_pci_memory_lock_and_enable(vdev); 3058c2ecf20Sopenharmony_ci free_irq(irq, vdev->ctx[vector].trigger); 3068c2ecf20Sopenharmony_ci vfio_pci_memory_unlock_and_restore(vdev, cmd); 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci kfree(vdev->ctx[vector].name); 3098c2ecf20Sopenharmony_ci eventfd_ctx_put(vdev->ctx[vector].trigger); 3108c2ecf20Sopenharmony_ci vdev->ctx[vector].trigger = NULL; 3118c2ecf20Sopenharmony_ci } 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_ci if (fd < 0) 3148c2ecf20Sopenharmony_ci return 0; 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_ci vdev->ctx[vector].name = kasprintf(GFP_KERNEL, "vfio-msi%s[%d](%s)", 3178c2ecf20Sopenharmony_ci msix ? "x" : "", vector, 3188c2ecf20Sopenharmony_ci pci_name(pdev)); 3198c2ecf20Sopenharmony_ci if (!vdev->ctx[vector].name) 3208c2ecf20Sopenharmony_ci return -ENOMEM; 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_ci trigger = eventfd_ctx_fdget(fd); 3238c2ecf20Sopenharmony_ci if (IS_ERR(trigger)) { 3248c2ecf20Sopenharmony_ci kfree(vdev->ctx[vector].name); 3258c2ecf20Sopenharmony_ci return PTR_ERR(trigger); 3268c2ecf20Sopenharmony_ci } 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_ci /* 3298c2ecf20Sopenharmony_ci * The MSIx vector table resides in device memory which may be cleared 3308c2ecf20Sopenharmony_ci * via backdoor resets. We don't allow direct access to the vector 3318c2ecf20Sopenharmony_ci * table so even if a userspace driver attempts to save/restore around 3328c2ecf20Sopenharmony_ci * such a reset it would be unsuccessful. To avoid this, restore the 3338c2ecf20Sopenharmony_ci * cached value of the message prior to enabling. 3348c2ecf20Sopenharmony_ci */ 3358c2ecf20Sopenharmony_ci cmd = vfio_pci_memory_lock_and_enable(vdev); 3368c2ecf20Sopenharmony_ci if (msix) { 3378c2ecf20Sopenharmony_ci struct msi_msg msg; 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ci get_cached_msi_msg(irq, &msg); 3408c2ecf20Sopenharmony_ci pci_write_msi_msg(irq, &msg); 3418c2ecf20Sopenharmony_ci } 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_ci ret = request_irq(irq, vfio_msihandler, 0, 3448c2ecf20Sopenharmony_ci vdev->ctx[vector].name, trigger); 3458c2ecf20Sopenharmony_ci vfio_pci_memory_unlock_and_restore(vdev, cmd); 3468c2ecf20Sopenharmony_ci if (ret) { 3478c2ecf20Sopenharmony_ci kfree(vdev->ctx[vector].name); 3488c2ecf20Sopenharmony_ci eventfd_ctx_put(trigger); 3498c2ecf20Sopenharmony_ci return ret; 3508c2ecf20Sopenharmony_ci } 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_ci vdev->ctx[vector].producer.token = trigger; 3538c2ecf20Sopenharmony_ci vdev->ctx[vector].producer.irq = irq; 3548c2ecf20Sopenharmony_ci ret = irq_bypass_register_producer(&vdev->ctx[vector].producer); 3558c2ecf20Sopenharmony_ci if (unlikely(ret)) { 3568c2ecf20Sopenharmony_ci dev_info(&pdev->dev, 3578c2ecf20Sopenharmony_ci "irq bypass producer (token %p) registration fails: %d\n", 3588c2ecf20Sopenharmony_ci vdev->ctx[vector].producer.token, ret); 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_ci vdev->ctx[vector].producer.token = NULL; 3618c2ecf20Sopenharmony_ci } 3628c2ecf20Sopenharmony_ci vdev->ctx[vector].trigger = trigger; 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_ci return 0; 3658c2ecf20Sopenharmony_ci} 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_cistatic int vfio_msi_set_block(struct vfio_pci_device *vdev, unsigned start, 3688c2ecf20Sopenharmony_ci unsigned count, int32_t *fds, bool msix) 3698c2ecf20Sopenharmony_ci{ 3708c2ecf20Sopenharmony_ci int i, j, ret = 0; 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ci if (start >= vdev->num_ctx || start + count > vdev->num_ctx) 3738c2ecf20Sopenharmony_ci return -EINVAL; 3748c2ecf20Sopenharmony_ci 3758c2ecf20Sopenharmony_ci for (i = 0, j = start; i < count && !ret; i++, j++) { 3768c2ecf20Sopenharmony_ci int fd = fds ? fds[i] : -1; 3778c2ecf20Sopenharmony_ci ret = vfio_msi_set_vector_signal(vdev, j, fd, msix); 3788c2ecf20Sopenharmony_ci } 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_ci if (ret) { 3818c2ecf20Sopenharmony_ci for (--j; j >= (int)start; j--) 3828c2ecf20Sopenharmony_ci vfio_msi_set_vector_signal(vdev, j, -1, msix); 3838c2ecf20Sopenharmony_ci } 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_ci return ret; 3868c2ecf20Sopenharmony_ci} 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_cistatic void vfio_msi_disable(struct vfio_pci_device *vdev, bool msix) 3898c2ecf20Sopenharmony_ci{ 3908c2ecf20Sopenharmony_ci struct pci_dev *pdev = vdev->pdev; 3918c2ecf20Sopenharmony_ci int i; 3928c2ecf20Sopenharmony_ci u16 cmd; 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_ci for (i = 0; i < vdev->num_ctx; i++) { 3958c2ecf20Sopenharmony_ci vfio_virqfd_disable(&vdev->ctx[i].unmask); 3968c2ecf20Sopenharmony_ci vfio_virqfd_disable(&vdev->ctx[i].mask); 3978c2ecf20Sopenharmony_ci } 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_ci vfio_msi_set_block(vdev, 0, vdev->num_ctx, NULL, msix); 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_ci cmd = vfio_pci_memory_lock_and_enable(vdev); 4028c2ecf20Sopenharmony_ci pci_free_irq_vectors(pdev); 4038c2ecf20Sopenharmony_ci vfio_pci_memory_unlock_and_restore(vdev, cmd); 4048c2ecf20Sopenharmony_ci 4058c2ecf20Sopenharmony_ci /* 4068c2ecf20Sopenharmony_ci * Both disable paths above use pci_intx_for_msi() to clear DisINTx 4078c2ecf20Sopenharmony_ci * via their shutdown paths. Restore for NoINTx devices. 4088c2ecf20Sopenharmony_ci */ 4098c2ecf20Sopenharmony_ci if (vdev->nointx) 4108c2ecf20Sopenharmony_ci pci_intx(pdev, 0); 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_ci vdev->irq_type = VFIO_PCI_NUM_IRQS; 4138c2ecf20Sopenharmony_ci vdev->num_ctx = 0; 4148c2ecf20Sopenharmony_ci kfree(vdev->ctx); 4158c2ecf20Sopenharmony_ci} 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_ci/* 4188c2ecf20Sopenharmony_ci * IOCTL support 4198c2ecf20Sopenharmony_ci */ 4208c2ecf20Sopenharmony_cistatic int vfio_pci_set_intx_unmask(struct vfio_pci_device *vdev, 4218c2ecf20Sopenharmony_ci unsigned index, unsigned start, 4228c2ecf20Sopenharmony_ci unsigned count, uint32_t flags, void *data) 4238c2ecf20Sopenharmony_ci{ 4248c2ecf20Sopenharmony_ci if (!is_intx(vdev) || start != 0 || count != 1) 4258c2ecf20Sopenharmony_ci return -EINVAL; 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_ci if (flags & VFIO_IRQ_SET_DATA_NONE) { 4288c2ecf20Sopenharmony_ci vfio_pci_intx_unmask(vdev); 4298c2ecf20Sopenharmony_ci } else if (flags & VFIO_IRQ_SET_DATA_BOOL) { 4308c2ecf20Sopenharmony_ci uint8_t unmask = *(uint8_t *)data; 4318c2ecf20Sopenharmony_ci if (unmask) 4328c2ecf20Sopenharmony_ci vfio_pci_intx_unmask(vdev); 4338c2ecf20Sopenharmony_ci } else if (flags & VFIO_IRQ_SET_DATA_EVENTFD) { 4348c2ecf20Sopenharmony_ci int32_t fd = *(int32_t *)data; 4358c2ecf20Sopenharmony_ci if (fd >= 0) 4368c2ecf20Sopenharmony_ci return vfio_virqfd_enable((void *) vdev, 4378c2ecf20Sopenharmony_ci vfio_pci_intx_unmask_handler, 4388c2ecf20Sopenharmony_ci vfio_send_intx_eventfd, NULL, 4398c2ecf20Sopenharmony_ci &vdev->ctx[0].unmask, fd); 4408c2ecf20Sopenharmony_ci 4418c2ecf20Sopenharmony_ci vfio_virqfd_disable(&vdev->ctx[0].unmask); 4428c2ecf20Sopenharmony_ci } 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_ci return 0; 4458c2ecf20Sopenharmony_ci} 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_cistatic int vfio_pci_set_intx_mask(struct vfio_pci_device *vdev, 4488c2ecf20Sopenharmony_ci unsigned index, unsigned start, 4498c2ecf20Sopenharmony_ci unsigned count, uint32_t flags, void *data) 4508c2ecf20Sopenharmony_ci{ 4518c2ecf20Sopenharmony_ci if (!is_intx(vdev) || start != 0 || count != 1) 4528c2ecf20Sopenharmony_ci return -EINVAL; 4538c2ecf20Sopenharmony_ci 4548c2ecf20Sopenharmony_ci if (flags & VFIO_IRQ_SET_DATA_NONE) { 4558c2ecf20Sopenharmony_ci vfio_pci_intx_mask(vdev); 4568c2ecf20Sopenharmony_ci } else if (flags & VFIO_IRQ_SET_DATA_BOOL) { 4578c2ecf20Sopenharmony_ci uint8_t mask = *(uint8_t *)data; 4588c2ecf20Sopenharmony_ci if (mask) 4598c2ecf20Sopenharmony_ci vfio_pci_intx_mask(vdev); 4608c2ecf20Sopenharmony_ci } else if (flags & VFIO_IRQ_SET_DATA_EVENTFD) { 4618c2ecf20Sopenharmony_ci return -ENOTTY; /* XXX implement me */ 4628c2ecf20Sopenharmony_ci } 4638c2ecf20Sopenharmony_ci 4648c2ecf20Sopenharmony_ci return 0; 4658c2ecf20Sopenharmony_ci} 4668c2ecf20Sopenharmony_ci 4678c2ecf20Sopenharmony_cistatic int vfio_pci_set_intx_trigger(struct vfio_pci_device *vdev, 4688c2ecf20Sopenharmony_ci unsigned index, unsigned start, 4698c2ecf20Sopenharmony_ci unsigned count, uint32_t flags, void *data) 4708c2ecf20Sopenharmony_ci{ 4718c2ecf20Sopenharmony_ci if (is_intx(vdev) && !count && (flags & VFIO_IRQ_SET_DATA_NONE)) { 4728c2ecf20Sopenharmony_ci vfio_intx_disable(vdev); 4738c2ecf20Sopenharmony_ci return 0; 4748c2ecf20Sopenharmony_ci } 4758c2ecf20Sopenharmony_ci 4768c2ecf20Sopenharmony_ci if (!(is_intx(vdev) || is_irq_none(vdev)) || start != 0 || count != 1) 4778c2ecf20Sopenharmony_ci return -EINVAL; 4788c2ecf20Sopenharmony_ci 4798c2ecf20Sopenharmony_ci if (flags & VFIO_IRQ_SET_DATA_EVENTFD) { 4808c2ecf20Sopenharmony_ci int32_t fd = *(int32_t *)data; 4818c2ecf20Sopenharmony_ci int ret; 4828c2ecf20Sopenharmony_ci 4838c2ecf20Sopenharmony_ci if (is_intx(vdev)) 4848c2ecf20Sopenharmony_ci return vfio_intx_set_signal(vdev, fd); 4858c2ecf20Sopenharmony_ci 4868c2ecf20Sopenharmony_ci ret = vfio_intx_enable(vdev); 4878c2ecf20Sopenharmony_ci if (ret) 4888c2ecf20Sopenharmony_ci return ret; 4898c2ecf20Sopenharmony_ci 4908c2ecf20Sopenharmony_ci ret = vfio_intx_set_signal(vdev, fd); 4918c2ecf20Sopenharmony_ci if (ret) 4928c2ecf20Sopenharmony_ci vfio_intx_disable(vdev); 4938c2ecf20Sopenharmony_ci 4948c2ecf20Sopenharmony_ci return ret; 4958c2ecf20Sopenharmony_ci } 4968c2ecf20Sopenharmony_ci 4978c2ecf20Sopenharmony_ci if (!is_intx(vdev)) 4988c2ecf20Sopenharmony_ci return -EINVAL; 4998c2ecf20Sopenharmony_ci 5008c2ecf20Sopenharmony_ci if (flags & VFIO_IRQ_SET_DATA_NONE) { 5018c2ecf20Sopenharmony_ci vfio_send_intx_eventfd(vdev, NULL); 5028c2ecf20Sopenharmony_ci } else if (flags & VFIO_IRQ_SET_DATA_BOOL) { 5038c2ecf20Sopenharmony_ci uint8_t trigger = *(uint8_t *)data; 5048c2ecf20Sopenharmony_ci if (trigger) 5058c2ecf20Sopenharmony_ci vfio_send_intx_eventfd(vdev, NULL); 5068c2ecf20Sopenharmony_ci } 5078c2ecf20Sopenharmony_ci return 0; 5088c2ecf20Sopenharmony_ci} 5098c2ecf20Sopenharmony_ci 5108c2ecf20Sopenharmony_cistatic int vfio_pci_set_msi_trigger(struct vfio_pci_device *vdev, 5118c2ecf20Sopenharmony_ci unsigned index, unsigned start, 5128c2ecf20Sopenharmony_ci unsigned count, uint32_t flags, void *data) 5138c2ecf20Sopenharmony_ci{ 5148c2ecf20Sopenharmony_ci int i; 5158c2ecf20Sopenharmony_ci bool msix = (index == VFIO_PCI_MSIX_IRQ_INDEX) ? true : false; 5168c2ecf20Sopenharmony_ci 5178c2ecf20Sopenharmony_ci if (irq_is(vdev, index) && !count && (flags & VFIO_IRQ_SET_DATA_NONE)) { 5188c2ecf20Sopenharmony_ci vfio_msi_disable(vdev, msix); 5198c2ecf20Sopenharmony_ci return 0; 5208c2ecf20Sopenharmony_ci } 5218c2ecf20Sopenharmony_ci 5228c2ecf20Sopenharmony_ci if (!(irq_is(vdev, index) || is_irq_none(vdev))) 5238c2ecf20Sopenharmony_ci return -EINVAL; 5248c2ecf20Sopenharmony_ci 5258c2ecf20Sopenharmony_ci if (flags & VFIO_IRQ_SET_DATA_EVENTFD) { 5268c2ecf20Sopenharmony_ci int32_t *fds = data; 5278c2ecf20Sopenharmony_ci int ret; 5288c2ecf20Sopenharmony_ci 5298c2ecf20Sopenharmony_ci if (vdev->irq_type == index) 5308c2ecf20Sopenharmony_ci return vfio_msi_set_block(vdev, start, count, 5318c2ecf20Sopenharmony_ci fds, msix); 5328c2ecf20Sopenharmony_ci 5338c2ecf20Sopenharmony_ci ret = vfio_msi_enable(vdev, start + count, msix); 5348c2ecf20Sopenharmony_ci if (ret) 5358c2ecf20Sopenharmony_ci return ret; 5368c2ecf20Sopenharmony_ci 5378c2ecf20Sopenharmony_ci ret = vfio_msi_set_block(vdev, start, count, fds, msix); 5388c2ecf20Sopenharmony_ci if (ret) 5398c2ecf20Sopenharmony_ci vfio_msi_disable(vdev, msix); 5408c2ecf20Sopenharmony_ci 5418c2ecf20Sopenharmony_ci return ret; 5428c2ecf20Sopenharmony_ci } 5438c2ecf20Sopenharmony_ci 5448c2ecf20Sopenharmony_ci if (!irq_is(vdev, index) || start + count > vdev->num_ctx) 5458c2ecf20Sopenharmony_ci return -EINVAL; 5468c2ecf20Sopenharmony_ci 5478c2ecf20Sopenharmony_ci for (i = start; i < start + count; i++) { 5488c2ecf20Sopenharmony_ci if (!vdev->ctx[i].trigger) 5498c2ecf20Sopenharmony_ci continue; 5508c2ecf20Sopenharmony_ci if (flags & VFIO_IRQ_SET_DATA_NONE) { 5518c2ecf20Sopenharmony_ci eventfd_signal(vdev->ctx[i].trigger, 1); 5528c2ecf20Sopenharmony_ci } else if (flags & VFIO_IRQ_SET_DATA_BOOL) { 5538c2ecf20Sopenharmony_ci uint8_t *bools = data; 5548c2ecf20Sopenharmony_ci if (bools[i - start]) 5558c2ecf20Sopenharmony_ci eventfd_signal(vdev->ctx[i].trigger, 1); 5568c2ecf20Sopenharmony_ci } 5578c2ecf20Sopenharmony_ci } 5588c2ecf20Sopenharmony_ci return 0; 5598c2ecf20Sopenharmony_ci} 5608c2ecf20Sopenharmony_ci 5618c2ecf20Sopenharmony_cistatic int vfio_pci_set_ctx_trigger_single(struct eventfd_ctx **ctx, 5628c2ecf20Sopenharmony_ci unsigned int count, uint32_t flags, 5638c2ecf20Sopenharmony_ci void *data) 5648c2ecf20Sopenharmony_ci{ 5658c2ecf20Sopenharmony_ci /* DATA_NONE/DATA_BOOL enables loopback testing */ 5668c2ecf20Sopenharmony_ci if (flags & VFIO_IRQ_SET_DATA_NONE) { 5678c2ecf20Sopenharmony_ci if (*ctx) { 5688c2ecf20Sopenharmony_ci if (count) { 5698c2ecf20Sopenharmony_ci eventfd_signal(*ctx, 1); 5708c2ecf20Sopenharmony_ci } else { 5718c2ecf20Sopenharmony_ci eventfd_ctx_put(*ctx); 5728c2ecf20Sopenharmony_ci *ctx = NULL; 5738c2ecf20Sopenharmony_ci } 5748c2ecf20Sopenharmony_ci return 0; 5758c2ecf20Sopenharmony_ci } 5768c2ecf20Sopenharmony_ci } else if (flags & VFIO_IRQ_SET_DATA_BOOL) { 5778c2ecf20Sopenharmony_ci uint8_t trigger; 5788c2ecf20Sopenharmony_ci 5798c2ecf20Sopenharmony_ci if (!count) 5808c2ecf20Sopenharmony_ci return -EINVAL; 5818c2ecf20Sopenharmony_ci 5828c2ecf20Sopenharmony_ci trigger = *(uint8_t *)data; 5838c2ecf20Sopenharmony_ci if (trigger && *ctx) 5848c2ecf20Sopenharmony_ci eventfd_signal(*ctx, 1); 5858c2ecf20Sopenharmony_ci 5868c2ecf20Sopenharmony_ci return 0; 5878c2ecf20Sopenharmony_ci } else if (flags & VFIO_IRQ_SET_DATA_EVENTFD) { 5888c2ecf20Sopenharmony_ci int32_t fd; 5898c2ecf20Sopenharmony_ci 5908c2ecf20Sopenharmony_ci if (!count) 5918c2ecf20Sopenharmony_ci return -EINVAL; 5928c2ecf20Sopenharmony_ci 5938c2ecf20Sopenharmony_ci fd = *(int32_t *)data; 5948c2ecf20Sopenharmony_ci if (fd == -1) { 5958c2ecf20Sopenharmony_ci if (*ctx) 5968c2ecf20Sopenharmony_ci eventfd_ctx_put(*ctx); 5978c2ecf20Sopenharmony_ci *ctx = NULL; 5988c2ecf20Sopenharmony_ci } else if (fd >= 0) { 5998c2ecf20Sopenharmony_ci struct eventfd_ctx *efdctx; 6008c2ecf20Sopenharmony_ci 6018c2ecf20Sopenharmony_ci efdctx = eventfd_ctx_fdget(fd); 6028c2ecf20Sopenharmony_ci if (IS_ERR(efdctx)) 6038c2ecf20Sopenharmony_ci return PTR_ERR(efdctx); 6048c2ecf20Sopenharmony_ci 6058c2ecf20Sopenharmony_ci if (*ctx) 6068c2ecf20Sopenharmony_ci eventfd_ctx_put(*ctx); 6078c2ecf20Sopenharmony_ci 6088c2ecf20Sopenharmony_ci *ctx = efdctx; 6098c2ecf20Sopenharmony_ci } 6108c2ecf20Sopenharmony_ci return 0; 6118c2ecf20Sopenharmony_ci } 6128c2ecf20Sopenharmony_ci 6138c2ecf20Sopenharmony_ci return -EINVAL; 6148c2ecf20Sopenharmony_ci} 6158c2ecf20Sopenharmony_ci 6168c2ecf20Sopenharmony_cistatic int vfio_pci_set_err_trigger(struct vfio_pci_device *vdev, 6178c2ecf20Sopenharmony_ci unsigned index, unsigned start, 6188c2ecf20Sopenharmony_ci unsigned count, uint32_t flags, void *data) 6198c2ecf20Sopenharmony_ci{ 6208c2ecf20Sopenharmony_ci if (index != VFIO_PCI_ERR_IRQ_INDEX || start != 0 || count > 1) 6218c2ecf20Sopenharmony_ci return -EINVAL; 6228c2ecf20Sopenharmony_ci 6238c2ecf20Sopenharmony_ci return vfio_pci_set_ctx_trigger_single(&vdev->err_trigger, 6248c2ecf20Sopenharmony_ci count, flags, data); 6258c2ecf20Sopenharmony_ci} 6268c2ecf20Sopenharmony_ci 6278c2ecf20Sopenharmony_cistatic int vfio_pci_set_req_trigger(struct vfio_pci_device *vdev, 6288c2ecf20Sopenharmony_ci unsigned index, unsigned start, 6298c2ecf20Sopenharmony_ci unsigned count, uint32_t flags, void *data) 6308c2ecf20Sopenharmony_ci{ 6318c2ecf20Sopenharmony_ci if (index != VFIO_PCI_REQ_IRQ_INDEX || start != 0 || count > 1) 6328c2ecf20Sopenharmony_ci return -EINVAL; 6338c2ecf20Sopenharmony_ci 6348c2ecf20Sopenharmony_ci return vfio_pci_set_ctx_trigger_single(&vdev->req_trigger, 6358c2ecf20Sopenharmony_ci count, flags, data); 6368c2ecf20Sopenharmony_ci} 6378c2ecf20Sopenharmony_ci 6388c2ecf20Sopenharmony_ciint vfio_pci_set_irqs_ioctl(struct vfio_pci_device *vdev, uint32_t flags, 6398c2ecf20Sopenharmony_ci unsigned index, unsigned start, unsigned count, 6408c2ecf20Sopenharmony_ci void *data) 6418c2ecf20Sopenharmony_ci{ 6428c2ecf20Sopenharmony_ci int (*func)(struct vfio_pci_device *vdev, unsigned index, 6438c2ecf20Sopenharmony_ci unsigned start, unsigned count, uint32_t flags, 6448c2ecf20Sopenharmony_ci void *data) = NULL; 6458c2ecf20Sopenharmony_ci 6468c2ecf20Sopenharmony_ci switch (index) { 6478c2ecf20Sopenharmony_ci case VFIO_PCI_INTX_IRQ_INDEX: 6488c2ecf20Sopenharmony_ci switch (flags & VFIO_IRQ_SET_ACTION_TYPE_MASK) { 6498c2ecf20Sopenharmony_ci case VFIO_IRQ_SET_ACTION_MASK: 6508c2ecf20Sopenharmony_ci func = vfio_pci_set_intx_mask; 6518c2ecf20Sopenharmony_ci break; 6528c2ecf20Sopenharmony_ci case VFIO_IRQ_SET_ACTION_UNMASK: 6538c2ecf20Sopenharmony_ci func = vfio_pci_set_intx_unmask; 6548c2ecf20Sopenharmony_ci break; 6558c2ecf20Sopenharmony_ci case VFIO_IRQ_SET_ACTION_TRIGGER: 6568c2ecf20Sopenharmony_ci func = vfio_pci_set_intx_trigger; 6578c2ecf20Sopenharmony_ci break; 6588c2ecf20Sopenharmony_ci } 6598c2ecf20Sopenharmony_ci break; 6608c2ecf20Sopenharmony_ci case VFIO_PCI_MSI_IRQ_INDEX: 6618c2ecf20Sopenharmony_ci case VFIO_PCI_MSIX_IRQ_INDEX: 6628c2ecf20Sopenharmony_ci switch (flags & VFIO_IRQ_SET_ACTION_TYPE_MASK) { 6638c2ecf20Sopenharmony_ci case VFIO_IRQ_SET_ACTION_MASK: 6648c2ecf20Sopenharmony_ci case VFIO_IRQ_SET_ACTION_UNMASK: 6658c2ecf20Sopenharmony_ci /* XXX Need masking support exported */ 6668c2ecf20Sopenharmony_ci break; 6678c2ecf20Sopenharmony_ci case VFIO_IRQ_SET_ACTION_TRIGGER: 6688c2ecf20Sopenharmony_ci func = vfio_pci_set_msi_trigger; 6698c2ecf20Sopenharmony_ci break; 6708c2ecf20Sopenharmony_ci } 6718c2ecf20Sopenharmony_ci break; 6728c2ecf20Sopenharmony_ci case VFIO_PCI_ERR_IRQ_INDEX: 6738c2ecf20Sopenharmony_ci switch (flags & VFIO_IRQ_SET_ACTION_TYPE_MASK) { 6748c2ecf20Sopenharmony_ci case VFIO_IRQ_SET_ACTION_TRIGGER: 6758c2ecf20Sopenharmony_ci if (pci_is_pcie(vdev->pdev)) 6768c2ecf20Sopenharmony_ci func = vfio_pci_set_err_trigger; 6778c2ecf20Sopenharmony_ci break; 6788c2ecf20Sopenharmony_ci } 6798c2ecf20Sopenharmony_ci break; 6808c2ecf20Sopenharmony_ci case VFIO_PCI_REQ_IRQ_INDEX: 6818c2ecf20Sopenharmony_ci switch (flags & VFIO_IRQ_SET_ACTION_TYPE_MASK) { 6828c2ecf20Sopenharmony_ci case VFIO_IRQ_SET_ACTION_TRIGGER: 6838c2ecf20Sopenharmony_ci func = vfio_pci_set_req_trigger; 6848c2ecf20Sopenharmony_ci break; 6858c2ecf20Sopenharmony_ci } 6868c2ecf20Sopenharmony_ci break; 6878c2ecf20Sopenharmony_ci } 6888c2ecf20Sopenharmony_ci 6898c2ecf20Sopenharmony_ci if (!func) 6908c2ecf20Sopenharmony_ci return -ENOTTY; 6918c2ecf20Sopenharmony_ci 6928c2ecf20Sopenharmony_ci return func(vdev, index, start, count, flags, data); 6938c2ecf20Sopenharmony_ci} 694