Commit 30aec6e1 authored by Linus Torvalds's avatar Linus Torvalds
Browse files

Merge tag 'vfio-v6.10-rc1' of https://github.com/awilliam/linux-vfio

Pull vfio updates from Alex Williamson:

 - The vfio fsl-mc bus driver has become orphaned. We'll consider
   removing it in future releases if a new maintainer isn't found (Alex
   Williamson)

 - Improved usage of opaque data in vfio-pci INTx handling, avoiding
   lookups of the eventfd through the interrupt and irqfd runtime paths
   (Alex Williamson)

 - Resolve an error path memory leak introduced in vfio-pci interrupt
   code (Ye Bin)

 - Addition of interrupt support for vfio devices exposed on the CDX
   bus, including a new MSI allocation helper and export of existing
   helpers for MSI alloc and free (Nipun Gupta)

 - A new vfio-pci variant driver supporting migration of Intel QAT VF
   devices for the GEN4 PFs (Xin Zeng & Yahui Cao)

 - Resolve a possibly circular locking dependency in vfio-pci by
   avoiding copy_to_user() from a PCI bus walk callback (Alex
   Williamson)

 - Trivial docs update to remove a duplicate semicolon (Foryun Ma)

* tag 'vfio-v6.10-rc1' of https://github.com/awilliam/linux-vfio:
  vfio/pci: Restore zero affected bus reset devices warning
  vfio: remove an extra semicolon
  vfio/pci: Collect hot-reset devices to local buffer
  vfio/qat: Add vfio_pci driver for Intel QAT SR-IOV VF devices
  vfio/cdx: add interrupt support
  genirq/msi: Add MSI allocation helper and export MSI functions
  vfio/pci: fix potential memory leak in vfio_intx_enable()
  vfio/pci: Pass eventfd context object through irqfd
  vfio/pci: Pass eventfd context to IRQ handler
  MAINTAINERS: Orphan vfio fsl-mc bus driver
parents 70ec81c2 cbb325e7
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -364,7 +364,7 @@ IOMMUFD IOAS/HWPT to enable userspace DMA::
				    MAP_PRIVATE | MAP_ANONYMOUS, 0, 0);
	map.iova = 0; /* 1MB starting at 0x0 from device view */
	map.length = 1024 * 1024;
	map.ioas_id = alloc_data.out_ioas_id;;
	map.ioas_id = alloc_data.out_ioas_id;

	ioctl(iommufd, IOMMU_IOAS_MAP, &map);

+9 −2
Original line number Diff line number Diff line
@@ -23512,9 +23512,8 @@ F: include/linux/vfio_pci_core.h
F:	include/uapi/linux/vfio.h
VFIO FSL-MC DRIVER
M:	Diana Craciun <diana.craciun@oss.nxp.com>
L:	kvm@vger.kernel.org
S:	Maintained
S:	Orphan
F:	drivers/vfio/fsl-mc/
VFIO HISILICON PCI DRIVER
@@ -23568,6 +23567,14 @@ L: kvm@vger.kernel.org
S:	Maintained
F:	drivers/vfio/platform/
VFIO QAT PCI DRIVER
M:	Xin Zeng <xin.zeng@intel.com>
M:	Giovanni Cabiddu <giovanni.cabiddu@intel.com>
L:	kvm@vger.kernel.org
L:	qat-linux@intel.com
S:	Supported
F:	drivers/vfio/pci/qat/
VFIO VIRTIO PCI DRIVER
M:	Yishai Hadas <yishaih@nvidia.com>
L:	kvm@vger.kernel.org
+1 −1
Original line number Diff line number Diff line
@@ -5,4 +5,4 @@

obj-$(CONFIG_VFIO_CDX) += vfio-cdx.o

vfio-cdx-objs := main.o
vfio-cdx-objs := main.o intr.o
+217 −0
Original line number Diff line number Diff line
// SPDX-License-Identifier: GPL-2.0
/*
 * Copyright (C) 2022-2023, Advanced Micro Devices, Inc.
 */

#include <linux/vfio.h>
#include <linux/slab.h>
#include <linux/types.h>
#include <linux/eventfd.h>
#include <linux/msi.h>
#include <linux/interrupt.h>

#include "linux/cdx/cdx_bus.h"
#include "private.h"

static irqreturn_t vfio_cdx_msihandler(int irq_no, void *arg)
{
	struct eventfd_ctx *trigger = arg;

	eventfd_signal(trigger);
	return IRQ_HANDLED;
}

