diff --git a/arch/s390/include/asm/pci.h b/arch/s390/include/asm/pci.h index 016386f7ef4a..88a125b92bdd 100644 --- a/arch/s390/include/asm/pci.h +++ b/arch/s390/include/asm/pci.h @@ -364,6 +364,8 @@ int zpci_clear_error_state(struct zpci_dev *zdev); int zpci_reset_load_store_blocked(struct zpci_dev *zdev); void zpci_start_mediated_recovery(struct zpci_dev *zdev); void zpci_stop_mediated_recovery(struct zpci_dev *zdev); +int zpci_get_pending_error(struct zpci_dev *zdev, + struct zpci_ccdf_err *ccdf); #ifdef CONFIG_NUMA diff --git a/arch/s390/pci/pci_event.c b/arch/s390/pci/pci_event.c index 8934b640a095..5829ad398800 100644 --- a/arch/s390/pci/pci_event.c +++ b/arch/s390/pci/pci_event.c @@ -77,6 +77,25 @@ static int zpci_store_pci_error(struct pci_dev *pdev, return 0; } +int zpci_get_pending_error(struct zpci_dev *zdev, + struct zpci_ccdf_err *ccdf) +{ + int head; + + guard(mutex)(&zdev->pending_errs_lock); + + if (!zdev->pending_errs.count) + return -ENOMSG; + + head = zdev->pending_errs.head % ZPCI_ERR_PENDING_MAX; + memcpy(ccdf, &zdev->pending_errs.err[head], + sizeof(struct zpci_ccdf_err)); + zdev->pending_errs.head++; + zdev->pending_errs.count--; + return 0; +} +EXPORT_SYMBOL_GPL(zpci_get_pending_error); + void zpci_start_mediated_recovery(struct zpci_dev *zdev) { guard(mutex)(&zdev->pending_errs_lock); diff --git a/drivers/vfio/pci/vfio_pci_core.c b/drivers/vfio/pci/vfio_pci_core.c index 6a184588ff23..d9dbddf25585 100644 --- a/drivers/vfio/pci/vfio_pci_core.c +++ b/drivers/vfio/pci/vfio_pci_core.c @@ -1628,6 +1628,8 @@ int vfio_pci_core_ioctl_feature(struct vfio_device *device, u32 flags, return vfio_pci_core_feature_token(vdev, flags, arg, argsz); case VFIO_DEVICE_FEATURE_DMA_BUF: return vfio_pci_core_feature_dma_buf(vdev, flags, arg, argsz); + case VFIO_DEVICE_FEATURE_ZPCI_ERROR: + return vfio_pci_zdev_feature_err(device, flags, arg, argsz); default: return -ENOTTY; } diff --git a/drivers/vfio/pci/vfio_pci_priv.h b/drivers/vfio/pci/vfio_pci_priv.h index fca9d0dfac90..4e7162234a2e 100644 --- a/drivers/vfio/pci/vfio_pci_priv.h +++ b/drivers/vfio/pci/vfio_pci_priv.h @@ -93,6 +93,8 @@ 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_feature_err(struct vfio_device *device, u32 flags, + void __user *arg, size_t argsz); #else static inline int vfio_pci_info_zdev_add_caps(struct vfio_pci_core_device *vdev, struct vfio_info_cap *caps) @@ -107,6 +109,13 @@ 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) {} + +static inline int vfio_pci_zdev_feature_err(struct vfio_device *device, + u32 flags, void __user *arg, + size_t argsz) +{ + return -ENOTTY; +} #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 78a28db00c6d..f47f36314a1c 100644 --- a/drivers/vfio/pci/vfio_pci_zdev.c +++ b/drivers/vfio/pci/vfio_pci_zdev.c @@ -24,7 +24,7 @@ static int zpci_base_cap(struct zpci_dev *zdev, struct vfio_info_cap *caps) { struct vfio_device_info_cap_zpci_base cap = { .header.id = VFIO_DEVICE_INFO_CAP_ZPCI_BASE, - .header.version = 2, + .header.version = 3, .start_dma = zdev->start_dma, .end_dma = zdev->end_dma, .pchid = zdev->pchid, @@ -32,7 +32,8 @@ static int zpci_base_cap(struct zpci_dev *zdev, struct vfio_info_cap *caps) .fmb_length = zdev->fmb_length, .pft = zdev->pft, .gid = zdev->pfgid, - .fh = zdev->fh + .fh = zdev->fh, + .ccdf_err_length = sizeof(struct zpci_ccdf_err) }; return vfio_info_add_capability(caps, &cap.header, sizeof(cap)); @@ -141,6 +142,45 @@ int vfio_pci_info_zdev_add_caps(struct vfio_pci_core_device *vdev, return ret; } +int vfio_pci_zdev_feature_err(struct vfio_device *device, u32 flags, + void __user *arg, size_t argsz) +{ + struct vfio_device_feature_zpci_err err; + struct vfio_pci_core_device *vdev; + struct zpci_ccdf_err ccdf = {}; + struct zpci_dev *zdev; + int ret; + + vdev = container_of(device, struct vfio_pci_core_device, vdev); + zdev = to_zpci(vdev->pdev); + if (!zdev) + return -ENODEV; + + ret = vfio_check_feature(flags, argsz, VFIO_DEVICE_FEATURE_GET, + sizeof(err)); + if (ret != 1) + return ret; + + if (copy_from_user(&err, arg, sizeof(err))) + return -EFAULT; + + if (!err.data) + return -EINVAL; + + ret = zpci_get_pending_error(zdev, &ccdf); + if (ret) + return ret; + + if (copy_to_user(u64_to_user_ptr(err.data), &ccdf, sizeof(ccdf))) { + dev_warn_ratelimited(device->dev, + "Failed to handle PCI error event for PCI function 0x%x", + zdev->fid); + return -EFAULT; + } + + return 0; +} + int vfio_pci_zdev_open_device(struct vfio_pci_core_device *vdev) { struct zpci_dev *zdev = to_zpci(vdev->pdev); diff --git a/include/uapi/linux/vfio.h b/include/uapi/linux/vfio.h index 5de618a3a5ee..e41437fa17ad 100644 --- a/include/uapi/linux/vfio.h +++ b/include/uapi/linux/vfio.h @@ -1534,6 +1534,27 @@ struct vfio_device_feature_dma_buf { */ #define VFIO_DEVICE_FEATURE_MIG_PRECOPY_INFOv2 12 +/** + * VFIO_DEVICE_FEATURE_ZPCI_ERROR feature provides PCI error information to + * userspace for vfio-pci devices on s390. On s390, PCI error recovery + * involves platform firmware and notification to operating systems is done + * by architecture specific mechanism. Exposing this information to + * userspace allows it to take appropriate actions to handle an + * error on the device. + * + * Userspace provides an opaque buffer of fixed length, and the kernel + * fills it with the zpci_ccdf_err data structure. The length of + * zpci_ccdf_err is provided to userspace via the + * VFIO_DEVICE_INFO_CAP_ZPCI_BASE capability. + * + * The ioctl returns -ENOMSG if there are no pending PCI errors. + */ +struct vfio_device_feature_zpci_err { + __aligned_u64 data; +}; + +#define VFIO_DEVICE_FEATURE_ZPCI_ERROR 13 + /* -------- API for Type1 VFIO IOMMU -------- */ /** diff --git a/include/uapi/linux/vfio_zdev.h b/include/uapi/linux/vfio_zdev.h index 77f2aff1f27e..d6c1175c884e 100644 --- a/include/uapi/linux/vfio_zdev.h +++ b/include/uapi/linux/vfio_zdev.h @@ -32,6 +32,8 @@ struct vfio_device_info_cap_zpci_base { /* End of version 1 */ __u32 fh; /* PCI function handle */ /* End of version 2 */ + __u32 ccdf_err_length; /* PCI CCDF length */ + /* End of version 3 */ }; /**