Add support to enable and disable TPH function with mode selection. Restrict unsafe device-specific TPH mode to be allowed only when module parameter enable_unsafe_tph_ds_mode=1 is set. Sync TPH software state after device reset to avoid state mismatch caused by FLR hardware reset. Signed-off-by: Chengwen Feng --- drivers/vfio/pci/vfio_pci_core.c | 47 ++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/drivers/vfio/pci/vfio_pci_core.c b/drivers/vfio/pci/vfio_pci_core.c index 6c498fbef2dc..d1f7f2933e66 100644 --- a/drivers/vfio/pci/vfio_pci_core.c +++ b/drivers/vfio/pci/vfio_pci_core.c @@ -1246,6 +1246,13 @@ static int vfio_pci_ioctl_reset(struct vfio_pci_core_device *vdev, vfio_pci_dma_buf_move(vdev, true); ret = pci_try_reset_function(vdev->pdev); + if (!ret) { + /* + * FLR clears TPH hardware state, but pci/tph.c software state + * remains enabled. Explicitly disable to sync state. + */ + pcie_disable_tph(vdev->pdev); + } if (__vfio_pci_memory_enabled(vdev)) vfio_pci_dma_buf_move(vdev, false); up_write(&vdev->memory_lock); @@ -1488,6 +1495,42 @@ static int vfio_pci_tph_get_cap(struct vfio_pci_core_device *vdev, return 0; } +static int vfio_pci_tph_enable(struct vfio_pci_core_device *vdev, + struct vfio_device_pci_tph_op *op, + void __user *uarg) +{ + struct pci_dev *pdev = vdev->pdev; + struct vfio_pci_tph_ctrl ctrl; + int mode; + + if (op->argsz < offsetofend(struct vfio_device_pci_tph_op, ctrl)) + return -EINVAL; + + if (copy_from_user(&ctrl, uarg, sizeof(ctrl))) + return -EFAULT; + + if (ctrl.mode != VFIO_PCI_TPH_MODE_IV && + ctrl.mode != VFIO_PCI_TPH_MODE_DS) + return -EINVAL; + + if (ctrl.mode == VFIO_PCI_TPH_MODE_DS && !enable_unsafe_tph_ds_mode) + return -EOPNOTSUPP; + + /* Reserved must be zero */ + if (memchr_inv(ctrl.reserved, 0, sizeof(ctrl.reserved))) + return -EINVAL; + + mode = (ctrl.mode == VFIO_PCI_TPH_MODE_IV) ? PCI_TPH_ST_IV_MODE : + PCI_TPH_ST_DS_MODE; + return pcie_enable_tph(pdev, mode); +} + +static int vfio_pci_tph_disable(struct vfio_pci_core_device *vdev) +{ + pcie_disable_tph(vdev->pdev); + return 0; +} + static int vfio_pci_ioctl_tph(struct vfio_pci_core_device *vdev, void __user *uarg) { @@ -1504,6 +1547,10 @@ static int vfio_pci_ioctl_tph(struct vfio_pci_core_device *vdev, switch (op.op) { case VFIO_PCI_TPH_GET_CAP: return vfio_pci_tph_get_cap(vdev, &op, uarg + minsz); + case VFIO_PCI_TPH_ENABLE: + return vfio_pci_tph_enable(vdev, &op, uarg + minsz); + case VFIO_PCI_TPH_DISABLE: + return vfio_pci_tph_disable(vdev); default: /* Other ops are not implemented yet */ return -EINVAL; -- 2.17.1