static int vfio_cdx_msi_enable(struct vfio_cdx_device *vdev, int nvec)
{
	struct cdx_device *cdx_dev = to_cdx_device(vdev->vdev.dev);
	struct device *dev = vdev->vdev.dev;
	int msi_idx, ret;

	vdev->cdx_irqs = kcalloc(nvec, sizeof(struct vfio_cdx_irq), GFP_KERNEL);
	if (!vdev->cdx_irqs)
		return -ENOMEM;

	ret = cdx_enable_msi(cdx_dev);
	if (ret) {
		kfree(vdev->cdx_irqs);
		return ret;
	}

	/* Allocate cdx MSIs */
	ret = msi_domain_alloc_irqs(dev, MSI_DEFAULT_DOMAIN, nvec);
	if (ret) {
		cdx_disable_msi(cdx_dev);
		kfree(vdev->cdx_irqs);
		return ret;
	}

	for (msi_idx = 0; msi_idx < nvec; msi_idx++)
		vdev->cdx_irqs[msi_idx].irq_no = msi_get_virq(dev, msi_idx);

	vdev->msi_count = nvec;
	vdev->config_msi = 1;

	return 0;
}

static int vfio_cdx_msi_set_vector_signal(struct vfio_cdx_device *vdev,
					  int vector, int fd)
{
	struct eventfd_ctx *trigger;
	int irq_no, ret;

	if (vector < 0 || vector >= vdev->msi_count)
		return -EINVAL;

	irq_no = vdev->cdx_irqs[vector].irq_no;

	if (vdev->cdx_irqs[vector].trigger) {
		free_irq(irq_no, vdev->cdx_irqs[vector].trigger);
		kfree(vdev->cdx_irqs[vector].name);
		eventfd_ctx_put(vdev->cdx_irqs[vector].trigger);
		vdev->cdx_irqs[vector].trigger = NULL;
	}

	if (fd < 0)
		return 0;

	vdev->cdx_irqs[vector].name = kasprintf(GFP_KERNEL, "vfio-msi[%d](%s)",
						vector, dev_name(vdev->vdev.dev));
	if (!vdev->cdx_irqs[vector].name)
		return -ENOMEM;

	trigger = eventfd_ctx_fdget(fd);
	if (IS_ERR(trigger)) {
		kfree(vdev->cdx_irqs[vector].name);
		return PTR_ERR(trigger);
	}

	ret = request_irq(irq_no, vfio_cdx_msihandler, 0,
			  vdev->cdx_irqs[vector].name, trigger);
	if (ret) {
		kfree(vdev->cdx_irqs[vector].name);
		eventfd_ctx_put(trigger);
		return ret;
	}

	vdev->cdx_irqs[vector].trigger = trigger;

	return 0;
}

static int vfio_cdx_msi_set_block(struct vfio_cdx_device *vdev,
				  unsigned int start, unsigned int count,
				  int32_t *fds)
{
	int i, j, ret = 0;

	if (start >= vdev->msi_count || start + count > vdev->msi_count)
		return -EINVAL;

	for (i = 0, j = start; i < count && !ret; i++, j++) {
		int fd = fds ? fds[i] : -1;

		ret = vfio_cdx_msi_set_vector_signal(vdev, j, fd);
	}

	if (ret) {
		for (--j; j >= (int)start; j--)
			vfio_cdx_msi_set_vector_signal(vdev, j, -1);
	}

	return ret;
}

static void vfio_cdx_msi_disable(struct vfio_cdx_device *vdev)
{
	struct cdx_device *cdx_dev = to_cdx_device(vdev->vdev.dev);
	struct device *dev = vdev->vdev.dev;

	vfio_cdx_msi_set_block(vdev, 0, vdev->msi_count, NULL);

	if (!vdev->config_msi)
		return;

	msi_domain_free_irqs_all(dev, MSI_DEFAULT_DOMAIN);
	cdx_disable_msi(cdx_dev);
	kfree(vdev->cdx_irqs);

	vdev->cdx_irqs = NULL;
	vdev->msi_count = 0;
	vdev->config_msi = 0;
}

static int vfio_cdx_set_msi_trigger(struct vfio_cdx_device *vdev,
				    unsigned int index, unsigned int start,
				    unsigned int count, u32 flags,
				    void *data)
{
	struct cdx_device *cdx_dev = to_cdx_device(vdev->vdev.dev);
	int i;

	if (start + count > cdx_dev->num_msi)
		return -EINVAL;

	if (!count && (flags & VFIO_IRQ_SET_DATA_NONE)) {
		vfio_cdx_msi_disable(vdev);
		return 0;
	}

	if (flags & VFIO_IRQ_SET_DATA_EVENTFD) {
		s32 *fds = data;
		int ret;

		if (vdev->config_msi)
			return vfio_cdx_msi_set_block(vdev, start, count,
						  fds);
		ret = vfio_cdx_msi_enable(vdev, cdx_dev->num_msi);
		if (ret)
			return ret;

		ret = vfio_cdx_msi_set_block(vdev, start, count, fds);
		if (ret)
			vfio_cdx_msi_disable(vdev);

		return ret;
	}

	for (i = start; i < start + count; i++) {
		if (!vdev->cdx_irqs[i].trigger)
			continue;
		if (flags & VFIO_IRQ_SET_DATA_NONE) {
			eventfd_signal(vdev->cdx_irqs[i].trigger);
		} else if (flags & VFIO_IRQ_SET_DATA_BOOL) {
			u8 *bools = data;

			if (bools[i - start])
				eventfd_signal(vdev->cdx_irqs[i].trigger);
		}
	}

	return 0;
}

