Add VFIO_PCI_TPH_SET_ST operation to support batch programming of steering tag entries. If any entry fails, all previously programmed entries are rolled back to 0 to avoid partial device state. Signed-off-by: Chengwen Feng --- drivers/vfio/pci/vfio_pci_core.c | 71 ++++++++++++++++++++++++++++++++ 1 file changed, 71 insertions(+) diff --git a/drivers/vfio/pci/vfio_pci_core.c b/drivers/vfio/pci/vfio_pci_core.c index a036e7d795be..35b8849ecf39 100644 --- a/drivers/vfio/pci/vfio_pci_core.c +++ b/drivers/vfio/pci/vfio_pci_core.c @@ -1573,6 +1573,75 @@ static int vfio_pci_tph_get_st(struct vfio_pci_core_device *vdev, return err; } +static int vfio_pci_tph_set_st(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_entry *ents; + struct vfio_pci_tph_st st; + enum tph_mem_type mtype; + int i = 0, j, err, size; + u32 tab_loc; + u16 st_val; + + tab_loc = pcie_tph_get_st_table_loc(pdev); + if (tab_loc != PCI_TPH_LOC_CAP && tab_loc != PCI_TPH_LOC_MSIX) + return -EOPNOTSUPP; + + if (copy_from_user(&st, uarg, sizeof(st))) + return -EFAULT; + + if (!st.count || st.count > VFIO_PCI_TPH_MAX_ENTRIES) + return -EINVAL; + + size = st.count * sizeof(*ents); + ents = kvmalloc(size, GFP_KERNEL); + if (!ents) + return -ENOMEM; + + if (copy_from_user(ents, uarg + sizeof(st), size)) { + err = -EFAULT; + goto out; + } + + for (; i < st.count; i++) { + if (ents[i].cpu == U32_MAX) { + err = pcie_tph_set_st_entry(pdev, ents[i].index, 0); + if (err) + goto out; + continue; + } + + if (ents[i].mem_type == VFIO_PCI_TPH_MEM_TYPE_VM) { + mtype = TPH_MEM_TYPE_VM; + } else if (ents[i].mem_type == VFIO_PCI_TPH_MEM_TYPE_PM) { + mtype = TPH_MEM_TYPE_PM; + } else { + err = -EINVAL; + goto out; + } + + err = pcie_tph_get_cpu_st(pdev, mtype, ents[i].cpu, &st_val); + if (err) + goto out; + err = pcie_tph_set_st_entry(pdev, ents[i].index, st_val); + if (err) + goto out; + } + +out: + if (err) { + /* Roll back previously programmed entries to 0 */ + for (j = 0; j < i; j++) { + if (ents[j].cpu != U32_MAX) + pcie_tph_set_st_entry(pdev, ents[j].index, 0); + } + } + kvfree(ents); + return err; +} + static int vfio_pci_ioctl_tph(struct vfio_pci_core_device *vdev, void __user *uarg) { @@ -1595,6 +1664,8 @@ static int vfio_pci_ioctl_tph(struct vfio_pci_core_device *vdev, return vfio_pci_tph_disable(vdev); case VFIO_PCI_TPH_GET_ST: return vfio_pci_tph_get_st(vdev, &op, uarg + minsz); + case VFIO_PCI_TPH_SET_ST: + return vfio_pci_tph_set_st(vdev, &op, uarg + minsz); default: /* Other ops are not implemented yet */ return -EINVAL; -- 2.17.1