Add setup for IMS enabling for the mediated device. On the actual hardware the MSIX vector 0 is misc interrupt and handles events such as administrative command completion, error reporting, performance monitor overflow, and etc. The MSIX vectors 1...N are used for descriptor completion interrupts. On the guest kernel, the MSIX interrupts are backed by the mediated device through emulation or IMS vectors. Vector 0 is handled through emulation by the host vdcm. The vector 1 (and more may be supported later) is backed by IMS. IMS can be setup with interrupt handlers via request_irq() just like MSIX interrupts once the relevant IRQ domain is set. The msi_domain_alloc_irqs()/msi_domain_free_irqs() APIs can then be used to allocate interrupts from the above set domain. Signed-off-by: Dave Jiang <dave.jiang@xxxxxxxxx> --- drivers/dma/idxd/idxd.h | 1 + drivers/vfio/mdev/idxd/mdev.c | 12 +++++++++ drivers/vfio/mdev/idxd/vdev.c | 53 ++++++++++++++++++++++++++++++++--------- kernel/irq/msi.c | 2 ++ 4 files changed, 57 insertions(+), 11 deletions(-) diff --git a/drivers/dma/idxd/idxd.h b/drivers/dma/idxd/idxd.h index 41eee987c9b7..c5ef6ccc9ba6 100644 --- a/drivers/dma/idxd/idxd.h +++ b/drivers/dma/idxd/idxd.h @@ -224,6 +224,7 @@ struct idxd_device { struct workqueue_struct *wq; struct work_struct work; + struct irq_domain *ims_domain; int *int_handles; struct auxiliary_device *mdev_auxdev; diff --git a/drivers/vfio/mdev/idxd/mdev.c b/drivers/vfio/mdev/idxd/mdev.c index 7cde707021db..8a4af882a47f 100644 --- a/drivers/vfio/mdev/idxd/mdev.c +++ b/drivers/vfio/mdev/idxd/mdev.c @@ -1167,6 +1167,7 @@ static int alloc_supported_types(struct idxd_device *idxd) int idxd_mdev_host_init(struct idxd_device *idxd) { struct device *dev = &idxd->pdev->dev; + struct ims_array_info ims_info; int rc; if (!test_bit(IDXD_FLAG_IMS_SUPPORTED, &idxd->flags)) @@ -1188,6 +1189,15 @@ int idxd_mdev_host_init(struct idxd_device *idxd) return -EOPNOTSUPP; } + ims_info.max_slots = idxd->ims_size; + ims_info.slots = idxd->reg_base + idxd->ims_offset; + idxd->ims_domain = pci_ims_array_create_msi_irq_domain(idxd->pdev, &ims_info); + if (!idxd->ims_domain) { + dev_warn(dev, "Fail to acquire IMS domain\n"); + iommu_dev_disable_feature(dev, IOMMU_DEV_FEAT_AUX); + return -ENODEV; + } + return mdev_register_device(dev, &idxd_vdcm_ops); } @@ -1196,6 +1206,8 @@ void idxd_mdev_host_release(struct idxd_device *idxd) struct device *dev = &idxd->pdev->dev; int rc; + irq_domain_remove(idxd->ims_domain); + mdev_unregister_device(dev); if (iommu_dev_has_feature(dev, IOMMU_DEV_FEAT_AUX)) { rc = iommu_dev_disable_feature(dev, IOMMU_DEV_FEAT_AUX); diff --git a/drivers/vfio/mdev/idxd/vdev.c b/drivers/vfio/mdev/idxd/vdev.c index 766fd98e9eea..8626438a9e54 100644 --- a/drivers/vfio/mdev/idxd/vdev.c +++ b/drivers/vfio/mdev/idxd/vdev.c @@ -16,6 +16,7 @@ #include <linux/intel-svm.h> #include <linux/kvm_host.h> #include <linux/eventfd.h> +#include <linux/irqchip/irq-ims-msi.h> #include <uapi/linux/idxd.h> #include "registers.h" #include "idxd.h" @@ -871,6 +872,47 @@ static void vidxd_wq_disable(struct vdcm_idxd *vidxd, int wq_id_mask) idxd_complete_command(vidxd, IDXD_CMDSTS_SUCCESS); } +void vidxd_free_ims_entries(struct vdcm_idxd *vidxd) +{ + struct mdev_device *mdev = vidxd->vdev.mdev; + struct device *dev = mdev_dev(mdev); + + msi_domain_free_irqs(dev_get_msi_domain(dev), dev); +} + +int vidxd_setup_ims_entries(struct vdcm_idxd *vidxd) +{ + struct irq_domain *irq_domain; + struct idxd_device *idxd = vidxd->idxd; + struct mdev_device *mdev = vidxd->vdev.mdev; + struct device *dev = mdev_dev(mdev); + struct msi_desc *entry; + struct ims_irq_entry *irq_entry; + int rc, i; + + irq_domain = idxd->ims_domain; + dev_set_msi_domain(dev, irq_domain); + + /* We are allocate MAX_MSIX - 1 is because vector 0 is emulated and not IMS backed. */ + rc = msi_domain_alloc_irqs(irq_domain, dev, VIDXD_MAX_MSIX_VECS - 1); + if (rc < 0) + return rc; + /* + * The first MSIX vector on the guest is emulated and not backed by IMS. To make matters + * simple the ims entries include the emulated vector. Here the code starts at index + * 1 to setup all the IMS backed vectors. + */ + i = 1; + for_each_msi_entry(entry, dev) { + irq_entry = &vidxd->irq_entries[i]; + irq_entry->ims_idx = entry->device_msi.hwirq; + irq_entry->irq = entry->irq; + i++; + } + + return 0; +} + static bool command_supported(struct vdcm_idxd *vidxd, u32 cmd) { struct idxd_device *idxd = vidxd->idxd; @@ -938,14 +980,3 @@ void vidxd_do_command(struct vdcm_idxd *vidxd, u32 val) break; } } - -int vidxd_setup_ims_entries(struct vdcm_idxd *vidxd) -{ - /* PLACEHOLDER */ - return 0; -} - -void vidxd_free_ims_entries(struct vdcm_idxd *vidxd) -{ - /* PLACEHOLDER */ -} diff --git a/kernel/irq/msi.c b/kernel/irq/msi.c index d70d92eac322..d95299b4ae79 100644 --- a/kernel/irq/msi.c +++ b/kernel/irq/msi.c @@ -536,6 +536,7 @@ int msi_domain_alloc_irqs(struct irq_domain *domain, struct device *dev, return ops->domain_alloc_irqs(domain, dev, nvec); } +EXPORT_SYMBOL_GPL(msi_domain_alloc_irqs); void __msi_domain_free_irqs(struct irq_domain *domain, struct device *dev) { @@ -572,6 +573,7 @@ void msi_domain_free_irqs(struct irq_domain *domain, struct device *dev) return ops->domain_free_irqs(domain, dev); } +EXPORT_SYMBOL_GPL(msi_domain_free_irqs); /** * msi_get_domain_info - Get the MSI interrupt domain info for @domain