Commit 71791b92 authored by Yi Liu's avatar Yi Liu Committed by Alex Williamson
Browse files

vfio/pci: Allow passing zero-length fd array in VFIO_DEVICE_PCI_HOT_RESET



This is the way user to invoke hot-reset for the devices opened by cdev
interface. User should check the flag VFIO_PCI_HOT_RESET_FLAG_DEV_ID_OWNED
in the output of VFIO_DEVICE_GET_PCI_HOT_RESET_INFO ioctl before doing
hot-reset for cdev devices.

Suggested-by: default avatarJason Gunthorpe <jgg@nvidia.com>
Reviewed-by: default avatarJason Gunthorpe <jgg@nvidia.com>
Tested-by: default avatarYanting Jiang <yanting.jiang@intel.com>
Tested-by: default avatarZhenzhong Duan <zhenzhong.duan@intel.com>
Signed-off-by: default avatarJason Gunthorpe <jgg@nvidia.com>
Signed-off-by: default avatarYi Liu <yi.l.liu@intel.com>
Link: https://lore.kernel.org/r/20230718105542.4138-11-yi.l.liu@intel.com


Signed-off-by: default avatarAlex Williamson <alex.williamson@redhat.com>
parent b56b7aab
Loading
Loading
Loading
Loading
+50 −11
Original line number Diff line number Diff line
@@ -181,7 +181,8 @@ static void vfio_pci_probe_mmaps(struct vfio_pci_core_device *vdev)
struct vfio_pci_group_info;
static void vfio_pci_dev_set_try_reset(struct vfio_device_set *dev_set);
static int vfio_pci_dev_set_hot_reset(struct vfio_device_set *dev_set,
				      struct vfio_pci_group_info *groups);
				      struct vfio_pci_group_info *groups,
				      struct iommufd_ctx *iommufd_ctx);

/*
 * INTx masking requires the ability to disable INTx signaling via PCI_COMMAND
@@ -1329,8 +1330,7 @@ vfio_pci_ioctl_pci_hot_reset_groups(struct vfio_pci_core_device *vdev,
	if (ret)
		return ret;

	/* Somewhere between 1 and count is OK */
	if (!array_count || array_count > count)
	if (array_count > count)
		return -EINVAL;

	group_fds = kcalloc(array_count, sizeof(*group_fds), GFP_KERNEL);
@@ -1379,7 +1379,7 @@ vfio_pci_ioctl_pci_hot_reset_groups(struct vfio_pci_core_device *vdev,
	info.count = array_count;
	info.files = files;

	ret = vfio_pci_dev_set_hot_reset(vdev->vdev.dev_set, &info);
	ret = vfio_pci_dev_set_hot_reset(vdev->vdev.dev_set, &info, NULL);

hot_reset_release:
	for (file_idx--; file_idx >= 0; file_idx--)
@@ -1402,13 +1402,21 @@ static int vfio_pci_ioctl_pci_hot_reset(struct vfio_pci_core_device *vdev,
	if (hdr.argsz < minsz || hdr.flags)
		return -EINVAL;

	/* zero-length array is only for cdev opened devices */
	if (!!hdr.count == vfio_device_cdev_opened(&vdev->vdev))
		return -EINVAL;

	/* Can we do a slot or bus reset or neither? */
	if (!pci_probe_reset_slot(vdev->pdev->slot))
		slot = true;
	else if (pci_probe_reset_bus(vdev->pdev->bus))
		return -ENODEV;

	if (hdr.count)
		return vfio_pci_ioctl_pci_hot_reset_groups(vdev, hdr.count, slot, arg);

	return vfio_pci_dev_set_hot_reset(vdev->vdev.dev_set, NULL,
					  vfio_iommufd_device_ictx(&vdev->vdev));
}

static int vfio_pci_ioctl_ioeventfd(struct vfio_pci_core_device *vdev,
@@ -2376,13 +2384,16 @@ const struct pci_error_handlers vfio_pci_core_err_handlers = {
};
EXPORT_SYMBOL_GPL(vfio_pci_core_err_handlers);

static bool vfio_dev_in_groups(struct vfio_pci_core_device *vdev,
static bool vfio_dev_in_groups(struct vfio_device *vdev,
			       struct vfio_pci_group_info *groups)
{
	unsigned int i;

	if (!groups)
		return false;

	for (i = 0; i < groups->count; i++)
		if (vfio_file_has_dev(groups->files[i], &vdev->vdev))
		if (vfio_file_has_dev(groups->files[i], vdev))
			return true;
	return false;
}
@@ -2458,7 +2469,8 @@ static int vfio_pci_dev_set_pm_runtime_get(struct vfio_device_set *dev_set)
 * get each memory_lock.
 */
static int vfio_pci_dev_set_hot_reset(struct vfio_device_set *dev_set,
				      struct vfio_pci_group_info *groups)
				      struct vfio_pci_group_info *groups,
				      struct iommufd_ctx *iommufd_ctx)
{
	struct vfio_pci_core_device *cur_mem;
	struct vfio_pci_core_device *cur_vma;
@@ -2488,11 +2500,38 @@ static int vfio_pci_dev_set_hot_reset(struct vfio_device_set *dev_set,
		goto err_unlock;

	list_for_each_entry(cur_vma, &dev_set->device_list, vdev.dev_set_list) {
		bool owned;

		/*
		 * Test whether all the affected devices are contained by the
		 * set of groups provided by the user.
		 * Test whether all the affected devices can be reset by the
		 * user.
		 *
		 * If called from a group opened device and the user provides
		 * a set of groups, all the devices in the dev_set should be
		 * contained by the set of groups provided by the user.
		 *
		 * If called from a cdev opened device and the user provides
		 * a zero-length array, all the devices in the dev_set must
		 * be bound to the same iommufd_ctx as the input iommufd_ctx.
		 * If there is any device that has not been bound to any
		 * iommufd_ctx yet, check if its iommu_group has any device
		 * bound to the input iommufd_ctx.  Such devices can be
		 * considered owned by the input iommufd_ctx as the device
		 * cannot be owned by another iommufd_ctx when its iommu_group
		 * is owned.
		 *
		 * Otherwise, reset is not allowed.
		 */
		if (!vfio_dev_in_groups(cur_vma, groups)) {
		if (iommufd_ctx) {
			int devid = vfio_iommufd_get_dev_id(&cur_vma->vdev,
							    iommufd_ctx);

			owned = (devid > 0 || devid == -ENOENT);
		} else {
			owned = vfio_dev_in_groups(&cur_vma->vdev, groups);
		}

		if (!owned) {
			ret = -EINVAL;
			goto err_undo;
		}
+21 −0
Original line number Diff line number Diff line
@@ -717,6 +717,9 @@ enum {
 *	  affected devices are represented in the dev_set and also owned by
 *	  the user.  This flag is available only when
 *	  flag VFIO_PCI_HOT_RESET_FLAG_DEV_ID is set, otherwise reserved.
 *	  When set, user could invoke VFIO_DEVICE_PCI_HOT_RESET with a zero
 *	  length fd array on the calling device as the ownership is validated
 *	  by iommufd_ctx.
 *
 * Return: 0 on success, -errno on failure:
 *	-enospc = insufficient buffer, -enodev = unsupported for device.
@@ -748,6 +751,24 @@ struct vfio_pci_hot_reset_info {
 * VFIO_DEVICE_PCI_HOT_RESET - _IOW(VFIO_TYPE, VFIO_BASE + 13,
 *				    struct vfio_pci_hot_reset)
 *
 * A PCI hot reset results in either a bus or slot reset which may affect
 * other devices sharing the bus/slot.  The calling user must have
 * ownership of the full set of affected devices as determined by the
 * VFIO_DEVICE_GET_PCI_HOT_RESET_INFO ioctl.
 *
 * When called on a device file descriptor acquired through the vfio
 * group interface, the user is required to provide proof of ownership
 * of those affected devices via the group_fds array in struct
 * vfio_pci_hot_reset.
 *
 * When called on a direct cdev opened vfio device, the flags field of
 * struct vfio_pci_hot_reset_info reports the ownership status of the
 * affected devices and this ioctl must be called with an empty group_fds
 * array.  See above INFO ioctl definition for ownership requirements.
 *
 * Mixed usage of legacy groups and cdevs across the set of affected
 * devices is not supported.
 *
 * Return: 0 on success, -errno on failure.
 */
struct vfio_pci_hot_reset {