@@ -69,6 +75,26 @@ vfio_pci_dma_buf_map(struct dma_buf_attachment *attachment,
return ret;
}
+static int vfio_pci_dma_buf_get_tph(struct dma_buf *dmabuf, bool extended,
+ u16 *steering_tag, u8 *ph)
+{
+ struct vfio_pci_dma_buf *priv = dmabuf->priv;
+
+ dma_resv_assert_held(dmabuf->resv);
+
+ if (extended) {
+ if (!priv->tph_st_ext_valid)
+ return -EOPNOTSUPP;
+ *steering_tag = priv->tph_st_ext;
+ } else {
+ if (!priv->tph_st_valid)
+ return -EOPNOTSUPP;
+ *steering_tag = priv->tph_st;
+ }
+ *ph = priv->tph_ph;
+ return 0;
+}
+
static void vfio_pci_dma_buf_unmap(struct dma_buf_attachment *attachment,
struct sg_table *sgt,
enum dma_data_direction dir)@@ -101,6 +127,7 @@ static void vfio_pci_dma_buf_release(struct dma_buf *dmabuf)
static const struct dma_buf_ops vfio_pci_dmabuf_ops = {
.attach = vfio_pci_dma_buf_attach,
+ .get_tph = vfio_pci_dma_buf_get_tph,
.map_dma_buf = vfio_pci_dma_buf_map,
.unmap_dma_buf = vfio_pci_dma_buf_unmap,
.release = vfio_pci_dma_buf_release,@@ -333,6 +360,71 @@ int vfio_pci_core_feature_dma_buf(struct vfio_pci_core_device *vdev, u32 flags,
return ret;
}
+int vfio_pci_core_feature_dma_buf_tph(struct vfio_pci_core_device *vdev,
+ u32 flags,
+ struct vfio_device_feature_dma_buf_tph __user *arg,
+ size_t argsz)
+{
+ struct vfio_device_feature_dma_buf_tph set_tph;
+ struct vfio_pci_dma_buf *priv;
+ struct dma_buf *dmabuf;
+ u8 comp;
+ int ret;
+
+ ret = vfio_check_feature(flags, argsz, VFIO_DEVICE_FEATURE_SET,
+ sizeof(set_tph));
+ if (ret != 1)
+ return ret;
+
+ if (copy_from_user(&set_tph, arg, sizeof(set_tph)))
+ return -EFAULT;
+
+ if (set_tph.flags & ~(VFIO_DMA_BUF_TPH_ST | VFIO_DMA_BUF_TPH_ST_EXT))
+ return -EINVAL;
+
+ if (set_tph.ph & ~0x3)
+ return -EINVAL;
+
+ comp = pcie_tph_completer_type(vdev->pdev);
+ if (comp == PCI_EXP_DEVCAP2_TPH_COMP_NONE)
+ return -EOPNOTSUPP;
+ if ((set_tph.flags & VFIO_DMA_BUF_TPH_ST_EXT) &&
+ comp != PCI_EXP_DEVCAP2_TPH_COMP_EXT_TPH)
+ return -EOPNOTSUPP;
+
+ dmabuf = dma_buf_get(set_tph.dmabuf_fd);
+ if (IS_ERR(dmabuf))
+ return PTR_ERR(dmabuf);
+
+ if (dmabuf->ops != &vfio_pci_dmabuf_ops) {
+ ret = -EINVAL;
+ goto out_put;
+ }
+
+ priv = dmabuf->priv;
+ if (priv->vdev != vdev) {
+ ret = -EINVAL;
+ goto out_put;
+ }
+
+ ret = dma_resv_lock_interruptible(dmabuf->resv, NULL);
+ if (ret)
+ goto out_put;
+
+ priv->tph_st = set_tph.steering_tag;
+ priv->tph_st_ext = set_tph.steering_tag_ext;
+ priv->tph_ph = set_tph.ph;
+ priv->tph_st_valid = !!(set_tph.flags & VFIO_DMA_BUF_TPH_ST);
+ priv->tph_st_ext_valid =
+ !!(set_tph.flags & VFIO_DMA_BUF_TPH_ST_EXT);
+ dma_resv_unlock(dmabuf->resv);
+ ret = 0;
+
+out_put:
+ dma_buf_put(dmabuf);
+ return ret;
+}
+
void vfio_pci_dma_buf_move(struct vfio_pci_core_device *vdev, bool revoked)
{
struct vfio_pci_dma_buf *priv;diff --git a/drivers/vfio/pci/vfio_pci_priv.h b/drivers/vfio/pci/vfio_pci_priv.h
index fca9d0dfac90..c58f369be4b3 100644
--- a/drivers/vfio/pci/vfio_pci_priv.h
+++ b/drivers/vfio/pci/vfio_pci_priv.h
@@ -118,6 +118,10 @@ static inline bool vfio_pci_is_vga(struct pci_dev *pdev)
int vfio_pci_core_feature_dma_buf(struct vfio_pci_core_device *vdev, u32 flags,
struct vfio_device_feature_dma_buf __user *arg,
size_t argsz);
+int vfio_pci_core_feature_dma_buf_tph(struct vfio_pci_core_device *vdev,
+ u32 flags,
+ struct vfio_device_feature_dma_buf_tph __user *arg,
+ size_t argsz);
void vfio_pci_dma_buf_cleanup(struct vfio_pci_core_device *vdev);
void vfio_pci_dma_buf_move(struct vfio_pci_core_device *vdev, bool revoked);
#else
@@ -128,6 +132,14 @@ vfio_pci_core_feature_dma_buf(struct vfio_pci_core_device *vdev, u32 flags,
{
return -ENOTTY;
}
+
+static inline int
+vfio_pci_core_feature_dma_buf_tph(struct vfio_pci_core_device *vdev, u32 flags,
+ struct vfio_device_feature_dma_buf_tph __user *arg,
+ size_t argsz)
+{
+ return -ENOTTY;
+}
static inline void vfio_pci_dma_buf_cleanup(struct vfio_pci_core_device *vdev)
{
}diff --git a/include/uapi/linux/vfio.h b/include/uapi/linux/vfio.h
index 5de618a3a5ee..5dd693220a0d 100644
--- a/include/uapi/linux/vfio.h
+++ b/include/uapi/linux/vfio.h
@@ -1534,6 +1534,43 @@ struct vfio_device_feature_dma_buf {
*/
#define VFIO_DEVICE_FEATURE_MIG_PRECOPY_INFOv2 12
+/**
+ * Upon VFIO_DEVICE_FEATURE_SET associate TPH (TLP Processing Hints) metadata
+ * with a vfio-exported dma-buf. The dma-buf must have been created by
+ * VFIO_DEVICE_FEATURE_DMA_BUF on this device, and the device must report
+ * TPH Completer support in Device Capabilities 2 (bits 13:12); requests
+ * carrying VFIO_DMA_BUF_TPH_ST_EXT additionally require the device to
+ * report the Extended TPH Completer encoding. Otherwise the ioctl
+ * returns -EOPNOTSUPP.
+ *
+ * dmabuf_fd is the file descriptor returned by VFIO_DEVICE_FEATURE_DMA_BUF.
+ *
+ * 8-bit ST (steering_tag) and 16-bit Extended ST (steering_tag_ext) are
+ * distinct namespaces. Userspace supplies whichever values are valid and sets
+ * the matching VFIO_DMA_BUF_TPH_ST / VFIO_DMA_BUF_TPH_ST_EXT bits in @flags;
+ * an importer requests one namespace and receives the matching value.
+ *
+ * @flags == 0 marks any previously published ST / Extended-ST as invalid
+ * for future get_tph() requests on this dma-buf.
+ *
+ * ph is the 2-bit TLP Processing Hint and must be in the range [0, 3].
+ *
+ * Userspace must publish TPH before handing the dma-buf fd to an importer.
+ * Calling SET again replaces the published values.
+ */
+#define VFIO_DEVICE_FEATURE_DMA_BUF_TPH 13
+
+#define VFIO_DMA_BUF_TPH_ST (1 << 0)
+#define VFIO_DMA_BUF_TPH_ST_EXT (1 << 1)
+
+struct vfio_device_feature_dma_buf_tph {
+ __s32 dmabuf_fd;
+ __u32 flags;
+ __u16 steering_tag_ext;
+ __u8 steering_tag;
+ __u8 ph;
+};
+
/* -------- API for Type1 VFIO IOMMU -------- */
/**