int vfio_cdx_set_irqs_ioctl(struct vfio_cdx_device *vdev,
			    u32 flags, unsigned int index,
			    unsigned int start, unsigned int count,
			    void *data)
{
	if (flags & VFIO_IRQ_SET_ACTION_TRIGGER)
		return vfio_cdx_set_msi_trigger(vdev, index, start,
			  count, flags, data);
	else
		return -EINVAL;
}

/* Free All IRQs for the given device */
void vfio_cdx_irqs_cleanup(struct vfio_cdx_device *vdev)
{
	/*
	 * Device does not support any interrupt or the interrupts
	 * were not configured
	 */
	if (!vdev->cdx_irqs)
		return;

	vfio_cdx_set_msi_trigger(vdev, 0, 0, 0, VFIO_IRQ_SET_DATA_NONE, NULL);
}
+62 −1
Original line number Diff line number Diff line
@@ -61,6 +61,7 @@ static void vfio_cdx_close_device(struct vfio_device *core_vdev)

	kfree(vdev->regions);
	cdx_dev_reset(core_vdev->dev);
	vfio_cdx_irqs_cleanup(vdev);
}

static int vfio_cdx_bm_ctrl(struct vfio_device *core_vdev, u32 flags,
@@ -123,7 +124,7 @@ static int vfio_cdx_ioctl_get_info(struct vfio_cdx_device *vdev,
	info.flags |= VFIO_DEVICE_FLAGS_RESET;

	info.num_regions = cdx_dev->res_count;
	info.num_irqs = 0;
	info.num_irqs = cdx_dev->num_msi ? 1 : 0;

	return copy_to_user(arg, &info, minsz) ? -EFAULT : 0;
}
@@ -152,6 +153,62 @@ static int vfio_cdx_ioctl_get_region_info(struct vfio_cdx_device *vdev,
	return copy_to_user(arg, &info, minsz) ? -EFAULT : 0;
}

static int vfio_cdx_ioctl_get_irq_info(struct vfio_cdx_device *vdev,
				       struct vfio_irq_info __user *arg)
{
	unsigned long minsz = offsetofend(struct vfio_irq_info, count);
	struct cdx_device *cdx_dev = to_cdx_device(vdev->vdev.dev);
	struct vfio_irq_info info;

	if (copy_from_user(&info, arg, minsz))
		return -EFAULT;

	if (info.argsz < minsz)
		return -EINVAL;

	if (info.index >= 1)
		return -EINVAL;

	if (!cdx_dev->num_msi)
		return -EINVAL;

	info.flags = VFIO_IRQ_INFO_EVENTFD | VFIO_IRQ_INFO_NORESIZE;
	info.count = cdx_dev->num_msi;

	return copy_to_user(arg, &info, minsz) ? -EFAULT : 0;
}

static int vfio_cdx_ioctl_set_irqs(struct vfio_cdx_device *vdev,
				   struct vfio_irq_set __user *arg)
{
	unsigned long minsz = offsetofend(struct vfio_irq_set, count);
	struct cdx_device *cdx_dev = to_cdx_device(vdev->vdev.dev);
	struct vfio_irq_set hdr;
	size_t data_size = 0;
	u8 *data = NULL;
	int ret = 0;

	if (copy_from_user(&hdr, arg, minsz))
		return -EFAULT;

	ret = vfio_set_irqs_validate_and_prepare(&hdr, cdx_dev->num_msi,
						 1, &data_size);
	if (ret)
		return ret;

	if (data_size) {
		data = memdup_user(arg->data, data_size);
		if (IS_ERR(data))
			return PTR_ERR(data);
	}

	ret = vfio_cdx_set_irqs_ioctl(vdev, hdr.flags, hdr.index,
				      hdr.start, hdr.count, data);
	kfree(data);

	return ret;
}

static long vfio_cdx_ioctl(struct vfio_device *core_vdev,
			   unsigned int cmd, unsigned long arg)
{
@@ -164,6 +221,10 @@ static long vfio_cdx_ioctl(struct vfio_device *core_vdev,
		return vfio_cdx_ioctl_get_info(vdev, uarg);
	case VFIO_DEVICE_GET_REGION_INFO:
		return vfio_cdx_ioctl_get_region_info(vdev, uarg);
	case VFIO_DEVICE_GET_IRQ_INFO:
		return vfio_cdx_ioctl_get_irq_info(vdev, uarg);
	case VFIO_DEVICE_SET_IRQS:
		return vfio_cdx_ioctl_set_irqs(vdev, uarg);
	case VFIO_DEVICE_RESET:
		return cdx_dev_reset(core_vdev->dev);
	default:
Loading