162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * VFIO PCI interrupt handling 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2012 Red Hat, Inc. All rights reserved. 662306a36Sopenharmony_ci * Author: Alex Williamson <alex.williamson@redhat.com> 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * Derived from original vfio: 962306a36Sopenharmony_ci * Copyright 2010 Cisco Systems, Inc. All rights reserved. 1062306a36Sopenharmony_ci * Author: Tom Lyon, pugs@cisco.com 1162306a36Sopenharmony_ci */ 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci#include <linux/device.h> 1462306a36Sopenharmony_ci#include <linux/interrupt.h> 1562306a36Sopenharmony_ci#include <linux/eventfd.h> 1662306a36Sopenharmony_ci#include <linux/msi.h> 1762306a36Sopenharmony_ci#include <linux/pci.h> 1862306a36Sopenharmony_ci#include <linux/file.h> 1962306a36Sopenharmony_ci#include <linux/vfio.h> 2062306a36Sopenharmony_ci#include <linux/wait.h> 2162306a36Sopenharmony_ci#include <linux/slab.h> 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci#include "vfio_pci_priv.h" 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_cistruct vfio_pci_irq_ctx { 2662306a36Sopenharmony_ci struct eventfd_ctx *trigger; 2762306a36Sopenharmony_ci struct virqfd *unmask; 2862306a36Sopenharmony_ci struct virqfd *mask; 2962306a36Sopenharmony_ci char *name; 3062306a36Sopenharmony_ci bool masked; 3162306a36Sopenharmony_ci struct irq_bypass_producer producer; 3262306a36Sopenharmony_ci}; 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_cistatic bool irq_is(struct vfio_pci_core_device *vdev, int type) 3562306a36Sopenharmony_ci{ 3662306a36Sopenharmony_ci return vdev->irq_type == type; 3762306a36Sopenharmony_ci} 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_cistatic bool is_intx(struct vfio_pci_core_device *vdev) 4062306a36Sopenharmony_ci{ 4162306a36Sopenharmony_ci return vdev->irq_type == VFIO_PCI_INTX_IRQ_INDEX; 4262306a36Sopenharmony_ci} 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_cistatic bool is_irq_none(struct vfio_pci_core_device *vdev) 4562306a36Sopenharmony_ci{ 4662306a36Sopenharmony_ci return !(vdev->irq_type == VFIO_PCI_INTX_IRQ_INDEX || 4762306a36Sopenharmony_ci vdev->irq_type == VFIO_PCI_MSI_IRQ_INDEX || 4862306a36Sopenharmony_ci vdev->irq_type == VFIO_PCI_MSIX_IRQ_INDEX); 4962306a36Sopenharmony_ci} 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_cistatic 5262306a36Sopenharmony_cistruct vfio_pci_irq_ctx *vfio_irq_ctx_get(struct vfio_pci_core_device *vdev, 5362306a36Sopenharmony_ci unsigned long index) 5462306a36Sopenharmony_ci{ 5562306a36Sopenharmony_ci return xa_load(&vdev->ctx, index); 5662306a36Sopenharmony_ci} 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_cistatic void vfio_irq_ctx_free(struct vfio_pci_core_device *vdev, 5962306a36Sopenharmony_ci struct vfio_pci_irq_ctx *ctx, unsigned long index) 6062306a36Sopenharmony_ci{ 6162306a36Sopenharmony_ci xa_erase(&vdev->ctx, index); 6262306a36Sopenharmony_ci kfree(ctx); 6362306a36Sopenharmony_ci} 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_cistatic struct vfio_pci_irq_ctx * 6662306a36Sopenharmony_civfio_irq_ctx_alloc(struct vfio_pci_core_device *vdev, unsigned long index) 6762306a36Sopenharmony_ci{ 6862306a36Sopenharmony_ci struct vfio_pci_irq_ctx *ctx; 6962306a36Sopenharmony_ci int ret; 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci ctx = kzalloc(sizeof(*ctx), GFP_KERNEL_ACCOUNT); 7262306a36Sopenharmony_ci if (!ctx) 7362306a36Sopenharmony_ci return NULL; 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci ret = xa_insert(&vdev->ctx, index, ctx, GFP_KERNEL_ACCOUNT); 7662306a36Sopenharmony_ci if (ret) { 7762306a36Sopenharmony_ci kfree(ctx); 7862306a36Sopenharmony_ci return NULL; 7962306a36Sopenharmony_ci } 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci return ctx; 8262306a36Sopenharmony_ci} 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci/* 8562306a36Sopenharmony_ci * INTx 8662306a36Sopenharmony_ci */ 8762306a36Sopenharmony_cistatic void vfio_send_intx_eventfd(void *opaque, void *unused) 8862306a36Sopenharmony_ci{ 8962306a36Sopenharmony_ci struct vfio_pci_core_device *vdev = opaque; 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci if (likely(is_intx(vdev) && !vdev->virq_disabled)) { 9262306a36Sopenharmony_ci struct vfio_pci_irq_ctx *ctx; 9362306a36Sopenharmony_ci struct eventfd_ctx *trigger; 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci ctx = vfio_irq_ctx_get(vdev, 0); 9662306a36Sopenharmony_ci if (WARN_ON_ONCE(!ctx)) 9762306a36Sopenharmony_ci return; 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci trigger = READ_ONCE(ctx->trigger); 10062306a36Sopenharmony_ci if (likely(trigger)) 10162306a36Sopenharmony_ci eventfd_signal(trigger, 1); 10262306a36Sopenharmony_ci } 10362306a36Sopenharmony_ci} 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci/* Returns true if the INTx vfio_pci_irq_ctx.masked value is changed. */ 10662306a36Sopenharmony_cistatic bool __vfio_pci_intx_mask(struct vfio_pci_core_device *vdev) 10762306a36Sopenharmony_ci{ 10862306a36Sopenharmony_ci struct pci_dev *pdev = vdev->pdev; 10962306a36Sopenharmony_ci struct vfio_pci_irq_ctx *ctx; 11062306a36Sopenharmony_ci unsigned long flags; 11162306a36Sopenharmony_ci bool masked_changed = false; 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci lockdep_assert_held(&vdev->igate); 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci spin_lock_irqsave(&vdev->irqlock, flags); 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci /* 11862306a36Sopenharmony_ci * Masking can come from interrupt, ioctl, or config space 11962306a36Sopenharmony_ci * via INTx disable. The latter means this can get called 12062306a36Sopenharmony_ci * even when not using intx delivery. In this case, just 12162306a36Sopenharmony_ci * try to have the physical bit follow the virtual bit. 12262306a36Sopenharmony_ci */ 12362306a36Sopenharmony_ci if (unlikely(!is_intx(vdev))) { 12462306a36Sopenharmony_ci if (vdev->pci_2_3) 12562306a36Sopenharmony_ci pci_intx(pdev, 0); 12662306a36Sopenharmony_ci goto out_unlock; 12762306a36Sopenharmony_ci } 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci ctx = vfio_irq_ctx_get(vdev, 0); 13062306a36Sopenharmony_ci if (WARN_ON_ONCE(!ctx)) 13162306a36Sopenharmony_ci goto out_unlock; 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci if (!ctx->masked) { 13462306a36Sopenharmony_ci /* 13562306a36Sopenharmony_ci * Can't use check_and_mask here because we always want to 13662306a36Sopenharmony_ci * mask, not just when something is pending. 13762306a36Sopenharmony_ci */ 13862306a36Sopenharmony_ci if (vdev->pci_2_3) 13962306a36Sopenharmony_ci pci_intx(pdev, 0); 14062306a36Sopenharmony_ci else 14162306a36Sopenharmony_ci disable_irq_nosync(pdev->irq); 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci ctx->masked = true; 14462306a36Sopenharmony_ci masked_changed = true; 14562306a36Sopenharmony_ci } 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ciout_unlock: 14862306a36Sopenharmony_ci spin_unlock_irqrestore(&vdev->irqlock, flags); 14962306a36Sopenharmony_ci return masked_changed; 15062306a36Sopenharmony_ci} 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_cibool vfio_pci_intx_mask(struct vfio_pci_core_device *vdev) 15362306a36Sopenharmony_ci{ 15462306a36Sopenharmony_ci bool mask_changed; 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci mutex_lock(&vdev->igate); 15762306a36Sopenharmony_ci mask_changed = __vfio_pci_intx_mask(vdev); 15862306a36Sopenharmony_ci mutex_unlock(&vdev->igate); 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci return mask_changed; 16162306a36Sopenharmony_ci} 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci/* 16462306a36Sopenharmony_ci * If this is triggered by an eventfd, we can't call eventfd_signal 16562306a36Sopenharmony_ci * or else we'll deadlock on the eventfd wait queue. Return >0 when 16662306a36Sopenharmony_ci * a signal is necessary, which can then be handled via a work queue 16762306a36Sopenharmony_ci * or directly depending on the caller. 16862306a36Sopenharmony_ci */ 16962306a36Sopenharmony_cistatic int vfio_pci_intx_unmask_handler(void *opaque, void *unused) 17062306a36Sopenharmony_ci{ 17162306a36Sopenharmony_ci struct vfio_pci_core_device *vdev = opaque; 17262306a36Sopenharmony_ci struct pci_dev *pdev = vdev->pdev; 17362306a36Sopenharmony_ci struct vfio_pci_irq_ctx *ctx; 17462306a36Sopenharmony_ci unsigned long flags; 17562306a36Sopenharmony_ci int ret = 0; 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci spin_lock_irqsave(&vdev->irqlock, flags); 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci /* 18062306a36Sopenharmony_ci * Unmasking comes from ioctl or config, so again, have the 18162306a36Sopenharmony_ci * physical bit follow the virtual even when not using INTx. 18262306a36Sopenharmony_ci */ 18362306a36Sopenharmony_ci if (unlikely(!is_intx(vdev))) { 18462306a36Sopenharmony_ci if (vdev->pci_2_3) 18562306a36Sopenharmony_ci pci_intx(pdev, 1); 18662306a36Sopenharmony_ci goto out_unlock; 18762306a36Sopenharmony_ci } 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci ctx = vfio_irq_ctx_get(vdev, 0); 19062306a36Sopenharmony_ci if (WARN_ON_ONCE(!ctx)) 19162306a36Sopenharmony_ci goto out_unlock; 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci if (ctx->masked && !vdev->virq_disabled) { 19462306a36Sopenharmony_ci /* 19562306a36Sopenharmony_ci * A pending interrupt here would immediately trigger, 19662306a36Sopenharmony_ci * but we can avoid that overhead by just re-sending 19762306a36Sopenharmony_ci * the interrupt to the user. 19862306a36Sopenharmony_ci */ 19962306a36Sopenharmony_ci if (vdev->pci_2_3) { 20062306a36Sopenharmony_ci if (!pci_check_and_unmask_intx(pdev)) 20162306a36Sopenharmony_ci ret = 1; 20262306a36Sopenharmony_ci } else 20362306a36Sopenharmony_ci enable_irq(pdev->irq); 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci ctx->masked = (ret > 0); 20662306a36Sopenharmony_ci } 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ciout_unlock: 20962306a36Sopenharmony_ci spin_unlock_irqrestore(&vdev->irqlock, flags); 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci return ret; 21262306a36Sopenharmony_ci} 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_cistatic void __vfio_pci_intx_unmask(struct vfio_pci_core_device *vdev) 21562306a36Sopenharmony_ci{ 21662306a36Sopenharmony_ci lockdep_assert_held(&vdev->igate); 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci if (vfio_pci_intx_unmask_handler(vdev, NULL) > 0) 21962306a36Sopenharmony_ci vfio_send_intx_eventfd(vdev, NULL); 22062306a36Sopenharmony_ci} 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_civoid vfio_pci_intx_unmask(struct vfio_pci_core_device *vdev) 22362306a36Sopenharmony_ci{ 22462306a36Sopenharmony_ci mutex_lock(&vdev->igate); 22562306a36Sopenharmony_ci __vfio_pci_intx_unmask(vdev); 22662306a36Sopenharmony_ci mutex_unlock(&vdev->igate); 22762306a36Sopenharmony_ci} 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_cistatic irqreturn_t vfio_intx_handler(int irq, void *dev_id) 23062306a36Sopenharmony_ci{ 23162306a36Sopenharmony_ci struct vfio_pci_core_device *vdev = dev_id; 23262306a36Sopenharmony_ci struct vfio_pci_irq_ctx *ctx; 23362306a36Sopenharmony_ci unsigned long flags; 23462306a36Sopenharmony_ci int ret = IRQ_NONE; 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci ctx = vfio_irq_ctx_get(vdev, 0); 23762306a36Sopenharmony_ci if (WARN_ON_ONCE(!ctx)) 23862306a36Sopenharmony_ci return ret; 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci spin_lock_irqsave(&vdev->irqlock, flags); 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci if (!vdev->pci_2_3) { 24362306a36Sopenharmony_ci disable_irq_nosync(vdev->pdev->irq); 24462306a36Sopenharmony_ci ctx->masked = true; 24562306a36Sopenharmony_ci ret = IRQ_HANDLED; 24662306a36Sopenharmony_ci } else if (!ctx->masked && /* may be shared */ 24762306a36Sopenharmony_ci pci_check_and_mask_intx(vdev->pdev)) { 24862306a36Sopenharmony_ci ctx->masked = true; 24962306a36Sopenharmony_ci ret = IRQ_HANDLED; 25062306a36Sopenharmony_ci } 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci spin_unlock_irqrestore(&vdev->irqlock, flags); 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci if (ret == IRQ_HANDLED) 25562306a36Sopenharmony_ci vfio_send_intx_eventfd(vdev, NULL); 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci return ret; 25862306a36Sopenharmony_ci} 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_cistatic int vfio_intx_enable(struct vfio_pci_core_device *vdev, 26162306a36Sopenharmony_ci struct eventfd_ctx *trigger) 26262306a36Sopenharmony_ci{ 26362306a36Sopenharmony_ci struct pci_dev *pdev = vdev->pdev; 26462306a36Sopenharmony_ci struct vfio_pci_irq_ctx *ctx; 26562306a36Sopenharmony_ci unsigned long irqflags; 26662306a36Sopenharmony_ci char *name; 26762306a36Sopenharmony_ci int ret; 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci if (!is_irq_none(vdev)) 27062306a36Sopenharmony_ci return -EINVAL; 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci if (!pdev->irq) 27362306a36Sopenharmony_ci return -ENODEV; 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci name = kasprintf(GFP_KERNEL_ACCOUNT, "vfio-intx(%s)", pci_name(pdev)); 27662306a36Sopenharmony_ci if (!name) 27762306a36Sopenharmony_ci return -ENOMEM; 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci ctx = vfio_irq_ctx_alloc(vdev, 0); 28062306a36Sopenharmony_ci if (!ctx) 28162306a36Sopenharmony_ci return -ENOMEM; 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci ctx->name = name; 28462306a36Sopenharmony_ci ctx->trigger = trigger; 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci /* 28762306a36Sopenharmony_ci * Fill the initial masked state based on virq_disabled. After 28862306a36Sopenharmony_ci * enable, changing the DisINTx bit in vconfig directly changes INTx 28962306a36Sopenharmony_ci * masking. igate prevents races during setup, once running masked 29062306a36Sopenharmony_ci * is protected via irqlock. 29162306a36Sopenharmony_ci * 29262306a36Sopenharmony_ci * Devices supporting DisINTx also reflect the current mask state in 29362306a36Sopenharmony_ci * the physical DisINTx bit, which is not affected during IRQ setup. 29462306a36Sopenharmony_ci * 29562306a36Sopenharmony_ci * Devices without DisINTx support require an exclusive interrupt. 29662306a36Sopenharmony_ci * IRQ masking is performed at the IRQ chip. Again, igate protects 29762306a36Sopenharmony_ci * against races during setup and IRQ handlers and irqfds are not 29862306a36Sopenharmony_ci * yet active, therefore masked is stable and can be used to 29962306a36Sopenharmony_ci * conditionally auto-enable the IRQ. 30062306a36Sopenharmony_ci * 30162306a36Sopenharmony_ci * irq_type must be stable while the IRQ handler is registered, 30262306a36Sopenharmony_ci * therefore it must be set before request_irq(). 30362306a36Sopenharmony_ci */ 30462306a36Sopenharmony_ci ctx->masked = vdev->virq_disabled; 30562306a36Sopenharmony_ci if (vdev->pci_2_3) { 30662306a36Sopenharmony_ci pci_intx(pdev, !ctx->masked); 30762306a36Sopenharmony_ci irqflags = IRQF_SHARED; 30862306a36Sopenharmony_ci } else { 30962306a36Sopenharmony_ci irqflags = ctx->masked ? IRQF_NO_AUTOEN : 0; 31062306a36Sopenharmony_ci } 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ci vdev->irq_type = VFIO_PCI_INTX_IRQ_INDEX; 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci ret = request_irq(pdev->irq, vfio_intx_handler, 31562306a36Sopenharmony_ci irqflags, ctx->name, vdev); 31662306a36Sopenharmony_ci if (ret) { 31762306a36Sopenharmony_ci vdev->irq_type = VFIO_PCI_NUM_IRQS; 31862306a36Sopenharmony_ci kfree(name); 31962306a36Sopenharmony_ci vfio_irq_ctx_free(vdev, ctx, 0); 32062306a36Sopenharmony_ci return ret; 32162306a36Sopenharmony_ci } 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ci return 0; 32462306a36Sopenharmony_ci} 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_cistatic int vfio_intx_set_signal(struct vfio_pci_core_device *vdev, 32762306a36Sopenharmony_ci struct eventfd_ctx *trigger) 32862306a36Sopenharmony_ci{ 32962306a36Sopenharmony_ci struct pci_dev *pdev = vdev->pdev; 33062306a36Sopenharmony_ci struct vfio_pci_irq_ctx *ctx; 33162306a36Sopenharmony_ci struct eventfd_ctx *old; 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci ctx = vfio_irq_ctx_get(vdev, 0); 33462306a36Sopenharmony_ci if (WARN_ON_ONCE(!ctx)) 33562306a36Sopenharmony_ci return -EINVAL; 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_ci old = ctx->trigger; 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_ci WRITE_ONCE(ctx->trigger, trigger); 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci /* Releasing an old ctx requires synchronizing in-flight users */ 34262306a36Sopenharmony_ci if (old) { 34362306a36Sopenharmony_ci synchronize_irq(pdev->irq); 34462306a36Sopenharmony_ci vfio_virqfd_flush_thread(&ctx->unmask); 34562306a36Sopenharmony_ci eventfd_ctx_put(old); 34662306a36Sopenharmony_ci } 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci return 0; 34962306a36Sopenharmony_ci} 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_cistatic void vfio_intx_disable(struct vfio_pci_core_device *vdev) 35262306a36Sopenharmony_ci{ 35362306a36Sopenharmony_ci struct pci_dev *pdev = vdev->pdev; 35462306a36Sopenharmony_ci struct vfio_pci_irq_ctx *ctx; 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ci ctx = vfio_irq_ctx_get(vdev, 0); 35762306a36Sopenharmony_ci WARN_ON_ONCE(!ctx); 35862306a36Sopenharmony_ci if (ctx) { 35962306a36Sopenharmony_ci vfio_virqfd_disable(&ctx->unmask); 36062306a36Sopenharmony_ci vfio_virqfd_disable(&ctx->mask); 36162306a36Sopenharmony_ci free_irq(pdev->irq, vdev); 36262306a36Sopenharmony_ci if (ctx->trigger) 36362306a36Sopenharmony_ci eventfd_ctx_put(ctx->trigger); 36462306a36Sopenharmony_ci kfree(ctx->name); 36562306a36Sopenharmony_ci vfio_irq_ctx_free(vdev, ctx, 0); 36662306a36Sopenharmony_ci } 36762306a36Sopenharmony_ci vdev->irq_type = VFIO_PCI_NUM_IRQS; 36862306a36Sopenharmony_ci} 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_ci/* 37162306a36Sopenharmony_ci * MSI/MSI-X 37262306a36Sopenharmony_ci */ 37362306a36Sopenharmony_cistatic irqreturn_t vfio_msihandler(int irq, void *arg) 37462306a36Sopenharmony_ci{ 37562306a36Sopenharmony_ci struct eventfd_ctx *trigger = arg; 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ci eventfd_signal(trigger, 1); 37862306a36Sopenharmony_ci return IRQ_HANDLED; 37962306a36Sopenharmony_ci} 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_cistatic int vfio_msi_enable(struct vfio_pci_core_device *vdev, int nvec, bool msix) 38262306a36Sopenharmony_ci{ 38362306a36Sopenharmony_ci struct pci_dev *pdev = vdev->pdev; 38462306a36Sopenharmony_ci unsigned int flag = msix ? PCI_IRQ_MSIX : PCI_IRQ_MSI; 38562306a36Sopenharmony_ci int ret; 38662306a36Sopenharmony_ci u16 cmd; 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ci if (!is_irq_none(vdev)) 38962306a36Sopenharmony_ci return -EINVAL; 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_ci /* return the number of supported vectors if we can't get all: */ 39262306a36Sopenharmony_ci cmd = vfio_pci_memory_lock_and_enable(vdev); 39362306a36Sopenharmony_ci ret = pci_alloc_irq_vectors(pdev, 1, nvec, flag); 39462306a36Sopenharmony_ci if (ret < nvec) { 39562306a36Sopenharmony_ci if (ret > 0) 39662306a36Sopenharmony_ci pci_free_irq_vectors(pdev); 39762306a36Sopenharmony_ci vfio_pci_memory_unlock_and_restore(vdev, cmd); 39862306a36Sopenharmony_ci return ret; 39962306a36Sopenharmony_ci } 40062306a36Sopenharmony_ci vfio_pci_memory_unlock_and_restore(vdev, cmd); 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_ci vdev->irq_type = msix ? VFIO_PCI_MSIX_IRQ_INDEX : 40362306a36Sopenharmony_ci VFIO_PCI_MSI_IRQ_INDEX; 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ci if (!msix) { 40662306a36Sopenharmony_ci /* 40762306a36Sopenharmony_ci * Compute the virtual hardware field for max msi vectors - 40862306a36Sopenharmony_ci * it is the log base 2 of the number of vectors. 40962306a36Sopenharmony_ci */ 41062306a36Sopenharmony_ci vdev->msi_qmax = fls(nvec * 2 - 1) - 1; 41162306a36Sopenharmony_ci } 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_ci return 0; 41462306a36Sopenharmony_ci} 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_ci/* 41762306a36Sopenharmony_ci * vfio_msi_alloc_irq() returns the Linux IRQ number of an MSI or MSI-X device 41862306a36Sopenharmony_ci * interrupt vector. If a Linux IRQ number is not available then a new 41962306a36Sopenharmony_ci * interrupt is allocated if dynamic MSI-X is supported. 42062306a36Sopenharmony_ci * 42162306a36Sopenharmony_ci * Where is vfio_msi_free_irq()? Allocated interrupts are maintained, 42262306a36Sopenharmony_ci * essentially forming a cache that subsequent allocations can draw from. 42362306a36Sopenharmony_ci * Interrupts are freed using pci_free_irq_vectors() when MSI/MSI-X is 42462306a36Sopenharmony_ci * disabled. 42562306a36Sopenharmony_ci */ 42662306a36Sopenharmony_cistatic int vfio_msi_alloc_irq(struct vfio_pci_core_device *vdev, 42762306a36Sopenharmony_ci unsigned int vector, bool msix) 42862306a36Sopenharmony_ci{ 42962306a36Sopenharmony_ci struct pci_dev *pdev = vdev->pdev; 43062306a36Sopenharmony_ci struct msi_map map; 43162306a36Sopenharmony_ci int irq; 43262306a36Sopenharmony_ci u16 cmd; 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_ci irq = pci_irq_vector(pdev, vector); 43562306a36Sopenharmony_ci if (WARN_ON_ONCE(irq == 0)) 43662306a36Sopenharmony_ci return -EINVAL; 43762306a36Sopenharmony_ci if (irq > 0 || !msix || !vdev->has_dyn_msix) 43862306a36Sopenharmony_ci return irq; 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ci cmd = vfio_pci_memory_lock_and_enable(vdev); 44162306a36Sopenharmony_ci map = pci_msix_alloc_irq_at(pdev, vector, NULL); 44262306a36Sopenharmony_ci vfio_pci_memory_unlock_and_restore(vdev, cmd); 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_ci return map.index < 0 ? map.index : map.virq; 44562306a36Sopenharmony_ci} 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_cistatic int vfio_msi_set_vector_signal(struct vfio_pci_core_device *vdev, 44862306a36Sopenharmony_ci unsigned int vector, int fd, bool msix) 44962306a36Sopenharmony_ci{ 45062306a36Sopenharmony_ci struct pci_dev *pdev = vdev->pdev; 45162306a36Sopenharmony_ci struct vfio_pci_irq_ctx *ctx; 45262306a36Sopenharmony_ci struct eventfd_ctx *trigger; 45362306a36Sopenharmony_ci int irq = -EINVAL, ret; 45462306a36Sopenharmony_ci u16 cmd; 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_ci ctx = vfio_irq_ctx_get(vdev, vector); 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_ci if (ctx) { 45962306a36Sopenharmony_ci irq_bypass_unregister_producer(&ctx->producer); 46062306a36Sopenharmony_ci irq = pci_irq_vector(pdev, vector); 46162306a36Sopenharmony_ci cmd = vfio_pci_memory_lock_and_enable(vdev); 46262306a36Sopenharmony_ci free_irq(irq, ctx->trigger); 46362306a36Sopenharmony_ci vfio_pci_memory_unlock_and_restore(vdev, cmd); 46462306a36Sopenharmony_ci /* Interrupt stays allocated, will be freed at MSI-X disable. */ 46562306a36Sopenharmony_ci kfree(ctx->name); 46662306a36Sopenharmony_ci eventfd_ctx_put(ctx->trigger); 46762306a36Sopenharmony_ci vfio_irq_ctx_free(vdev, ctx, vector); 46862306a36Sopenharmony_ci } 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_ci if (fd < 0) 47162306a36Sopenharmony_ci return 0; 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_ci if (irq == -EINVAL) { 47462306a36Sopenharmony_ci /* Interrupt stays allocated, will be freed at MSI-X disable. */ 47562306a36Sopenharmony_ci irq = vfio_msi_alloc_irq(vdev, vector, msix); 47662306a36Sopenharmony_ci if (irq < 0) 47762306a36Sopenharmony_ci return irq; 47862306a36Sopenharmony_ci } 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_ci ctx = vfio_irq_ctx_alloc(vdev, vector); 48162306a36Sopenharmony_ci if (!ctx) 48262306a36Sopenharmony_ci return -ENOMEM; 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_ci ctx->name = kasprintf(GFP_KERNEL_ACCOUNT, "vfio-msi%s[%d](%s)", 48562306a36Sopenharmony_ci msix ? "x" : "", vector, pci_name(pdev)); 48662306a36Sopenharmony_ci if (!ctx->name) { 48762306a36Sopenharmony_ci ret = -ENOMEM; 48862306a36Sopenharmony_ci goto out_free_ctx; 48962306a36Sopenharmony_ci } 49062306a36Sopenharmony_ci 49162306a36Sopenharmony_ci trigger = eventfd_ctx_fdget(fd); 49262306a36Sopenharmony_ci if (IS_ERR(trigger)) { 49362306a36Sopenharmony_ci ret = PTR_ERR(trigger); 49462306a36Sopenharmony_ci goto out_free_name; 49562306a36Sopenharmony_ci } 49662306a36Sopenharmony_ci 49762306a36Sopenharmony_ci /* 49862306a36Sopenharmony_ci * If the vector was previously allocated, refresh the on-device 49962306a36Sopenharmony_ci * message data before enabling in case it had been cleared or 50062306a36Sopenharmony_ci * corrupted (e.g. due to backdoor resets) since writing. 50162306a36Sopenharmony_ci */ 50262306a36Sopenharmony_ci cmd = vfio_pci_memory_lock_and_enable(vdev); 50362306a36Sopenharmony_ci if (msix) { 50462306a36Sopenharmony_ci struct msi_msg msg; 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_ci get_cached_msi_msg(irq, &msg); 50762306a36Sopenharmony_ci pci_write_msi_msg(irq, &msg); 50862306a36Sopenharmony_ci } 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_ci ret = request_irq(irq, vfio_msihandler, 0, ctx->name, trigger); 51162306a36Sopenharmony_ci vfio_pci_memory_unlock_and_restore(vdev, cmd); 51262306a36Sopenharmony_ci if (ret) 51362306a36Sopenharmony_ci goto out_put_eventfd_ctx; 51462306a36Sopenharmony_ci 51562306a36Sopenharmony_ci ctx->producer.token = trigger; 51662306a36Sopenharmony_ci ctx->producer.irq = irq; 51762306a36Sopenharmony_ci ret = irq_bypass_register_producer(&ctx->producer); 51862306a36Sopenharmony_ci if (unlikely(ret)) { 51962306a36Sopenharmony_ci dev_info(&pdev->dev, 52062306a36Sopenharmony_ci "irq bypass producer (token %p) registration fails: %d\n", 52162306a36Sopenharmony_ci ctx->producer.token, ret); 52262306a36Sopenharmony_ci 52362306a36Sopenharmony_ci ctx->producer.token = NULL; 52462306a36Sopenharmony_ci } 52562306a36Sopenharmony_ci ctx->trigger = trigger; 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_ci return 0; 52862306a36Sopenharmony_ci 52962306a36Sopenharmony_ciout_put_eventfd_ctx: 53062306a36Sopenharmony_ci eventfd_ctx_put(trigger); 53162306a36Sopenharmony_ciout_free_name: 53262306a36Sopenharmony_ci kfree(ctx->name); 53362306a36Sopenharmony_ciout_free_ctx: 53462306a36Sopenharmony_ci vfio_irq_ctx_free(vdev, ctx, vector); 53562306a36Sopenharmony_ci return ret; 53662306a36Sopenharmony_ci} 53762306a36Sopenharmony_ci 53862306a36Sopenharmony_cistatic int vfio_msi_set_block(struct vfio_pci_core_device *vdev, unsigned start, 53962306a36Sopenharmony_ci unsigned count, int32_t *fds, bool msix) 54062306a36Sopenharmony_ci{ 54162306a36Sopenharmony_ci unsigned int i, j; 54262306a36Sopenharmony_ci int ret = 0; 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_ci for (i = 0, j = start; i < count && !ret; i++, j++) { 54562306a36Sopenharmony_ci int fd = fds ? fds[i] : -1; 54662306a36Sopenharmony_ci ret = vfio_msi_set_vector_signal(vdev, j, fd, msix); 54762306a36Sopenharmony_ci } 54862306a36Sopenharmony_ci 54962306a36Sopenharmony_ci if (ret) { 55062306a36Sopenharmony_ci for (i = start; i < j; i++) 55162306a36Sopenharmony_ci vfio_msi_set_vector_signal(vdev, i, -1, msix); 55262306a36Sopenharmony_ci } 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_ci return ret; 55562306a36Sopenharmony_ci} 55662306a36Sopenharmony_ci 55762306a36Sopenharmony_cistatic void vfio_msi_disable(struct vfio_pci_core_device *vdev, bool msix) 55862306a36Sopenharmony_ci{ 55962306a36Sopenharmony_ci struct pci_dev *pdev = vdev->pdev; 56062306a36Sopenharmony_ci struct vfio_pci_irq_ctx *ctx; 56162306a36Sopenharmony_ci unsigned long i; 56262306a36Sopenharmony_ci u16 cmd; 56362306a36Sopenharmony_ci 56462306a36Sopenharmony_ci xa_for_each(&vdev->ctx, i, ctx) { 56562306a36Sopenharmony_ci vfio_virqfd_disable(&ctx->unmask); 56662306a36Sopenharmony_ci vfio_virqfd_disable(&ctx->mask); 56762306a36Sopenharmony_ci vfio_msi_set_vector_signal(vdev, i, -1, msix); 56862306a36Sopenharmony_ci } 56962306a36Sopenharmony_ci 57062306a36Sopenharmony_ci cmd = vfio_pci_memory_lock_and_enable(vdev); 57162306a36Sopenharmony_ci pci_free_irq_vectors(pdev); 57262306a36Sopenharmony_ci vfio_pci_memory_unlock_and_restore(vdev, cmd); 57362306a36Sopenharmony_ci 57462306a36Sopenharmony_ci /* 57562306a36Sopenharmony_ci * Both disable paths above use pci_intx_for_msi() to clear DisINTx 57662306a36Sopenharmony_ci * via their shutdown paths. Restore for NoINTx devices. 57762306a36Sopenharmony_ci */ 57862306a36Sopenharmony_ci if (vdev->nointx) 57962306a36Sopenharmony_ci pci_intx(pdev, 0); 58062306a36Sopenharmony_ci 58162306a36Sopenharmony_ci vdev->irq_type = VFIO_PCI_NUM_IRQS; 58262306a36Sopenharmony_ci} 58362306a36Sopenharmony_ci 58462306a36Sopenharmony_ci/* 58562306a36Sopenharmony_ci * IOCTL support 58662306a36Sopenharmony_ci */ 58762306a36Sopenharmony_cistatic int vfio_pci_set_intx_unmask(struct vfio_pci_core_device *vdev, 58862306a36Sopenharmony_ci unsigned index, unsigned start, 58962306a36Sopenharmony_ci unsigned count, uint32_t flags, void *data) 59062306a36Sopenharmony_ci{ 59162306a36Sopenharmony_ci if (!is_intx(vdev) || start != 0 || count != 1) 59262306a36Sopenharmony_ci return -EINVAL; 59362306a36Sopenharmony_ci 59462306a36Sopenharmony_ci if (flags & VFIO_IRQ_SET_DATA_NONE) { 59562306a36Sopenharmony_ci __vfio_pci_intx_unmask(vdev); 59662306a36Sopenharmony_ci } else if (flags & VFIO_IRQ_SET_DATA_BOOL) { 59762306a36Sopenharmony_ci uint8_t unmask = *(uint8_t *)data; 59862306a36Sopenharmony_ci if (unmask) 59962306a36Sopenharmony_ci __vfio_pci_intx_unmask(vdev); 60062306a36Sopenharmony_ci } else if (flags & VFIO_IRQ_SET_DATA_EVENTFD) { 60162306a36Sopenharmony_ci struct vfio_pci_irq_ctx *ctx = vfio_irq_ctx_get(vdev, 0); 60262306a36Sopenharmony_ci int32_t fd = *(int32_t *)data; 60362306a36Sopenharmony_ci 60462306a36Sopenharmony_ci if (WARN_ON_ONCE(!ctx)) 60562306a36Sopenharmony_ci return -EINVAL; 60662306a36Sopenharmony_ci if (fd >= 0) 60762306a36Sopenharmony_ci return vfio_virqfd_enable((void *) vdev, 60862306a36Sopenharmony_ci vfio_pci_intx_unmask_handler, 60962306a36Sopenharmony_ci vfio_send_intx_eventfd, NULL, 61062306a36Sopenharmony_ci &ctx->unmask, fd); 61162306a36Sopenharmony_ci 61262306a36Sopenharmony_ci vfio_virqfd_disable(&ctx->unmask); 61362306a36Sopenharmony_ci } 61462306a36Sopenharmony_ci 61562306a36Sopenharmony_ci return 0; 61662306a36Sopenharmony_ci} 61762306a36Sopenharmony_ci 61862306a36Sopenharmony_cistatic int vfio_pci_set_intx_mask(struct vfio_pci_core_device *vdev, 61962306a36Sopenharmony_ci unsigned index, unsigned start, 62062306a36Sopenharmony_ci unsigned count, uint32_t flags, void *data) 62162306a36Sopenharmony_ci{ 62262306a36Sopenharmony_ci if (!is_intx(vdev) || start != 0 || count != 1) 62362306a36Sopenharmony_ci return -EINVAL; 62462306a36Sopenharmony_ci 62562306a36Sopenharmony_ci if (flags & VFIO_IRQ_SET_DATA_NONE) { 62662306a36Sopenharmony_ci __vfio_pci_intx_mask(vdev); 62762306a36Sopenharmony_ci } else if (flags & VFIO_IRQ_SET_DATA_BOOL) { 62862306a36Sopenharmony_ci uint8_t mask = *(uint8_t *)data; 62962306a36Sopenharmony_ci if (mask) 63062306a36Sopenharmony_ci __vfio_pci_intx_mask(vdev); 63162306a36Sopenharmony_ci } else if (flags & VFIO_IRQ_SET_DATA_EVENTFD) { 63262306a36Sopenharmony_ci return -ENOTTY; /* XXX implement me */ 63362306a36Sopenharmony_ci } 63462306a36Sopenharmony_ci 63562306a36Sopenharmony_ci return 0; 63662306a36Sopenharmony_ci} 63762306a36Sopenharmony_ci 63862306a36Sopenharmony_cistatic int vfio_pci_set_intx_trigger(struct vfio_pci_core_device *vdev, 63962306a36Sopenharmony_ci unsigned index, unsigned start, 64062306a36Sopenharmony_ci unsigned count, uint32_t flags, void *data) 64162306a36Sopenharmony_ci{ 64262306a36Sopenharmony_ci if (is_intx(vdev) && !count && (flags & VFIO_IRQ_SET_DATA_NONE)) { 64362306a36Sopenharmony_ci vfio_intx_disable(vdev); 64462306a36Sopenharmony_ci return 0; 64562306a36Sopenharmony_ci } 64662306a36Sopenharmony_ci 64762306a36Sopenharmony_ci if (!(is_intx(vdev) || is_irq_none(vdev)) || start != 0 || count != 1) 64862306a36Sopenharmony_ci return -EINVAL; 64962306a36Sopenharmony_ci 65062306a36Sopenharmony_ci if (flags & VFIO_IRQ_SET_DATA_EVENTFD) { 65162306a36Sopenharmony_ci struct eventfd_ctx *trigger = NULL; 65262306a36Sopenharmony_ci int32_t fd = *(int32_t *)data; 65362306a36Sopenharmony_ci int ret; 65462306a36Sopenharmony_ci 65562306a36Sopenharmony_ci if (fd >= 0) { 65662306a36Sopenharmony_ci trigger = eventfd_ctx_fdget(fd); 65762306a36Sopenharmony_ci if (IS_ERR(trigger)) 65862306a36Sopenharmony_ci return PTR_ERR(trigger); 65962306a36Sopenharmony_ci } 66062306a36Sopenharmony_ci 66162306a36Sopenharmony_ci if (is_intx(vdev)) 66262306a36Sopenharmony_ci ret = vfio_intx_set_signal(vdev, trigger); 66362306a36Sopenharmony_ci else 66462306a36Sopenharmony_ci ret = vfio_intx_enable(vdev, trigger); 66562306a36Sopenharmony_ci 66662306a36Sopenharmony_ci if (ret && trigger) 66762306a36Sopenharmony_ci eventfd_ctx_put(trigger); 66862306a36Sopenharmony_ci 66962306a36Sopenharmony_ci return ret; 67062306a36Sopenharmony_ci } 67162306a36Sopenharmony_ci 67262306a36Sopenharmony_ci if (!is_intx(vdev)) 67362306a36Sopenharmony_ci return -EINVAL; 67462306a36Sopenharmony_ci 67562306a36Sopenharmony_ci if (flags & VFIO_IRQ_SET_DATA_NONE) { 67662306a36Sopenharmony_ci vfio_send_intx_eventfd(vdev, NULL); 67762306a36Sopenharmony_ci } else if (flags & VFIO_IRQ_SET_DATA_BOOL) { 67862306a36Sopenharmony_ci uint8_t trigger = *(uint8_t *)data; 67962306a36Sopenharmony_ci if (trigger) 68062306a36Sopenharmony_ci vfio_send_intx_eventfd(vdev, NULL); 68162306a36Sopenharmony_ci } 68262306a36Sopenharmony_ci return 0; 68362306a36Sopenharmony_ci} 68462306a36Sopenharmony_ci 68562306a36Sopenharmony_cistatic int vfio_pci_set_msi_trigger(struct vfio_pci_core_device *vdev, 68662306a36Sopenharmony_ci unsigned index, unsigned start, 68762306a36Sopenharmony_ci unsigned count, uint32_t flags, void *data) 68862306a36Sopenharmony_ci{ 68962306a36Sopenharmony_ci struct vfio_pci_irq_ctx *ctx; 69062306a36Sopenharmony_ci unsigned int i; 69162306a36Sopenharmony_ci bool msix = (index == VFIO_PCI_MSIX_IRQ_INDEX) ? true : false; 69262306a36Sopenharmony_ci 69362306a36Sopenharmony_ci if (irq_is(vdev, index) && !count && (flags & VFIO_IRQ_SET_DATA_NONE)) { 69462306a36Sopenharmony_ci vfio_msi_disable(vdev, msix); 69562306a36Sopenharmony_ci return 0; 69662306a36Sopenharmony_ci } 69762306a36Sopenharmony_ci 69862306a36Sopenharmony_ci if (!(irq_is(vdev, index) || is_irq_none(vdev))) 69962306a36Sopenharmony_ci return -EINVAL; 70062306a36Sopenharmony_ci 70162306a36Sopenharmony_ci if (flags & VFIO_IRQ_SET_DATA_EVENTFD) { 70262306a36Sopenharmony_ci int32_t *fds = data; 70362306a36Sopenharmony_ci int ret; 70462306a36Sopenharmony_ci 70562306a36Sopenharmony_ci if (vdev->irq_type == index) 70662306a36Sopenharmony_ci return vfio_msi_set_block(vdev, start, count, 70762306a36Sopenharmony_ci fds, msix); 70862306a36Sopenharmony_ci 70962306a36Sopenharmony_ci ret = vfio_msi_enable(vdev, start + count, msix); 71062306a36Sopenharmony_ci if (ret) 71162306a36Sopenharmony_ci return ret; 71262306a36Sopenharmony_ci 71362306a36Sopenharmony_ci ret = vfio_msi_set_block(vdev, start, count, fds, msix); 71462306a36Sopenharmony_ci if (ret) 71562306a36Sopenharmony_ci vfio_msi_disable(vdev, msix); 71662306a36Sopenharmony_ci 71762306a36Sopenharmony_ci return ret; 71862306a36Sopenharmony_ci } 71962306a36Sopenharmony_ci 72062306a36Sopenharmony_ci if (!irq_is(vdev, index)) 72162306a36Sopenharmony_ci return -EINVAL; 72262306a36Sopenharmony_ci 72362306a36Sopenharmony_ci for (i = start; i < start + count; i++) { 72462306a36Sopenharmony_ci ctx = vfio_irq_ctx_get(vdev, i); 72562306a36Sopenharmony_ci if (!ctx) 72662306a36Sopenharmony_ci continue; 72762306a36Sopenharmony_ci if (flags & VFIO_IRQ_SET_DATA_NONE) { 72862306a36Sopenharmony_ci eventfd_signal(ctx->trigger, 1); 72962306a36Sopenharmony_ci } else if (flags & VFIO_IRQ_SET_DATA_BOOL) { 73062306a36Sopenharmony_ci uint8_t *bools = data; 73162306a36Sopenharmony_ci if (bools[i - start]) 73262306a36Sopenharmony_ci eventfd_signal(ctx->trigger, 1); 73362306a36Sopenharmony_ci } 73462306a36Sopenharmony_ci } 73562306a36Sopenharmony_ci return 0; 73662306a36Sopenharmony_ci} 73762306a36Sopenharmony_ci 73862306a36Sopenharmony_cistatic int vfio_pci_set_ctx_trigger_single(struct eventfd_ctx **ctx, 73962306a36Sopenharmony_ci unsigned int count, uint32_t flags, 74062306a36Sopenharmony_ci void *data) 74162306a36Sopenharmony_ci{ 74262306a36Sopenharmony_ci /* DATA_NONE/DATA_BOOL enables loopback testing */ 74362306a36Sopenharmony_ci if (flags & VFIO_IRQ_SET_DATA_NONE) { 74462306a36Sopenharmony_ci if (*ctx) { 74562306a36Sopenharmony_ci if (count) { 74662306a36Sopenharmony_ci eventfd_signal(*ctx, 1); 74762306a36Sopenharmony_ci } else { 74862306a36Sopenharmony_ci eventfd_ctx_put(*ctx); 74962306a36Sopenharmony_ci *ctx = NULL; 75062306a36Sopenharmony_ci } 75162306a36Sopenharmony_ci return 0; 75262306a36Sopenharmony_ci } 75362306a36Sopenharmony_ci } else if (flags & VFIO_IRQ_SET_DATA_BOOL) { 75462306a36Sopenharmony_ci uint8_t trigger; 75562306a36Sopenharmony_ci 75662306a36Sopenharmony_ci if (!count) 75762306a36Sopenharmony_ci return -EINVAL; 75862306a36Sopenharmony_ci 75962306a36Sopenharmony_ci trigger = *(uint8_t *)data; 76062306a36Sopenharmony_ci if (trigger && *ctx) 76162306a36Sopenharmony_ci eventfd_signal(*ctx, 1); 76262306a36Sopenharmony_ci 76362306a36Sopenharmony_ci return 0; 76462306a36Sopenharmony_ci } else if (flags & VFIO_IRQ_SET_DATA_EVENTFD) { 76562306a36Sopenharmony_ci int32_t fd; 76662306a36Sopenharmony_ci 76762306a36Sopenharmony_ci if (!count) 76862306a36Sopenharmony_ci return -EINVAL; 76962306a36Sopenharmony_ci 77062306a36Sopenharmony_ci fd = *(int32_t *)data; 77162306a36Sopenharmony_ci if (fd == -1) { 77262306a36Sopenharmony_ci if (*ctx) 77362306a36Sopenharmony_ci eventfd_ctx_put(*ctx); 77462306a36Sopenharmony_ci *ctx = NULL; 77562306a36Sopenharmony_ci } else if (fd >= 0) { 77662306a36Sopenharmony_ci struct eventfd_ctx *efdctx; 77762306a36Sopenharmony_ci 77862306a36Sopenharmony_ci efdctx = eventfd_ctx_fdget(fd); 77962306a36Sopenharmony_ci if (IS_ERR(efdctx)) 78062306a36Sopenharmony_ci return PTR_ERR(efdctx); 78162306a36Sopenharmony_ci 78262306a36Sopenharmony_ci if (*ctx) 78362306a36Sopenharmony_ci eventfd_ctx_put(*ctx); 78462306a36Sopenharmony_ci 78562306a36Sopenharmony_ci *ctx = efdctx; 78662306a36Sopenharmony_ci } 78762306a36Sopenharmony_ci return 0; 78862306a36Sopenharmony_ci } 78962306a36Sopenharmony_ci 79062306a36Sopenharmony_ci return -EINVAL; 79162306a36Sopenharmony_ci} 79262306a36Sopenharmony_ci 79362306a36Sopenharmony_cistatic int vfio_pci_set_err_trigger(struct vfio_pci_core_device *vdev, 79462306a36Sopenharmony_ci unsigned index, unsigned start, 79562306a36Sopenharmony_ci unsigned count, uint32_t flags, void *data) 79662306a36Sopenharmony_ci{ 79762306a36Sopenharmony_ci if (index != VFIO_PCI_ERR_IRQ_INDEX || start != 0 || count > 1) 79862306a36Sopenharmony_ci return -EINVAL; 79962306a36Sopenharmony_ci 80062306a36Sopenharmony_ci return vfio_pci_set_ctx_trigger_single(&vdev->err_trigger, 80162306a36Sopenharmony_ci count, flags, data); 80262306a36Sopenharmony_ci} 80362306a36Sopenharmony_ci 80462306a36Sopenharmony_cistatic int vfio_pci_set_req_trigger(struct vfio_pci_core_device *vdev, 80562306a36Sopenharmony_ci unsigned index, unsigned start, 80662306a36Sopenharmony_ci unsigned count, uint32_t flags, void *data) 80762306a36Sopenharmony_ci{ 80862306a36Sopenharmony_ci if (index != VFIO_PCI_REQ_IRQ_INDEX || start != 0 || count > 1) 80962306a36Sopenharmony_ci return -EINVAL; 81062306a36Sopenharmony_ci 81162306a36Sopenharmony_ci return vfio_pci_set_ctx_trigger_single(&vdev->req_trigger, 81262306a36Sopenharmony_ci count, flags, data); 81362306a36Sopenharmony_ci} 81462306a36Sopenharmony_ci 81562306a36Sopenharmony_ciint vfio_pci_set_irqs_ioctl(struct vfio_pci_core_device *vdev, uint32_t flags, 81662306a36Sopenharmony_ci unsigned index, unsigned start, unsigned count, 81762306a36Sopenharmony_ci void *data) 81862306a36Sopenharmony_ci{ 81962306a36Sopenharmony_ci int (*func)(struct vfio_pci_core_device *vdev, unsigned index, 82062306a36Sopenharmony_ci unsigned start, unsigned count, uint32_t flags, 82162306a36Sopenharmony_ci void *data) = NULL; 82262306a36Sopenharmony_ci 82362306a36Sopenharmony_ci switch (index) { 82462306a36Sopenharmony_ci case VFIO_PCI_INTX_IRQ_INDEX: 82562306a36Sopenharmony_ci switch (flags & VFIO_IRQ_SET_ACTION_TYPE_MASK) { 82662306a36Sopenharmony_ci case VFIO_IRQ_SET_ACTION_MASK: 82762306a36Sopenharmony_ci func = vfio_pci_set_intx_mask; 82862306a36Sopenharmony_ci break; 82962306a36Sopenharmony_ci case VFIO_IRQ_SET_ACTION_UNMASK: 83062306a36Sopenharmony_ci func = vfio_pci_set_intx_unmask; 83162306a36Sopenharmony_ci break; 83262306a36Sopenharmony_ci case VFIO_IRQ_SET_ACTION_TRIGGER: 83362306a36Sopenharmony_ci func = vfio_pci_set_intx_trigger; 83462306a36Sopenharmony_ci break; 83562306a36Sopenharmony_ci } 83662306a36Sopenharmony_ci break; 83762306a36Sopenharmony_ci case VFIO_PCI_MSI_IRQ_INDEX: 83862306a36Sopenharmony_ci case VFIO_PCI_MSIX_IRQ_INDEX: 83962306a36Sopenharmony_ci switch (flags & VFIO_IRQ_SET_ACTION_TYPE_MASK) { 84062306a36Sopenharmony_ci case VFIO_IRQ_SET_ACTION_MASK: 84162306a36Sopenharmony_ci case VFIO_IRQ_SET_ACTION_UNMASK: 84262306a36Sopenharmony_ci /* XXX Need masking support exported */ 84362306a36Sopenharmony_ci break; 84462306a36Sopenharmony_ci case VFIO_IRQ_SET_ACTION_TRIGGER: 84562306a36Sopenharmony_ci func = vfio_pci_set_msi_trigger; 84662306a36Sopenharmony_ci break; 84762306a36Sopenharmony_ci } 84862306a36Sopenharmony_ci break; 84962306a36Sopenharmony_ci case VFIO_PCI_ERR_IRQ_INDEX: 85062306a36Sopenharmony_ci switch (flags & VFIO_IRQ_SET_ACTION_TYPE_MASK) { 85162306a36Sopenharmony_ci case VFIO_IRQ_SET_ACTION_TRIGGER: 85262306a36Sopenharmony_ci if (pci_is_pcie(vdev->pdev)) 85362306a36Sopenharmony_ci func = vfio_pci_set_err_trigger; 85462306a36Sopenharmony_ci break; 85562306a36Sopenharmony_ci } 85662306a36Sopenharmony_ci break; 85762306a36Sopenharmony_ci case VFIO_PCI_REQ_IRQ_INDEX: 85862306a36Sopenharmony_ci switch (flags & VFIO_IRQ_SET_ACTION_TYPE_MASK) { 85962306a36Sopenharmony_ci case VFIO_IRQ_SET_ACTION_TRIGGER: 86062306a36Sopenharmony_ci func = vfio_pci_set_req_trigger; 86162306a36Sopenharmony_ci break; 86262306a36Sopenharmony_ci } 86362306a36Sopenharmony_ci break; 86462306a36Sopenharmony_ci } 86562306a36Sopenharmony_ci 86662306a36Sopenharmony_ci if (!func) 86762306a36Sopenharmony_ci return -ENOTTY; 86862306a36Sopenharmony_ci 86962306a36Sopenharmony_ci return func(vdev, index, start, count, flags, data); 87062306a36Sopenharmony_ci} 871