For zPCI devices we should drive a platform specific function reset as part of VFIO_DEVICE_RESET. This reset is needed recover a zPCI device in error state. Signed-off-by: Farhan Ali --- arch/s390/pci/pci.c | 1 + drivers/vfio/pci/vfio_pci_core.c | 4 ++++ drivers/vfio/pci/vfio_pci_priv.h | 5 ++++ drivers/vfio/pci/vfio_pci_zdev.c | 39 ++++++++++++++++++++++++++++++++ 4 files changed, 49 insertions(+) diff --git a/arch/s390/pci/pci.c b/arch/s390/pci/pci.c index f795e05b5001..860a64993b58 100644 --- a/arch/s390/pci/pci.c +++ b/arch/s390/pci/pci.c @@ -788,6 +788,7 @@ int zpci_hot_reset_device(struct zpci_dev *zdev) return rc; } +EXPORT_SYMBOL_GPL(zpci_hot_reset_device); /** * zpci_create_device() - Create a new zpci_dev and add it to the zbus diff --git a/drivers/vfio/pci/vfio_pci_core.c b/drivers/vfio/pci/vfio_pci_core.c index 7dcf5439dedc..7220a22135a9 100644 --- a/drivers/vfio/pci/vfio_pci_core.c +++ b/drivers/vfio/pci/vfio_pci_core.c @@ -1227,6 +1227,10 @@ static int vfio_pci_ioctl_reset(struct vfio_pci_core_device *vdev, */ vfio_pci_set_power_state(vdev, PCI_D0); + ret = vfio_pci_zdev_reset(vdev); + if (ret && ret != -ENODEV) + return ret; + ret = pci_try_reset_function(vdev->pdev); up_write(&vdev->memory_lock); diff --git a/drivers/vfio/pci/vfio_pci_priv.h b/drivers/vfio/pci/vfio_pci_priv.h index a9972eacb293..5288577b3170 100644 --- a/drivers/vfio/pci/vfio_pci_priv.h +++ b/drivers/vfio/pci/vfio_pci_priv.h @@ -86,6 +86,7 @@ int vfio_pci_info_zdev_add_caps(struct vfio_pci_core_device *vdev, struct vfio_info_cap *caps); int vfio_pci_zdev_open_device(struct vfio_pci_core_device *vdev); void vfio_pci_zdev_close_device(struct vfio_pci_core_device *vdev); +int vfio_pci_zdev_reset(struct vfio_pci_core_device *vdev); #else static inline int vfio_pci_info_zdev_add_caps(struct vfio_pci_core_device *vdev, struct vfio_info_cap *caps) @@ -100,6 +101,10 @@ static inline int vfio_pci_zdev_open_device(struct vfio_pci_core_device *vdev) static inline void vfio_pci_zdev_close_device(struct vfio_pci_core_device *vdev) {} +int vfio_pci_zdev_reset(struct vfio_pci_core_device *vdev) +{ + return -ENODEV; +} #endif static inline bool vfio_pci_is_vga(struct pci_dev *pdev) diff --git a/drivers/vfio/pci/vfio_pci_zdev.c b/drivers/vfio/pci/vfio_pci_zdev.c index 818235b28caa..dd1919ccb3be 100644 --- a/drivers/vfio/pci/vfio_pci_zdev.c +++ b/drivers/vfio/pci/vfio_pci_zdev.c @@ -212,6 +212,45 @@ static int vfio_pci_zdev_setup_err_region(struct vfio_pci_core_device *vdev) return ret; } +int vfio_pci_zdev_reset(struct vfio_pci_core_device *vdev) +{ + struct zpci_dev *zdev = to_zpci(vdev->pdev); + int rc = -EIO; + + if (!zdev) + return -ENODEV; + + /* + * If we can't get the zdev->state_lock the device state is + * currently undergoing a transition and we bail out - just + * the same as if the device's state is not configured at all. + */ + if (!mutex_trylock(&zdev->state_lock)) + return rc; + + /* We can reset only if the function is configured */ + if (zdev->state != ZPCI_FN_STATE_CONFIGURED) + goto out; + + rc = zpci_hot_reset_device(zdev); + if (rc != 0) + goto out; + + if (!vdev->pci_saved_state) { + pci_err(vdev->pdev, "No saved available for the device"); + rc = -EIO; + goto out; + } + + pci_dev_lock(vdev->pdev); + pci_load_saved_state(vdev->pdev, vdev->pci_saved_state); + pci_restore_state(vdev->pdev); + pci_dev_unlock(vdev->pdev); +out: + mutex_unlock(&zdev->state_lock); + return rc; +} + int vfio_pci_zdev_open_device(struct vfio_pci_core_device *vdev) { struct zpci_dev *zdev = to_zpci(vdev->pdev); -- 2.43.0