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

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.comSigned-off-by: default avatarAlex Williamson <alex.williamson@redhat.com>
parent b56b7aab
...@@ -181,7 +181,8 @@ static void vfio_pci_probe_mmaps(struct vfio_pci_core_device *vdev) ...@@ -181,7 +181,8 @@ static void vfio_pci_probe_mmaps(struct vfio_pci_core_device *vdev)
struct vfio_pci_group_info; struct vfio_pci_group_info;
static void vfio_pci_dev_set_try_reset(struct vfio_device_set *dev_set); 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, 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 * 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, ...@@ -1329,8 +1330,7 @@ vfio_pci_ioctl_pci_hot_reset_groups(struct vfio_pci_core_device *vdev,
if (ret) if (ret)
return ret; return ret;
/* Somewhere between 1 and count is OK */ if (array_count > count)
if (!array_count || array_count > count)
return -EINVAL; return -EINVAL;
group_fds = kcalloc(array_count, sizeof(*group_fds), GFP_KERNEL); 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, ...@@ -1379,7 +1379,7 @@ vfio_pci_ioctl_pci_hot_reset_groups(struct vfio_pci_core_device *vdev,
info.count = array_count; info.count = array_count;
info.files = files; 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: hot_reset_release:
for (file_idx--; file_idx >= 0; file_idx--) 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, ...@@ -1402,13 +1402,21 @@ static int vfio_pci_ioctl_pci_hot_reset(struct vfio_pci_core_device *vdev,
if (hdr.argsz < minsz || hdr.flags) if (hdr.argsz < minsz || hdr.flags)
return -EINVAL; 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? */ /* Can we do a slot or bus reset or neither? */
if (!pci_probe_reset_slot(vdev->pdev->slot)) if (!pci_probe_reset_slot(vdev->pdev->slot))
slot = true; slot = true;
else if (pci_probe_reset_bus(vdev->pdev->bus)) else if (pci_probe_reset_bus(vdev->pdev->bus))
return -ENODEV; return -ENODEV;
return vfio_pci_ioctl_pci_hot_reset_groups(vdev, hdr.count, slot, arg); 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, 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 = { ...@@ -2376,13 +2384,16 @@ const struct pci_error_handlers vfio_pci_core_err_handlers = {
}; };
EXPORT_SYMBOL_GPL(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) struct vfio_pci_group_info *groups)
{ {
unsigned int i; unsigned int i;
if (!groups)
return false;
for (i = 0; i < groups->count; i++) 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 true;
return false; return false;
} }
...@@ -2458,7 +2469,8 @@ static int vfio_pci_dev_set_pm_runtime_get(struct vfio_device_set *dev_set) ...@@ -2458,7 +2469,8 @@ static int vfio_pci_dev_set_pm_runtime_get(struct vfio_device_set *dev_set)
* get each memory_lock. * get each memory_lock.
*/ */
static int vfio_pci_dev_set_hot_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)
{ {
struct vfio_pci_core_device *cur_mem; struct vfio_pci_core_device *cur_mem;
struct vfio_pci_core_device *cur_vma; 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, ...@@ -2488,11 +2500,38 @@ static int vfio_pci_dev_set_hot_reset(struct vfio_device_set *dev_set,
goto err_unlock; goto err_unlock;
list_for_each_entry(cur_vma, &dev_set->device_list, vdev.dev_set_list) { 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 * Test whether all the affected devices can be reset by the
* set of groups provided by the user. * 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; ret = -EINVAL;
goto err_undo; goto err_undo;
} }
......
...@@ -717,6 +717,9 @@ enum { ...@@ -717,6 +717,9 @@ enum {
* affected devices are represented in the dev_set and also owned by * affected devices are represented in the dev_set and also owned by
* the user. This flag is available only when * the user. This flag is available only when
* flag VFIO_PCI_HOT_RESET_FLAG_DEV_ID is set, otherwise reserved. * 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: * Return: 0 on success, -errno on failure:
* -enospc = insufficient buffer, -enodev = unsupported for device. * -enospc = insufficient buffer, -enodev = unsupported for device.
...@@ -748,6 +751,24 @@ struct vfio_pci_hot_reset_info { ...@@ -748,6 +751,24 @@ struct vfio_pci_hot_reset_info {
* VFIO_DEVICE_PCI_HOT_RESET - _IOW(VFIO_TYPE, VFIO_BASE + 13, * VFIO_DEVICE_PCI_HOT_RESET - _IOW(VFIO_TYPE, VFIO_BASE + 13,
* struct vfio_pci_hot_reset) * 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. * Return: 0 on success, -errno on failure.
*/ */
struct vfio_pci_hot_reset { struct vfio_pci_hot_reset {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment