AI Trajectory


Trajectory: (Expand/Collapse All)
Seq Timestamp Type Name Duration
0/0 2026/07/14 16:05 flow patch-triage
1m Args:
null

Results:
{
  "EnableConfigs": [
    "PCIE_TPH",
    "VFIO_PCI",
    "PCI_P2PDMA",
    "DMA_SHARED_BUFFER"
  ],
  "FocusSymbols": [
    "vfio_pci_core_feature_tph",
    "vfio_pci_core_feature_tph_resolve",
    "vfio_pci_core_feature_tph_st",
    "vfio_pci_core_feature_dma_buf_tph",
    "vfio_pci_dma_buf_get_pci_tph",
    "dma_buf_get_pci_tph",
    "pcie_tph_get_cpu_st_ext",
    "pcie_tph_set_st_entries",
    "pcie_enable_tph_ext",
    "vfio_tph_config_write"
  ],
  "Reasoning": "The patch introduces functional changes to VFIO PCI and DMA-BUF to support PCIe TLP Processing Hints (TPH). It adds new VFIO ioctls/features for TPH configuration and resolution, which are reachable from userspace and should be fuzzed.",
  "WorthFuzzing": true
}

1/1 2026/07/14 16:05 action read-patch-diff
0m Args:
null

Results:
{
  "PatchDiff": "commit db00c624771fc0764f7771f705c1590f7f644c79\nAuthor: syz-cluster \u003ctriage@syzkaller.com\u003e\nDate:   Tue Jul 14 16:05:43 2026 +0000\n\n    syz-cluster: applied patch under review\n\ndiff --git a/drivers/dma-buf/dma-buf.c b/drivers/dma-buf/dma-buf.c\nindex d504c636dc294e..e8314ed240d336 100644\n--- a/drivers/dma-buf/dma-buf.c\n+++ b/drivers/dma-buf/dma-buf.c\n@@ -939,6 +939,7 @@ dma_buf_pin_on_map(struct dma_buf_attachment *attach)\n  *     - dma_buf_unmap_attachment()\n  *     - dma_buf_vmap()\n  *     - dma_buf_vunmap()\n+ *     - dma_buf_get_pci_tph()\n  *\n  * 2. Importers must not hold the dma-buf reservation lock when calling these\n  *    functions:\n@@ -979,6 +980,7 @@ dma_buf_pin_on_map(struct dma_buf_attachment *attach)\n  *     - \u0026dma_buf_ops.unmap_dma_buf()\n  *     - \u0026dma_buf_ops.vmap()\n  *     - \u0026dma_buf_ops.vunmap()\n+ *     - \u0026dma_buf_ops.get_pci_tph()\n  *\n  * 3. Exporters must hold the dma-buf reservation lock when calling these\n  *    functions:\n@@ -1144,6 +1146,36 @@ void dma_buf_unpin(struct dma_buf_attachment *attach)\n }\n EXPORT_SYMBOL_NS_GPL(dma_buf_unpin, \"DMA_BUF\");\n \n+/**\n+ * dma_buf_get_pci_tph - Retrieve PCIe TLP Processing Hint (TPH) metadata\n+ * @dmabuf: DMA buffer to query\n+ * @extended: false for 8-bit ST, true for 16-bit Extended ST\n+ * @steering_tag: returns the raw steering tag for the requested namespace\n+ * @ph: returns the TPH processing hint\n+ *\n+ * Wrapper for the optional \u0026dma_buf_ops.get_pci_tph callback.\n+ *\n+ * Must be called with \u0026dma_buf.resv held. Returns -EOPNOTSUPP if the\n+ * exporter does not implement the callback or has no metadata for the\n+ * requested namespace.\n+ *\n+ * The returned steering tag and processing hint are only valid until the\n+ * exporter invalidates the current mapping (signalled to the importer via\n+ * \u0026dma_buf_attach_ops.move_notify). Importers must re-query after a new\n+ * mapping is established following invalidation.\n+ */\n+int dma_buf_get_pci_tph(struct dma_buf *dmabuf, bool extended,\n+\t\t\tu16 *steering_tag, u8 *ph)\n+{\n+\tdma_resv_assert_held(dmabuf-\u003eresv);\n+\n+\tif (!dmabuf-\u003eops-\u003eget_pci_tph)\n+\t\treturn -EOPNOTSUPP;\n+\n+\treturn dmabuf-\u003eops-\u003eget_pci_tph(dmabuf, extended, steering_tag, ph);\n+}\n+EXPORT_SYMBOL_NS_GPL(dma_buf_get_pci_tph, \"DMA_BUF\");\n+\n /**\n  * dma_buf_map_attachment - Returns the scatterlist table of the attachment;\n  * mapped into _device_ address space. Is a wrapper for map_dma_buf() of the\ndiff --git a/drivers/pci/tph.c b/drivers/pci/tph.c\nindex 655ffd60e62f6a..e6a850ab2b037e 100644\n--- a/drivers/pci/tph.c\n+++ b/drivers/pci/tph.c\n@@ -165,14 +165,62 @@ static u8 get_st_modes(struct pci_dev *pdev)\n  */\n u32 pcie_tph_get_st_table_loc(struct pci_dev *pdev)\n {\n-\tu32 reg;\n+\tu32 reg = 0;\n+\n+\tif (!pdev-\u003etph_cap)\n+\t\treturn PCI_TPH_LOC_NONE;\n \n \tpci_read_config_dword(pdev, pdev-\u003etph_cap + PCI_TPH_CAP, \u0026reg);\n \n-\treturn FIELD_GET(PCI_TPH_CAP_LOC_MASK, reg);\n+\treturn reg \u0026 PCI_TPH_CAP_LOC_MASK;\n }\n EXPORT_SYMBOL(pcie_tph_get_st_table_loc);\n \n+/**\n+ * pcie_tph_enabled_req_type - Return the device's enabled TPH requester type\n+ * @pdev: PCI device to query\n+ *\n+ * Return: PCI_TPH_REQ_DISABLE, PCI_TPH_REQ_TPH_ONLY or PCI_TPH_REQ_EXT_TPH.\n+ */\n+u8 pcie_tph_enabled_req_type(struct pci_dev *pdev)\n+{\n+\treturn pdev-\u003etph_req_type;\n+}\n+EXPORT_SYMBOL(pcie_tph_enabled_req_type);\n+\n+/**\n+ * pcie_tph_completer_type - Return the device's TPH Completer support\n+ * @pdev: PCI device to query\n+ *\n+ * Reads the \"TPH Completer Supported\" field (bits 13:12) of Device\n+ * Capabilities 2. The reserved 0b10 encoding is folded into\n+ * \"not supported\" so callers only need to compare against the three\n+ * defined values.\n+ *\n+ * Return: one of %PCI_EXP_DEVCAP2_TPH_COMP_NONE,\n+ *         %PCI_EXP_DEVCAP2_TPH_COMP_TPH_ONLY or\n+ *         %PCI_EXP_DEVCAP2_TPH_COMP_EXT_TPH.\n+ */\n+u8 pcie_tph_completer_type(struct pci_dev *pdev)\n+{\n+\tu32 reg;\n+\n+\tif (pcie_capability_read_dword(pdev, PCI_EXP_DEVCAP2, \u0026reg))\n+\t\treturn PCI_EXP_DEVCAP2_TPH_COMP_NONE;\n+\tif (PCI_POSSIBLE_ERROR(reg))\n+\t\treturn PCI_EXP_DEVCAP2_TPH_COMP_NONE;\n+\n+\tswitch (FIELD_GET(PCI_EXP_DEVCAP2_TPH_COMP_MASK, reg)) {\n+\tcase PCI_EXP_DEVCAP2_TPH_COMP_TPH_ONLY:\n+\t\treturn PCI_EXP_DEVCAP2_TPH_COMP_TPH_ONLY;\n+\tcase PCI_EXP_DEVCAP2_TPH_COMP_EXT_TPH:\n+\t\treturn PCI_EXP_DEVCAP2_TPH_COMP_EXT_TPH;\n+\tdefault:\n+\t\treturn PCI_EXP_DEVCAP2_TPH_COMP_NONE;\n+\t}\n+}\n+EXPORT_SYMBOL(pcie_tph_completer_type);\n+\n /*\n  * Return the size of ST table. If ST table is not in TPH Requester Extended\n  * Capability space, return 0. Otherwise return the ST Table Size + 1.\n@@ -184,9 +232,6 @@ u16 pcie_tph_get_st_table_size(struct pci_dev *pdev)\n \n \t/* Check ST table location first */\n \tloc = pcie_tph_get_st_table_loc(pdev);\n-\n-\t/* Convert loc to match with PCI_TPH_LOC_* defined in pci_regs.h */\n-\tloc = FIELD_PREP(PCI_TPH_CAP_LOC_MASK, loc);\n \tif (loc != PCI_TPH_LOC_CAP)\n \t\treturn 0;\n \n@@ -231,20 +276,22 @@ static int write_tag_to_st_table(struct pci_dev *pdev, int index, u16 tag)\n }\n \n /**\n- * pcie_tph_get_cpu_st() - Retrieve Steering Tag for a target memory associated\n- * with a specific CPU\n+ * pcie_tph_get_cpu_st_ext() - Retrieve Steering Tag for a target memory\n+ * associated with a specific CPU, with explicit requester type selection\n  * @pdev: PCI device\n  * @mem_type: target memory type (volatile or persistent RAM)\n+ * @req_type: TPH requester type to select ST namespace\n  * @cpu: associated CPU id\n  * @tag: Steering Tag to be returned\n  *\n- * Return the Steering Tag for a target memory that is associated with a\n- * specific CPU as indicated by cpu.\n+ * Return the Steering Tag for a target memory associated with a specific CPU,\n+ * using the specified TPH requester type to select the correct ST namespace.\n+ * Validates requester type against hardware negotiated capability limits.\n  *\n  * Return: 0 if success, otherwise negative value (-errno)\n  */\n-int pcie_tph_get_cpu_st(struct pci_dev *pdev, enum tph_mem_type mem_type,\n-\t\t\tunsigned int cpu, u16 *tag)\n+int pcie_tph_get_cpu_st_ext(struct pci_dev *pdev, enum tph_mem_type mem_type,\n+\t\t\t    u8 req_type, unsigned int cpu, u16 *tag)\n {\n #ifdef CONFIG_ACPI\n \tstruct pci_dev *rp;\n@@ -253,6 +300,10 @@ int pcie_tph_get_cpu_st(struct pci_dev *pdev, enum tph_mem_type mem_type,\n \tu32 cpu_uid;\n \tint ret;\n \n+\tif (req_type == PCI_TPH_REQ_DISABLE || req_type == 2 ||\n+\t\treq_type \u003e pdev-\u003etph_max_type)\n+\t\treturn -EINVAL;\n+\n \tret = acpi_get_cpu_uid(cpu, \u0026cpu_uid);\n \tif (ret != 0)\n \t\treturn ret;\n@@ -268,35 +319,54 @@ int pcie_tph_get_cpu_st(struct pci_dev *pdev, enum tph_mem_type mem_type,\n \t\treturn -EINVAL;\n \t}\n \n-\t*tag = tph_extract_tag(mem_type, pdev-\u003etph_req_type, \u0026info);\n+\t*tag = tph_extract_tag(mem_type, req_type, \u0026info);\n \n-\tpci_dbg(pdev, \"get steering tag: mem_type=%s, cpu=%d, tag=%#04x\\n\",\n+\tpci_dbg(pdev, \"get steering tag: mem_type=%s, req_type=%u, cpu=%d, tag=%#04x\\n\",\n \t\t(mem_type == TPH_MEM_TYPE_VM) ? \"volatile\" : \"persistent\",\n-\t\tcpu, *tag);\n+\t\treq_type, cpu, *tag);\n \n \treturn 0;\n #else\n \treturn -ENODEV;\n #endif\n }\n-EXPORT_SYMBOL(pcie_tph_get_cpu_st);\n+EXPORT_SYMBOL(pcie_tph_get_cpu_st_ext);\n \n /**\n- * pcie_tph_set_st_entry() - Set Steering Tag in the ST table entry\n+ * pcie_tph_get_cpu_st() - Retrieve Steering Tag for a target memory associated\n+ * with a specific CPU\n  * @pdev: PCI device\n- * @index: ST table entry index\n- * @tag: Steering Tag to be written\n+ * @mem_type: target memory type (volatile or persistent RAM)\n+ * @cpu: associated CPU id\n+ * @tag: Steering Tag to be returned\n  *\n- * Figure out the proper location of ST table, either in the MSI-X table or\n- * in the TPH Extended Capability space, and write the Steering Tag into\n- * the ST entry pointed by index.\n+ * Return the Steering Tag for a target memory associated with a specific CPU,\n+ * using the default negotiated TPH requester type.\n  *\n  * Return: 0 if success, otherwise negative value (-errno)\n  */\n-int pcie_tph_set_st_entry(struct pci_dev *pdev, unsigned int index, u16 tag)\n+int pcie_tph_get_cpu_st(struct pci_dev *pdev, enum tph_mem_type mem_type,\n+\t\t\tunsigned int cpu, u16 *tag)\n+{\n+\treturn pcie_tph_get_cpu_st_ext(pdev, mem_type, pdev-\u003etph_max_type,\n+\t\t\t\t       cpu, tag);\n+}\n+EXPORT_SYMBOL(pcie_tph_get_cpu_st);\n+\n+/**\n+ * pcie_tph_set_st_entries - Batch set Steering Tag entries in the ST table\n+ * @pdev: PCI device\n+ * @start: starting ST table index\n+ * @count: number of contiguous entries to write\n+ * @tags: array of u16 tag values to program\n+ *\n+ * Return: number of successfully written entries, or negative errno\n+ */\n+int pcie_tph_set_st_entries(struct pci_dev *pdev, unsigned int start,\n+\t\t\t    unsigned int count, const u16 *tags)\n {\n-\tu32 loc;\n \tint err = 0;\n+\tu32 loc, i;\n \n \tif (!pdev-\u003etph_cap)\n \t\treturn -EINVAL;\n@@ -306,7 +376,7 @@ int pcie_tph_set_st_entry(struct pci_dev *pdev, unsigned int index, u16 tag)\n \n \t/* No need to write tag if device is in \"No ST Mode\" */\n \tif (pdev-\u003etph_mode == PCI_TPH_ST_NS_MODE)\n-\t\treturn 0;\n+\t\treturn count;\n \n \t/*\n \t * Disable TPH before updating ST to avoid potential instability as\n@@ -315,31 +385,47 @@ int pcie_tph_set_st_entry(struct pci_dev *pdev, unsigned int index, u16 tag)\n \tset_ctrl_reg_req_en(pdev, PCI_TPH_REQ_DISABLE);\n \n \tloc = pcie_tph_get_st_table_loc(pdev);\n-\t/* Convert loc to match with PCI_TPH_LOC_* */\n-\tloc = FIELD_PREP(PCI_TPH_CAP_LOC_MASK, loc);\n-\n-\tswitch (loc) {\n-\tcase PCI_TPH_LOC_MSIX:\n-\t\terr = pci_msix_write_tph_tag(pdev, index, tag);\n-\t\tbreak;\n-\tcase PCI_TPH_LOC_CAP:\n-\t\terr = write_tag_to_st_table(pdev, index, tag);\n-\t\tbreak;\n-\tdefault:\n-\t\terr = -EINVAL;\n-\t}\n-\n-\tif (err) {\n-\t\tpcie_disable_tph(pdev);\n-\t\treturn err;\n+\tfor (i = 0; i \u003c count; i++) {\n+\t\tswitch (loc) {\n+\t\tcase PCI_TPH_LOC_MSIX:\n+\t\t\terr = pci_msix_write_tph_tag(pdev, start + i, tags[i]);\n+\t\t\tbreak;\n+\t\tcase PCI_TPH_LOC_CAP:\n+\t\t\terr = write_tag_to_st_table(pdev, start + i, tags[i]);\n+\t\t\tbreak;\n+\t\tdefault:\n+\t\t\terr = -EINVAL;\n+\t\t}\n+\t\tif (err != 0) {\n+\t\t\tpci_err(pdev, \"failed to set steering tag: %s table, index=%u, tags=%u, ret=%d\\n\",\n+\t\t\t\t(loc == PCI_TPH_LOC_MSIX) ? \"MSI-X\" : \"ST\",\n+\t\t\t\tstart + i, tags[i], err);\n+\t\t\tpcie_disable_tph(pdev);\n+\t\t\treturn i \u003e 0 ? i : err;\n+\t\t}\n \t}\n \n \tset_ctrl_reg_req_en(pdev, pdev-\u003etph_req_type);\n \n-\tpci_dbg(pdev, \"set steering tag: %s table, index=%d, tag=%#04x\\n\",\n-\t\t(loc == PCI_TPH_LOC_MSIX) ? \"MSI-X\" : \"ST\", index, tag);\n+\tpci_dbg(pdev, \"batch set steering tag: %s table, start=%u, count=%u success!\\n\",\n+\t\t(loc == PCI_TPH_LOC_MSIX) ? \"MSI-X\" : \"ST\", start, count);\n \n-\treturn 0;\n+\treturn count;\n+}\n+EXPORT_SYMBOL(pcie_tph_set_st_entries);\n+\n+/**\n+ * pcie_tph_set_st_entry() - Set Steering Tag in the ST table entry\n+ * @pdev: PCI device\n+ * @index: ST table entry index\n+ * @tag: Steering Tag to be written\n+ *\n+ * Return: 0 if success, otherwise negative value (-errno)\n+ */\n+int pcie_tph_set_st_entry(struct pci_dev *pdev, unsigned int index, u16 tag)\n+{\n+\tint ret = pcie_tph_set_st_entries(pdev, index, 1, \u0026tag);\n+\treturn ret == 1 ? 0 : ret;\n }\n EXPORT_SYMBOL(pcie_tph_set_st_entry);\n \n@@ -366,26 +452,21 @@ void pcie_disable_tph(struct pci_dev *pdev)\n EXPORT_SYMBOL(pcie_disable_tph);\n \n /**\n- * pcie_enable_tph - Enable TPH support for device using a specific ST mode\n+ * pcie_enable_tph_ext - Enable TPH support with specific ST mode and\n+ * requester type\n  * @pdev: PCI device\n- * @mode: ST mode to enable. Current supported modes include:\n- *\n- *   - PCI_TPH_ST_NS_MODE: NO ST Mode\n- *   - PCI_TPH_ST_IV_MODE: Interrupt Vector Mode\n- *   - PCI_TPH_ST_DS_MODE: Device Specific Mode\n+ * @mode: ST mode to enable: PCI_TPH_ST_(NS_MODE / IV_MODE / DS_MODE)\n+ * @req_type: Requester type to enable: PCI_TPH_REQ_(TPH_ONLY / EXT_TPH)\n  *\n- * Check whether the mode is actually supported by the device before enabling\n- * and return an error if not. Additionally determine what types of requests,\n- * TPH or extended TPH, can be issued by the device based on its TPH requester\n- * capability and the Root Port's completer capability.\n+ * Check whether the mode and requester type is actually supported by the\n+ * device before enabling and return an error if not.\n  *\n  * Return: 0 on success, otherwise negative value (-errno)\n  */\n-int pcie_enable_tph(struct pci_dev *pdev, int mode)\n+int pcie_enable_tph_ext(struct pci_dev *pdev, u8 mode, u8 req_type)\n {\n \tu32 reg;\n \tu8 dev_modes;\n-\tu8 rp_req_type;\n \n \t/* Honor \"notph\" kernel parameter */\n \tif (pci_tph_disabled)\n@@ -397,6 +478,14 @@ int pcie_enable_tph(struct pci_dev *pdev, int mode)\n \tif (pdev-\u003etph_enabled)\n \t\treturn -EBUSY;\n \n+\tif (pdev-\u003etph_max_type == PCI_TPH_REQ_DISABLE)\n+\t\treturn -EINVAL;\n+\n+\t/* Sanitize and check TPH requester type compatibility */\n+\tif (req_type == PCI_TPH_REQ_DISABLE || req_type == 2 ||\n+\t\treq_type \u003e pdev-\u003etph_max_type)\n+\t\treturn -EINVAL;\n+\n \t/* Sanitize and check ST mode compatibility */\n \tmode \u0026= PCI_TPH_CTRL_MODE_SEL_MASK;\n \tdev_modes = get_st_modes(pdev);\n@@ -405,23 +494,7 @@ int pcie_enable_tph(struct pci_dev *pdev, int mode)\n \n \tpdev-\u003etph_mode = mode;\n \n-\t/* Get req_type supported by device and its Root Port */\n-\tpci_read_config_dword(pdev, pdev-\u003etph_cap + PCI_TPH_CAP, \u0026reg);\n-\tif (FIELD_GET(PCI_TPH_CAP_EXT_TPH, reg))\n-\t\tpdev-\u003etph_req_type = PCI_TPH_REQ_EXT_TPH;\n-\telse\n-\t\tpdev-\u003etph_req_type = PCI_TPH_REQ_TPH_ONLY;\n-\n-\t/* Check if the device is behind a Root Port */\n-\tif (pci_pcie_type(pdev) != PCI_EXP_TYPE_RC_END) {\n-\t\trp_req_type = get_rp_completer_type(pdev);\n-\n-\t\t/* Final req_type is the smallest value of two */\n-\t\tpdev-\u003etph_req_type = min(pdev-\u003etph_req_type, rp_req_type);\n-\t}\n-\n-\tif (pdev-\u003etph_req_type == PCI_TPH_REQ_DISABLE)\n-\t\treturn -EINVAL;\n+\tpdev-\u003etph_req_type = req_type;\n \n \t/* Write them into TPH control register */\n \tpci_read_config_dword(pdev, pdev-\u003etph_cap + PCI_TPH_CTRL, \u0026reg);\n@@ -435,6 +508,23 @@ int pcie_enable_tph(struct pci_dev *pdev, int mode)\n \n \treturn 0;\n }\n+EXPORT_SYMBOL(pcie_enable_tph_ext);\n+\n+/**\n+ * pcie_enable_tph - Enable TPH support for device using a specific ST mode\n+ * @pdev: PCI device\n+ * @mode: ST mode to enable: PCI_TPH_ST_(NS_MODE / IV_MODE / DS_MODE)\n+ *\n+ * Wrapper for pcie_enable_tph_ext(), automatically uses the pre-cached\n+ * tph_max_type value (negotiated maximum requester type between device and\n+ * root port).\n+ *\n+ * Return: 0 on success, otherwise negative value (-errno)\n+ */\n+int pcie_enable_tph(struct pci_dev *pdev, u8 mode)\n+{\n+\treturn pcie_enable_tph_ext(pdev, mode, pdev-\u003etph_max_type);\n+}\n EXPORT_SYMBOL(pcie_enable_tph);\n \n void pci_restore_tph_state(struct pci_dev *pdev)\n@@ -508,14 +598,60 @@ void pci_no_tph(void)\n \n void pci_tph_init(struct pci_dev *pdev)\n {\n+\tu8 tph_req_type, rp_req_type;\n \tint num_entries;\n \tu32 save_size;\n+\tu32 reg = 0;\n \n \tpdev-\u003etph_cap = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_TPH);\n \tif (!pdev-\u003etph_cap)\n \t\treturn;\n \n+\t/* Get req_type supported by device and its Root Port */\n+\tpci_read_config_dword(pdev, pdev-\u003etph_cap + PCI_TPH_CAP, \u0026reg);\n+\tif (FIELD_GET(PCI_TPH_CAP_EXT_TPH, reg))\n+\t\ttph_req_type = PCI_TPH_REQ_EXT_TPH;\n+\telse\n+\t\ttph_req_type = PCI_TPH_REQ_TPH_ONLY;\n+\n+\t/* Check if the device is behind a Root Port */\n+\tif (pci_pcie_type(pdev) != PCI_EXP_TYPE_RC_END) {\n+\t\trp_req_type = get_rp_completer_type(pdev);\n+\t\t/* Final req_type is the smallest value of two */\n+\t\ttph_req_type = min(tph_req_type, rp_req_type);\n+\t}\n+\n+\tpdev-\u003etph_max_type = tph_req_type;\n+\n \tnum_entries = pcie_tph_get_st_table_size(pdev);\n \tsave_size = sizeof(u32) + num_entries * sizeof(u16);\n \tpci_add_ext_cap_save_buffer(pdev, PCI_EXT_CAP_ID_TPH, save_size);\n }\n+\n+/**\n+ * pcie_tph_dsm_supported - Check if root port exposes TPH ST _DSM\n+ * @pdev: target PCI device\n+ *\n+ * Return true if root port of @pdev provides TPH ST DSM function,\n+ * false otherwise (ACPI disabled or DSM missing).\n+ */\n+bool pcie_tph_dsm_supported(struct pci_dev *pdev)\n+{\n+#ifdef CONFIG_ACPI\n+\tstruct pci_dev *rp = pcie_find_root_port(pdev);\n+\tacpi_handle handle;\n+\n+\tif (!rp || !rp-\u003ebus || !rp-\u003ebus-\u003ebridge)\n+\t\treturn false;\n+\n+\thandle = ACPI_HANDLE(rp-\u003ebus-\u003ebridge);\n+\tif (!handle)\n+\t\treturn false;\n+\n+\treturn acpi_check_dsm(handle, \u0026pci_acpi_dsm_guid, 7,\n+\t\t\t      BIT(TPH_ST_DSM_FUNC_INDEX));\n+#else\n+\treturn false;\n+#endif\n+}\n+EXPORT_SYMBOL_GPL(pcie_tph_dsm_supported);\ndiff --git a/drivers/vfio/pci/vfio_pci.c b/drivers/vfio/pci/vfio_pci.c\nindex 830369ff878dbe..c407c217b649fa 100644\n--- a/drivers/vfio/pci/vfio_pci.c\n+++ b/drivers/vfio/pci/vfio_pci.c\n@@ -60,6 +60,26 @@ static bool disable_denylist;\n module_param(disable_denylist, bool, 0444);\n MODULE_PARM_DESC(disable_denylist, \"Disable use of device denylist. Disabling the denylist allows binding to devices with known errata that may lead to exploitable stability or security issues when accessed by untrusted users.\");\n \n+static unsigned int tph_policy;\n+static int tph_policy_set(const char *val, const struct kernel_param *kp)\n+{\n+\treturn param_set_uint_minmax(val, kp, VFIO_PCI_TPH_POLICY_NO_ST,\n+\t\t\t\t     VFIO_PCI_TPH_POLICY_DS_ST);\n+}\n+static const struct kernel_param_ops tph_param_ops = {\n+\t.set = tph_policy_set,\n+\t.get = param_get_uint,\n+};\n+module_param_cb(tph_policy, \u0026tph_param_ops, \u0026tph_policy, 0644);\n+MODULE_PARM_DESC(tph_policy,\n+\t\"Global TPH policy level (0=default No-ST):\\n\"\n+\t\"0 = No-ST mode: RESOLVE only returns PH for DMABUF source;\\n\"\n+\t\"                ST programming unavailable\\n\"\n+\t\"1 = Add IV mode: RESOLVE only returns PH for DMABUF source;\\n\"\n+\t\"                 ST program supports NONE/DMABUF/CPU sources\\n\"\n+\t\"2 = Add DS mode: RESOLVE returns PH for DMABUF source, ST for DMABUF/CPU sources;\\n\"\n+\t\"                 ST program supports NONE/DMABUF/CPU sources\\n\");\n+\n static bool vfio_pci_dev_in_denylist(struct pci_dev *pdev)\n {\n \tswitch (pdev-\u003evendor) {\n@@ -142,6 +162,9 @@ static int vfio_pci_init_dev(struct vfio_device *core_vdev)\n #ifdef CONFIG_VFIO_PCI_VGA\n \tvdev-\u003edisable_vga = disable_vga;\n #endif\n+#ifdef CONFIG_PCIE_TPH\n+\tvdev-\u003etph_policy = tph_policy;\n+#endif\n \n \treturn vfio_pci_core_init_dev(core_vdev);\n }\ndiff --git a/drivers/vfio/pci/vfio_pci_config.c b/drivers/vfio/pci/vfio_pci_config.c\nindex a10ed733f0e3af..97dad6db69e39d 100644\n--- a/drivers/vfio/pci/vfio_pci_config.c\n+++ b/drivers/vfio/pci/vfio_pci_config.c\n@@ -22,6 +22,7 @@\n \n #include \u003clinux/fs.h\u003e\n #include \u003clinux/pci.h\u003e\n+#include \u003clinux/pci-tph.h\u003e\n #include \u003clinux/uaccess.h\u003e\n #include \u003clinux/vfio.h\u003e\n #include \u003clinux/slab.h\u003e\n@@ -1085,6 +1086,146 @@ static int __init init_pci_ext_cap_pwr_perm(struct perm_bits *perm)\n \treturn 0;\n }\n \n+static int vfio_find_cap_start(struct vfio_pci_core_device *vdev, int pos)\n+{\n+\tu8 cap;\n+\tint base = (pos \u003e= PCI_CFG_SPACE_SIZE) ? PCI_CFG_SPACE_SIZE :\n+\t\t\t\t\t\t PCI_STD_HEADER_SIZEOF;\n+\tcap = vdev-\u003epci_config_map[pos];\n+\n+\tif (cap == PCI_CAP_ID_BASIC)\n+\t\treturn 0;\n+\n+\t/* XXX Can we have to abutting capabilities of the same type? */\n+\twhile (pos - 1 \u003e= base \u0026\u0026 vdev-\u003epci_config_map[pos - 1] == cap)\n+\t\tpos--;\n+\n+\treturn pos;\n+}\n+\n+static void vfio_update_tph_vconfig_bytes(struct vfio_pci_core_device *vdev,\n+\t\t\t\t\t  int pos)\n+{\n+\t__le32 *vptr = (__le32 *)\u0026vdev-\u003evconfig[pos + PCI_TPH_CAP];\n+\tstruct pci_dev *pdev = vdev-\u003epdev;\n+\tu32 val = le32_to_cpu(*vptr);\n+\n+\tif (!pcie_ext_tph_supported(pdev)) {\n+\t\t/* Remove extended TPH if root-port doesn't support */\n+\t\tval \u0026= ~PCI_TPH_CAP_EXT_TPH;\n+\t}\n+\n+\tif (vdev-\u003etph_policy == VFIO_PCI_TPH_POLICY_NO_ST) {\n+\t\t/* Report only No-ST mode supported */\n+\t\tval \u0026= ~(PCI_TPH_CAP_ST_IV | PCI_TPH_CAP_ST_DS |\n+\t\t\t PCI_TPH_CAP_LOC_MASK | PCI_TPH_CAP_ST_MASK);\n+\t} else if (vdev-\u003etph_policy == VFIO_PCI_TPH_POLICY_IV_ST) {\n+\t\t/* Report only No-ST and IV modes supported */\n+\t\tval \u0026= ~PCI_TPH_CAP_ST_DS;\n+\t\t/* Remove ST location and size if dev doesn't support IV mode */\n+\t\tif (!(val \u0026 PCI_TPH_CAP_ST_IV))\n+\t\t\tval \u0026= ~(PCI_TPH_CAP_LOC_MASK | PCI_TPH_CAP_ST_MASK);\n+\t}\n+\n+\t*vptr = cpu_to_le32(val);\n+\n+\t/* Backup updated capability register value */\n+\tvdev-\u003etph_cap_virt = val;\n+}\n+\n+/* Permissions for TPH extended capability */\n+static int __init init_pci_ext_cap_tph_perm(struct perm_bits *perm)\n+{\n+\t/*\n+\t * We only virtualize the TPH header (excluding the ST table).\n+\t * For the ST table (if any), the read operation returns 0 by\n+\t * default, and the write operation is discarded by default.\n+\t */\n+\tif (alloc_perm_bits(perm, PCI_TPH_BASE_SIZEOF))\n+\t\treturn -ENOMEM;\n+\n+\tp_setd(perm, 0, ALL_VIRT, NO_WRITE);\n+\tp_setd(perm, PCI_TPH_CAP, ALL_VIRT, NO_WRITE);\n+\n+\tp_setd(perm, PCI_TPH_CTRL, ALL_VIRT,\n+\t       PCI_TPH_CTRL_MODE_SEL_MASK | PCI_TPH_CTRL_REQ_EN_MASK);\n+\n+\treturn 0;\n+}\n+\n+static int vfio_tph_config_read(struct vfio_pci_core_device *vdev, int pos,\n+\t\t\t\tint count, struct perm_bits *perm,\n+\t\t\t\tint offset, __le32 *val)\n+{\n+\tif (offset \u003e= PCI_TPH_BASE_SIZEOF) {\n+\t\t*val = cpu_to_le32(0);\n+\t\treturn count;\n+\t}\n+\n+\tif (offset \u003e= PCI_TPH_CAP \u0026\u0026 !vdev-\u003etph_opt_in) {\n+\t\t*val = cpu_to_le32(0);\n+\t\treturn count;\n+\t}\n+\n+\treturn vfio_default_config_read(vdev, pos, count, perm, offset, val);\n+}\n+\n+static int vfio_tph_config_write(struct vfio_pci_core_device *vdev, int pos,\n+\t\t\t\t int count, struct perm_bits *perm,\n+\t\t\t\t int offset, __le32 val)\n+{\n+\tu16 start = vfio_find_cap_start(vdev, pos);\n+\tstruct pci_dev *pdev = vdev-\u003epdev;\n+\tu32 org_ctrl, new_ctrl, cap;\n+\tu8 mode, req, org_req;\n+\t__le32 org_val = 0;\n+\tu16 cmd;\n+\tint ret;\n+\n+\tif (!vdev-\u003etph_opt_in || offset \u003e= PCI_TPH_BASE_SIZEOF)\n+\t\treturn count;\n+\n+\tcmd = vfio_pci_memory_lock_and_enable(vdev);\n+\n+\torg_ctrl = le32_to_cpu(*(__le32 *)\u0026vdev-\u003evconfig[start + PCI_TPH_CTRL]);\n+\tvfio_default_config_read(vdev, pos, count, perm, offset, \u0026org_val);\n+\n+\tret = vfio_default_config_write(vdev, pos, count, perm, offset, val);\n+\tif (ret != count)\n+\t\tgoto out;\n+\n+\tnew_ctrl = le32_to_cpu(*(__le32 *)\u0026vdev-\u003evconfig[start + PCI_TPH_CTRL]);\n+\tif (new_ctrl == org_ctrl)\n+\t\tgoto out; /* Only care about changes in TPH_CTRL. */\n+\n+\tcap = le32_to_cpu(*(__le32 *)\u0026vdev-\u003evconfig[start + PCI_TPH_CAP]);\n+\tmode = FIELD_GET(PCI_TPH_CTRL_MODE_SEL_MASK, new_ctrl);\n+\treq = FIELD_GET(PCI_TPH_CTRL_REQ_EN_MASK, new_ctrl);\n+\tif (mode \u003e PCI_TPH_ST_DS_MODE || !(cap \u0026 (1u \u003c\u003c mode)) || req == 0x2 ||\n+\t\t(req == PCI_TPH_REQ_EXT_TPH \u0026\u0026 !(cap \u0026 PCI_TPH_CAP_EXT_TPH)))\n+\t\tgoto restore; /* Drop invalid or unsupported write value */\n+\n+\torg_req = FIELD_GET(PCI_TPH_CTRL_REQ_EN_MASK, org_ctrl);\n+\tif (req == org_req)\n+\t\tgoto restore; /* Only care about requester enable */\n+\n+\tif (req == PCI_TPH_REQ_TPH_ONLY || req == PCI_TPH_REQ_EXT_TPH) {\n+\t\tret = pcie_enable_tph_ext(pdev, mode, req);\n+\t\tif (ret)\n+\t\t\tgoto restore;\n+\t} else if (req == PCI_TPH_REQ_DISABLE) {\n+\t\tpcie_disable_tph(vdev-\u003epdev);\n+\t}\n+\n+\tgoto out;\n+\n+restore:\n+\tvfio_default_config_write(vdev, pos, count, perm, offset, org_val);\n+out:\n+\tvfio_pci_memory_unlock_and_restore(vdev, cmd);\n+\treturn count;\n+}\n+\n /*\n  * Initialize the shared permission tables\n  */\n@@ -1100,6 +1241,7 @@ void vfio_pci_uninit_perm_bits(void)\n \n \tfree_perm_bits(\u0026ecap_perms[PCI_EXT_CAP_ID_ERR]);\n \tfree_perm_bits(\u0026ecap_perms[PCI_EXT_CAP_ID_PWR]);\n+\tfree_perm_bits(\u0026ecap_perms[PCI_EXT_CAP_ID_TPH]);\n }\n \n int __init vfio_pci_init_perm_bits(void)\n@@ -1120,6 +1262,9 @@ int __init vfio_pci_init_perm_bits(void)\n \t/* Extended capabilities */\n \tret |= init_pci_ext_cap_err_perm(\u0026ecap_perms[PCI_EXT_CAP_ID_ERR]);\n \tret |= init_pci_ext_cap_pwr_perm(\u0026ecap_perms[PCI_EXT_CAP_ID_PWR]);\n+\tret |= init_pci_ext_cap_tph_perm(\u0026ecap_perms[PCI_EXT_CAP_ID_TPH]);\n+\tecap_perms[PCI_EXT_CAP_ID_TPH].readfn = vfio_tph_config_read;\n+\tecap_perms[PCI_EXT_CAP_ID_TPH].writefn = vfio_tph_config_write;\n \tecap_perms[PCI_EXT_CAP_ID_VNDR].writefn = vfio_raw_config_write;\n \tecap_perms[PCI_EXT_CAP_ID_DVSEC].writefn = vfio_raw_config_write;\n \n@@ -1129,23 +1274,6 @@ int __init vfio_pci_init_perm_bits(void)\n \treturn ret;\n }\n \n-static int vfio_find_cap_start(struct vfio_pci_core_device *vdev, int pos)\n-{\n-\tu8 cap;\n-\tint base = (pos \u003e= PCI_CFG_SPACE_SIZE) ? PCI_CFG_SPACE_SIZE :\n-\t\t\t\t\t\t PCI_STD_HEADER_SIZEOF;\n-\tcap = vdev-\u003epci_config_map[pos];\n-\n-\tif (cap == PCI_CAP_ID_BASIC)\n-\t\treturn 0;\n-\n-\t/* XXX Can we have to abutting capabilities of the same type? */\n-\twhile (pos - 1 \u003e= base \u0026\u0026 vdev-\u003epci_config_map[pos - 1] == cap)\n-\t\tpos--;\n-\n-\treturn pos;\n-}\n-\n static int vfio_msi_config_read(struct vfio_pci_core_device *vdev, int pos,\n \t\t\t\tint count, struct perm_bits *perm,\n \t\t\t\tint offset, __le32 *val)\n@@ -1701,6 +1829,8 @@ static int vfio_ecap_init(struct vfio_pci_core_device *vdev)\n \t\tret = vfio_fill_vconfig_bytes(vdev, epos, len);\n \t\tif (ret)\n \t\t\treturn ret;\n+\t\tif (ecap == PCI_EXT_CAP_ID_TPH)\n+\t\t\tvfio_update_tph_vconfig_bytes(vdev, epos);\n \n \t\t/*\n \t\t * If we're just using this capability to anchor the list,\ndiff --git a/drivers/vfio/pci/vfio_pci_core.c b/drivers/vfio/pci/vfio_pci_core.c\nindex 3f11a9624b9c03..b8d9d840a93b57 100644\n--- a/drivers/vfio/pci/vfio_pci_core.c\n+++ b/drivers/vfio/pci/vfio_pci_core.c\n@@ -13,6 +13,8 @@\n #include \u003clinux/aperture.h\u003e\n #include \u003clinux/debugfs.h\u003e\n #include \u003clinux/device.h\u003e\n+#include \u003clinux/dma-buf.h\u003e\n+#include \u003clinux/dma-resv.h\u003e\n #include \u003clinux/eventfd.h\u003e\n #include \u003clinux/file.h\u003e\n #include \u003clinux/interrupt.h\u003e\n@@ -30,6 +32,7 @@\n #include \u003clinux/sched/mm.h\u003e\n #include \u003clinux/iommufd.h\u003e\n #include \u003clinux/pci-p2pdma.h\u003e\n+#include \u003clinux/pci-tph.h\u003e\n #include \u003clinux/seq_file.h\u003e\n #if IS_ENABLED(CONFIG_EEH)\n #include \u003casm/eeh.h\u003e\n@@ -127,10 +130,20 @@ static int vfio_pci_core_debugfs_disable_idle_d3(struct seq_file *seq,\n \treturn 0;\n }\n \n+static int vfio_pci_core_debugfs_tph(struct seq_file *seq,\n+\t\t\t\t\t    void *data)\n+{\n+\tstruct vfio_pci_core_device *vdev = vfio_pci_core_debugfs_private(seq);\n+\n+\tseq_printf(seq, \"policy=%u cap-virt=0x%x opt-in=%u\\n\",\n+\t\tvdev-\u003etph_policy, vdev-\u003etph_cap_virt, vdev-\u003etph_opt_in);\n+\treturn 0;\n+}\n+\n /*\n- * disable_idle_d3 and nointxmask are writable module parameters latched\n- * per device at init, so a device's effective value can differ from the\n- * current parameter setting.  Expose the per-device (read-only) values\n+ * disable_idle_d3, nointxmask and tph_policy are writable module parameters\n+ * latched per device at init, so a device's effective value can differ from\n+ * the current parameter setting.  Expose the per-device (read-only) values\n  * here for visibility; read-only parameters can't drift and are omitted.\n  */\n static void vfio_pci_core_debugfs_init(struct vfio_pci_core_device *vdev)\n@@ -146,6 +159,8 @@ static void vfio_pci_core_debugfs_init(struct vfio_pci_core_device *vdev)\n \t\t\t\t    vfio_pci_core_debugfs_nointxmask);\n \tdebugfs_create_devm_seqfile(dev, \"disable_idle_d3\", pci_dir,\n \t\t\t\t    vfio_pci_core_debugfs_disable_idle_d3);\n+\tdebugfs_create_devm_seqfile(dev, \"tph\", pci_dir,\n+\t\t\t\t    vfio_pci_core_debugfs_tph);\n }\n #else\n static inline void vfio_pci_core_debugfs_init(struct vfio_pci_core_device *vdev)\n@@ -610,6 +625,9 @@ int vfio_pci_core_enable(struct vfio_pci_core_device *vdev)\n \t\tgoto out_disable_device;\n \n \tvdev-\u003ereset_works = !ret;\n+\tvdev-\u003etph_opt_in = false;\n+\t/* Reset TPH status on new user session */\n+\tpcie_disable_tph(vdev-\u003epdev);\n \tpci_save_state(pdev);\n \tvdev-\u003epci_saved_state = pci_store_saved_state(pdev);\n \tif (!vdev-\u003epci_saved_state)\n@@ -757,6 +775,9 @@ void vfio_pci_core_disable(struct vfio_pci_core_device *vdev)\n \n \tvfio_pci_zdev_close_device(vdev);\n \n+\t/* Reset TPH status on exit user session */\n+\tpcie_disable_tph(vdev-\u003epdev);\n+\n \t/*\n \t * If we have saved state, restore it.  If we can reset the device,\n \t * even better.  Resetting with current state seems better than\n@@ -1558,6 +1579,7 @@ long vfio_pci_core_ioctl(struct vfio_device *core_vdev, unsigned int cmd,\n \tstruct vfio_pci_core_device *vdev =\n \t\tcontainer_of(core_vdev, struct vfio_pci_core_device, vdev);\n \tvoid __user *uarg = (void __user *)arg;\n+\tint ret;\n \n \tswitch (cmd) {\n \tcase VFIO_DEVICE_GET_INFO:\n@@ -1569,9 +1591,15 @@ long vfio_pci_core_ioctl(struct vfio_device *core_vdev, unsigned int cmd,\n \tcase VFIO_DEVICE_IOEVENTFD:\n \t\treturn vfio_pci_ioctl_ioeventfd(vdev, uarg);\n \tcase VFIO_DEVICE_PCI_HOT_RESET:\n-\t\treturn vfio_pci_ioctl_pci_hot_reset(vdev, uarg);\n+\t\tret = vfio_pci_ioctl_pci_hot_reset(vdev, uarg);\n+\t\tif (!ret)\n+\t\t\tpcie_disable_tph(vdev-\u003epdev);\n+\t\treturn ret;\n \tcase VFIO_DEVICE_RESET:\n-\t\treturn vfio_pci_ioctl_reset(vdev, uarg);\n+\t\tret = vfio_pci_ioctl_reset(vdev, uarg);\n+\t\tif (!ret)\n+\t\t\tpcie_disable_tph(vdev-\u003epdev);\n+\t\treturn ret;\n \tcase VFIO_DEVICE_SET_IRQS:\n \t\treturn vfio_pci_ioctl_set_irqs(vdev, uarg);\n \tdefault:\n@@ -1607,6 +1635,299 @@ static int vfio_pci_core_feature_token(struct vfio_pci_core_device *vdev,\n \treturn 0;\n }\n \n+static u32 vfio_pci_get_tph_st_size(struct vfio_pci_core_device *vdev)\n+{\n+\tstruct pci_dev *pdev = vdev-\u003epdev;\n+\tu32 loc = pcie_tph_get_st_table_loc(pdev);\n+\tint ret;\n+\n+\tif (loc == PCI_TPH_LOC_CAP) {\n+\t\treturn pcie_tph_get_st_table_size(pdev);\n+\t} else if (loc == PCI_TPH_LOC_MSIX) {\n+\t\tret = pci_msix_vec_count(pdev);\n+\t\tif (ret \u003c 0)\n+\t\t\treturn 0;\n+\t\treturn ret;\n+\t} else {\n+\t\treturn 0;\n+\t}\n+}\n+\n+static u32 vfio_pci_get_tph_capability(struct vfio_pci_core_device *vdev)\n+{\n+\tu32 flags = VFIO_DEVICE_TPH_CAP_RESOLVE_DMABUF_PH;\n+\n+\tif (vdev-\u003etph_policy \u003e= VFIO_PCI_TPH_POLICY_IV_ST \u0026\u0026\n+\t\t(vdev-\u003etph_cap_virt \u0026 PCI_TPH_CAP_ST_IV)) {\n+\t\tflags |= VFIO_DEVICE_TPH_CAP_ST_NONE;\n+\t\tflags |= VFIO_DEVICE_TPH_CAP_ST_DMABUF;\n+\t\tif (pcie_tph_dsm_supported(vdev-\u003epdev))\n+\t\t\tflags |= VFIO_DEVICE_TPH_CAP_ST_CPU;\n+\t}\n+\tif (vdev-\u003etph_policy \u003e= VFIO_PCI_TPH_POLICY_DS_ST \u0026\u0026\n+\t\t(vdev-\u003etph_cap_virt \u0026 PCI_TPH_CAP_ST_DS)) {\n+\t\tif (vfio_pci_get_tph_st_size(vdev) \u003e 0) {\n+\t\t\tflags |= VFIO_DEVICE_TPH_CAP_ST_NONE;\n+\t\t\tflags |= VFIO_DEVICE_TPH_CAP_ST_DMABUF;\n+\t\t\tif (pcie_tph_dsm_supported(vdev-\u003epdev))\n+\t\t\t\tflags |= VFIO_DEVICE_TPH_CAP_ST_CPU;\n+\t\t}\n+\t\tif (pcie_tph_dsm_supported(vdev-\u003epdev))\n+\t\t\tflags |= VFIO_DEVICE_TPH_CAP_RESOLVE_CPU_ST;\n+\t}\n+\n+\treturn flags;\n+}\n+\n+static int vfio_pci_core_feature_tph(struct vfio_pci_core_device *vdev,\n+\t\t\t\t     u32 flags,\n+\t\t\t\t     struct vfio_device_feature_tph __user *arg,\n+\t\t\t\t     size_t argsz)\n+{\n+\tstruct vfio_device_feature_tph tph;\n+\tint ret;\n+\n+\tif (!pcie_std_tph_supported(vdev-\u003epdev))\n+\t\treturn -EOPNOTSUPP;\n+\n+\tret = vfio_check_feature(flags, argsz,\n+\t\t\tVFIO_DEVICE_FEATURE_GET | VFIO_DEVICE_FEATURE_SET,\n+\t\t\tsizeof(tph));\n+\tif (ret != 1)\n+\t\treturn ret;\n+\n+\tif (copy_from_user(\u0026tph, arg, sizeof(tph)))\n+\t\treturn -EFAULT;\n+\n+\tif (flags \u0026 VFIO_DEVICE_FEATURE_SET) {\n+\t\tif (tph.flags != 0)\n+\t\t\treturn -EINVAL;\n+\t\tvdev-\u003etph_opt_in = 1;\n+\t\treturn 0;\n+\t}\n+\n+\tif (!vdev-\u003etph_opt_in)\n+\t\treturn -EINVAL;\n+\n+\ttph.flags = vfio_pci_get_tph_capability(vdev);\n+\n+\treturn copy_to_user(arg, \u0026tph, sizeof(tph)) ? -EFAULT : 0;\n+}\n+\n+static u32 vfio_pci_get_tph_resolve_allow_src(struct vfio_pci_core_device *vdev)\n+{\n+\tu32 flags = vfio_pci_get_tph_capability(vdev);\n+\tu32 allow_src = 0;\n+\n+\tif (flags \u0026 (VFIO_DEVICE_TPH_CAP_RESOLVE_DMABUF_PH |\n+\t\t     VFIO_DEVICE_TPH_CAP_RESOLVE_DMABUF_ST))\n+\t\tallow_src |= VFIO_DEVICE_TPH_SRC_DMABUF;\n+\tif (flags \u0026 VFIO_DEVICE_TPH_CAP_RESOLVE_CPU_ST)\n+\t\tallow_src |= VFIO_DEVICE_TPH_SRC_CPU_VOLATILE |\n+\t\t\t     VFIO_DEVICE_TPH_SRC_CPU_PERSISTENT;\n+\n+\treturn allow_src;\n+}\n+\n+static int vfio_pci_get_dmabuf_tph(int fd, bool extended, u16 *st, u8 *ph)\n+{\n+\tstruct dma_buf *dmabuf;\n+\tint ret;\n+\n+\tdmabuf = dma_buf_get(fd);\n+\tif (IS_ERR(dmabuf))\n+\t\treturn PTR_ERR(dmabuf);\n+\n+\tret = dma_resv_lock_interruptible(dmabuf-\u003eresv, NULL);\n+\tif (ret == 0)\n+\t\tret = dma_buf_get_pci_tph(dmabuf, extended, st, ph);\n+\tdma_resv_unlock(dmabuf-\u003eresv);\n+\n+\tdma_buf_put(dmabuf);\n+\treturn ret;\n+}\n+\n+static int vfio_pci_get_tph_st_tag(struct pci_dev *pdev, u32 src_bits,\n+\t\t\t\t   u32 src_hndl, bool extended, u16 *st)\n+{\n+\tu8 req_type = extended ? PCI_TPH_REQ_EXT_TPH : PCI_TPH_REQ_TPH_ONLY;\n+\tint ret = 0;\n+\tu8 ph;\n+\n+\tif (src_bits \u0026 VFIO_DEVICE_TPH_SRC_DMABUF)\n+\t\tret = vfio_pci_get_dmabuf_tph(src_hndl, extended, st, \u0026ph);\n+\telse if (src_bits \u0026 VFIO_DEVICE_TPH_SRC_CPU_VOLATILE)\n+\t\tret = pcie_tph_get_cpu_st_ext(pdev, TPH_MEM_TYPE_VM,\n+\t\t\t\t\t      req_type, src_hndl, st);\n+\telse if (src_bits \u0026 VFIO_DEVICE_TPH_SRC_CPU_PERSISTENT)\n+\t\tret = pcie_tph_get_cpu_st_ext(pdev, TPH_MEM_TYPE_PM,\n+\t\t\t\t\t      req_type, src_hndl, st);\n+\telse if (src_bits \u0026 VFIO_DEVICE_TPH_SRC_NONE)\n+\t\t*st = 0;\n+\telse\n+\t\tret = -EINVAL;\n+\n+\tif (ret != 0)\n+\t\t*st = 0;\n+\n+\treturn ret;\n+}\n+\n+static int vfio_pci_core_feature_tph_resolve(struct vfio_pci_core_device *vdev,\n+\t\t\tu32 flags,\n+\t\t\tstruct vfio_device_feature_tph_resolve __user *arg,\n+\t\t\tsize_t argsz)\n+{\n+\tstruct vfio_device_feature_tph_resolve resolve;\n+\tu32 user_flags, allow_src;\n+\tbool extended;\n+\tu16 tag = 0;\n+\tu8 ph = 0;\n+\tint ret;\n+\n+\tif (!vdev-\u003etph_opt_in)\n+\t\treturn -EOPNOTSUPP;\n+\n+\tret = vfio_check_feature(flags, argsz, VFIO_DEVICE_FEATURE_GET,\n+\t\t\t\t sizeof(resolve));\n+\tif (ret != 1)\n+\t\treturn ret;\n+\n+\tif (copy_from_user(\u0026resolve, arg, sizeof(resolve)))\n+\t\treturn -EFAULT;\n+\n+\tuser_flags = resolve.flags;\n+\textended = !!(user_flags \u0026 VFIO_DEVICE_TPH_EXTENDED);\n+\tif (extended \u0026\u0026 !pcie_ext_tph_supported(vdev-\u003epdev))\n+\t\treturn -EOPNOTSUPP;\n+\tuser_flags \u0026= ~VFIO_DEVICE_TPH_EXTENDED;\n+\n+\tallow_src = vfio_pci_get_tph_resolve_allow_src(vdev);\n+\tif (user_flags \u0026 ~allow_src || !is_power_of_2(user_flags))\n+\t\treturn -EINVAL;\n+\n+\tresolve.valid = 0;\n+\tresolve.ph = 0;\n+\tresolve.st = 0;\n+\n+\tif (user_flags \u0026 VFIO_DEVICE_TPH_SRC_DMABUF) {\n+\t\tret = vfio_pci_get_dmabuf_tph(resolve.src, extended,\n+\t\t\t\t\t      \u0026tag, \u0026ph);\n+\t\tif (ret)\n+\t\t\treturn ret;\n+\t\tresolve.ph = ph;\n+\t\tresolve.valid = VFIO_DEVICE_TPH_VALID_PH;\n+\t\tif (vdev-\u003etph_policy \u003e= VFIO_PCI_TPH_POLICY_DS_ST) {\n+\t\t\tresolve.st = tag;\n+\t\t\tresolve.valid |= VFIO_DEVICE_TPH_VALID_ST;\n+\t\t}\n+\t\tgoto out;\n+\t}\n+\n+\tret = vfio_pci_get_tph_st_tag(vdev-\u003epdev, user_flags, resolve.src,\n+\t\t\t\t      extended, \u0026tag);\n+\tif (ret)\n+\t\treturn ret;\n+\tresolve.valid |= VFIO_DEVICE_TPH_VALID_ST;\n+\tresolve.st = tag;\n+\n+out:\n+\treturn copy_to_user(arg, \u0026resolve, sizeof(resolve)) ? -EFAULT : 0;\n+}\n+\n+static u32 vfio_pci_get_tph_st_allow_src(struct vfio_pci_core_device *vdev)\n+{\n+\tu32 flags = vfio_pci_get_tph_capability(vdev);\n+\tu32 allow_src = 0;\n+\n+\tif (flags \u0026 VFIO_DEVICE_TPH_CAP_ST_DMABUF)\n+\t\tallow_src |= VFIO_DEVICE_TPH_SRC_DMABUF;\n+\tif (flags \u0026 VFIO_DEVICE_TPH_CAP_ST_CPU)\n+\t\tallow_src |= VFIO_DEVICE_TPH_SRC_CPU_VOLATILE |\n+\t\t\t     VFIO_DEVICE_TPH_SRC_CPU_PERSISTENT;\n+\tif (flags \u0026 VFIO_DEVICE_TPH_CAP_ST_NONE)\n+\t\tallow_src |= VFIO_DEVICE_TPH_SRC_NONE;\n+\n+\treturn allow_src;\n+}\n+\n+static int vfio_pci_core_feature_tph_st(struct vfio_pci_core_device *vdev,\n+\t\t\t\tu32 flags,\n+\t\t\t\tstruct vfio_device_feature_tph_st __user *arg,\n+\t\t\t\tsize_t argsz)\n+{\n+\tstruct vfio_device_feature_tph_st tph_st;\n+\tu32 user_flags, allow_src, st_size;\n+\tstruct pci_dev *pdev = vdev-\u003epdev;\n+\tbool extended, stop_on_zero;\n+\tu32 *src_hndl = NULL;\n+\tu16 *tags = NULL;\n+\tint ret, i;\n+\tu16 cmd;\n+\n+\tif (!vdev-\u003etph_opt_in || vdev-\u003etph_policy == VFIO_PCI_TPH_POLICY_NO_ST)\n+\t\treturn -EOPNOTSUPP;\n+\n+\tret = vfio_check_feature(flags, argsz, VFIO_DEVICE_FEATURE_SET,\n+\t\t\t\t sizeof(tph_st));\n+\tif (ret != 1)\n+\t\treturn ret;\n+\n+\tif (copy_from_user(\u0026tph_st, arg, sizeof(tph_st)))\n+\t\treturn -EFAULT;\n+\n+\tuser_flags = tph_st.flags;\n+\textended = !!(user_flags \u0026 VFIO_DEVICE_TPH_EXTENDED);\n+\tif (extended \u0026\u0026 !pcie_ext_tph_supported(vdev-\u003epdev))\n+\t\treturn -EOPNOTSUPP;\n+\tuser_flags \u0026= ~VFIO_DEVICE_TPH_EXTENDED;\n+\n+\tstop_on_zero = !!(user_flags \u0026 VFIO_DEVICE_TPH_REQUIRE_ST);\n+\tif (stop_on_zero \u0026\u0026 (user_flags \u0026 VFIO_DEVICE_TPH_SRC_NONE))\n+\t\treturn -EINVAL;\n+\tuser_flags \u0026= ~VFIO_DEVICE_TPH_REQUIRE_ST;\n+\n+\tallow_src = vfio_pci_get_tph_st_allow_src(vdev);\n+\tif (user_flags \u0026 ~allow_src || !is_power_of_2(user_flags))\n+\t\treturn -EINVAL;\n+\n+\tst_size = vfio_pci_get_tph_st_size(vdev);\n+\tif (tph_st.start \u003e= st_size || tph_st.count \u003e st_size - tph_st.start ||\n+\t\ttph_st.count == 0)\n+\t\treturn -EINVAL;\n+\n+\tif (!(user_flags \u0026 VFIO_DEVICE_TPH_SRC_NONE)) {\n+\t\tsrc_hndl = memdup_array_user(u64_to_user_ptr(tph_st.dests),\n+\t\t\t\t\t     tph_st.count, sizeof(u32));\n+\t\tif (IS_ERR(src_hndl))\n+\t\t\treturn PTR_ERR(src_hndl);\n+\t}\n+\ttags = kcalloc(tph_st.count, sizeof(u16), GFP_KERNEL_ACCOUNT);\n+\tif (!tags) {\n+\t\tret = -ENOMEM;\n+\t\tgoto out;\n+\t}\n+\n+\tfor (i = 0; i \u003c tph_st.count; i++) {\n+\t\tret = vfio_pci_get_tph_st_tag(pdev, user_flags,\n+\t\t\t\t\t      src_hndl ? src_hndl[i] : 0,\n+\t\t\t\t\t      extended, \u0026tags[i]);\n+\t\tif (ret || (stop_on_zero \u0026\u0026 tags[i] == 0))\n+\t\t\tbreak;\n+\t}\n+\n+\tif (i \u003e 0) {\n+\t\tcmd = vfio_pci_memory_lock_and_enable(vdev);\n+\t\tret = pcie_tph_set_st_entries(pdev, tph_st.start, i, tags);\n+\t\tvfio_pci_memory_unlock_and_restore(vdev, cmd);\n+\t}\n+\n+out:\n+\tkfree(tags);\n+\tkfree(src_hndl);\n+\treturn ret;\n+}\n+\n int vfio_pci_core_ioctl_feature(struct vfio_device *device, u32 flags,\n \t\t\t\tvoid __user *arg, size_t argsz)\n {\n@@ -1625,6 +1946,16 @@ int vfio_pci_core_ioctl_feature(struct vfio_device *device, u32 flags,\n \t\treturn vfio_pci_core_feature_token(vdev, flags, arg, argsz);\n \tcase VFIO_DEVICE_FEATURE_DMA_BUF:\n \t\treturn vfio_pci_core_feature_dma_buf(vdev, flags, arg, argsz);\n+\tcase VFIO_DEVICE_FEATURE_DMA_BUF_TPH:\n+\t\treturn vfio_pci_core_feature_dma_buf_tph(vdev, flags, arg,\n+\t\t\t\t\t\t\t argsz);\n+\tcase VFIO_DEVICE_FEATURE_TPH:\n+\t\treturn vfio_pci_core_feature_tph(vdev, flags, arg, argsz);\n+\tcase VFIO_DEVICE_FEATURE_TPH_RESOLVE:\n+\t\treturn vfio_pci_core_feature_tph_resolve(vdev, flags,\n+\t\t\t\t\t\t\t arg, argsz);\n+\tcase VFIO_DEVICE_FEATURE_TPH_ST:\n+\t\treturn vfio_pci_core_feature_tph_st(vdev, flags, arg, argsz);\n \tdefault:\n \t\treturn -ENOTTY;\n \t}\ndiff --git a/drivers/vfio/pci/vfio_pci_dmabuf.c b/drivers/vfio/pci/vfio_pci_dmabuf.c\nindex c16f460c01d681..5351839c1338af 100644\n--- a/drivers/vfio/pci/vfio_pci_dmabuf.c\n+++ b/drivers/vfio/pci/vfio_pci_dmabuf.c\n@@ -3,6 +3,7 @@\n  */\n #include \u003clinux/dma-buf-mapping.h\u003e\n #include \u003clinux/pci-p2pdma.h\u003e\n+#include \u003clinux/pci-tph.h\u003e\n #include \u003clinux/dma-resv.h\u003e\n \n #include \"vfio_pci_priv.h\"\n@@ -19,7 +20,17 @@ struct vfio_pci_dma_buf {\n \tu32 nr_ranges;\n \tstruct kref kref;\n \tstruct completion comp;\n-\tu8 revoked : 1;\n+\n+\t/*\n+\t * Updates protected by dmabuf-\u003eresv, @revoked additionally\n+\t * protected by memory_lock.\n+\t */\n+\tu16 tph_st_ext;\n+\tu8 tph_st;\n+\tbool revoked;\n+\tu8 tph_st_valid:1;\n+\tu8 tph_st_ext_valid:1;\n+\tu8 tph_ph:2;\n };\n \n static int vfio_pci_dma_buf_attach(struct dma_buf *dmabuf,\n@@ -30,7 +41,7 @@ static int vfio_pci_dma_buf_attach(struct dma_buf *dmabuf,\n \tif (!attachment-\u003epeer2peer)\n \t\treturn -EOPNOTSUPP;\n \n-\tif (priv-\u003erevoked)\n+\tif (READ_ONCE(priv-\u003erevoked))\n \t\treturn -ENODEV;\n \n \tif (!dma_buf_attach_revocable(attachment))\n@@ -69,6 +80,26 @@ vfio_pci_dma_buf_map(struct dma_buf_attachment *attachment,\n \treturn ret;\n }\n \n+static int vfio_pci_dma_buf_get_pci_tph(struct dma_buf *dmabuf, bool extended,\n+\t\t\t\t\tu16 *steering_tag, u8 *ph)\n+{\n+\tstruct vfio_pci_dma_buf *priv = dmabuf-\u003epriv;\n+\n+\tdma_resv_assert_held(dmabuf-\u003eresv);\n+\n+\tif (extended) {\n+\t\tif (!priv-\u003etph_st_ext_valid)\n+\t\t\treturn -EOPNOTSUPP;\n+\t\t*steering_tag = priv-\u003etph_st_ext;\n+\t} else {\n+\t\tif (!priv-\u003etph_st_valid)\n+\t\t\treturn -EOPNOTSUPP;\n+\t\t*steering_tag = priv-\u003etph_st;\n+\t}\n+\t*ph = priv-\u003etph_ph;\n+\treturn 0;\n+}\n+\n static void vfio_pci_dma_buf_unmap(struct dma_buf_attachment *attachment,\n \t\t\t\t   struct sg_table *sgt,\n \t\t\t\t   enum dma_data_direction dir)\n@@ -101,6 +132,7 @@ static void vfio_pci_dma_buf_release(struct dma_buf *dmabuf)\n \n static const struct dma_buf_ops vfio_pci_dmabuf_ops = {\n \t.attach = vfio_pci_dma_buf_attach,\n+\t.get_pci_tph = vfio_pci_dma_buf_get_pci_tph,\n \t.map_dma_buf = vfio_pci_dma_buf_map,\n \t.unmap_dma_buf = vfio_pci_dma_buf_unmap,\n \t.release = vfio_pci_dma_buf_release,\n@@ -333,6 +365,72 @@ int vfio_pci_core_feature_dma_buf(struct vfio_pci_core_device *vdev, u32 flags,\n \treturn ret;\n }\n \n+int vfio_pci_core_feature_dma_buf_tph(struct vfio_pci_core_device *vdev,\n+\t\t\t\t      u32 flags,\n+\t\t\t\t      struct vfio_device_feature_dma_buf_tph __user *arg,\n+\t\t\t\t      size_t argsz)\n+{\n+\tstruct vfio_device_feature_dma_buf_tph set_tph;\n+\tstruct vfio_pci_dma_buf *priv;\n+\tstruct dma_buf *dmabuf;\n+\tu8 comp;\n+\tint ret;\n+\n+\tcomp = pcie_tph_completer_type(vdev-\u003epdev);\n+\tif (comp == PCI_EXP_DEVCAP2_TPH_COMP_NONE)\n+\t\treturn -EOPNOTSUPP;\n+\n+\tret = vfio_check_feature(flags, argsz, VFIO_DEVICE_FEATURE_SET,\n+\t\t\t\t sizeof(set_tph));\n+\tif (ret != 1)\n+\t\treturn ret;\n+\n+\tif (copy_from_user(\u0026set_tph, arg, sizeof(set_tph)))\n+\t\treturn -EFAULT;\n+\n+\tif (set_tph.flags \u0026 ~(VFIO_DMA_BUF_TPH_ST | VFIO_DMA_BUF_TPH_ST_EXT))\n+\t\treturn -EINVAL;\n+\n+\tif (set_tph.ph \u0026 ~0x3)\n+\t\treturn -EINVAL;\n+\n+\tif ((set_tph.flags \u0026 VFIO_DMA_BUF_TPH_ST_EXT) \u0026\u0026\n+\t    comp != PCI_EXP_DEVCAP2_TPH_COMP_EXT_TPH)\n+\t\treturn -EOPNOTSUPP;\n+\n+\tdmabuf = dma_buf_get(set_tph.dmabuf_fd);\n+\tif (IS_ERR(dmabuf))\n+\t\treturn PTR_ERR(dmabuf);\n+\n+\tif (dmabuf-\u003eops != \u0026vfio_pci_dmabuf_ops) {\n+\t\tret = -EINVAL;\n+\t\tgoto out_put;\n+\t}\n+\n+\tpriv = dmabuf-\u003epriv;\n+\tif (priv-\u003evdev != vdev) {\n+\t\tret = -EINVAL;\n+\t\tgoto out_put;\n+\t}\n+\n+\tret = dma_resv_lock_interruptible(dmabuf-\u003eresv, NULL);\n+\tif (ret)\n+\t\tgoto out_put;\n+\n+\tpriv-\u003etph_st         = set_tph.steering_tag;\n+\tpriv-\u003etph_st_ext     = set_tph.steering_tag_ext;\n+\tpriv-\u003etph_ph         = set_tph.ph;\n+\tpriv-\u003etph_st_valid   = !!(set_tph.flags \u0026 VFIO_DMA_BUF_TPH_ST);\n+\tpriv-\u003etph_st_ext_valid =\n+\t\t!!(set_tph.flags \u0026 VFIO_DMA_BUF_TPH_ST_EXT);\n+\tdma_resv_unlock(dmabuf-\u003eresv);\n+\tret = 0;\n+\n+out_put:\n+\tdma_buf_put(dmabuf);\n+\treturn ret;\n+}\n+\n void vfio_pci_dma_buf_move(struct vfio_pci_core_device *vdev, bool revoked)\n {\n \tstruct vfio_pci_dma_buf *priv;\ndiff --git a/drivers/vfio/pci/vfio_pci_priv.h b/drivers/vfio/pci/vfio_pci_priv.h\nindex fca9d0dfac90f8..03c4a0414264ef 100644\n--- a/drivers/vfio/pci/vfio_pci_priv.h\n+++ b/drivers/vfio/pci/vfio_pci_priv.h\n@@ -11,6 +11,12 @@\n /* Cap maximum number of ioeventfds per device (arbitrary) */\n #define VFIO_PCI_IOEVENTFD_MAX\t\t1000\n \n+enum vfio_pci_tph_policy {\n+\tVFIO_PCI_TPH_POLICY_NO_ST = 0,\n+\tVFIO_PCI_TPH_POLICY_IV_ST = 1,\n+\tVFIO_PCI_TPH_POLICY_DS_ST = 2,\n+};\n+\n struct vfio_pci_ioeventfd {\n \tstruct list_head\tnext;\n \tstruct vfio_pci_core_device\t*vdev;\n@@ -118,6 +124,10 @@ static inline bool vfio_pci_is_vga(struct pci_dev *pdev)\n int vfio_pci_core_feature_dma_buf(struct vfio_pci_core_device *vdev, u32 flags,\n \t\t\t\t  struct vfio_device_feature_dma_buf __user *arg,\n \t\t\t\t  size_t argsz);\n+int vfio_pci_core_feature_dma_buf_tph(struct vfio_pci_core_device *vdev,\n+\t\t\t\t      u32 flags,\n+\t\t\t\t      struct vfio_device_feature_dma_buf_tph __user *arg,\n+\t\t\t\t      size_t argsz);\n void vfio_pci_dma_buf_cleanup(struct vfio_pci_core_device *vdev);\n void vfio_pci_dma_buf_move(struct vfio_pci_core_device *vdev, bool revoked);\n #else\n@@ -128,6 +138,14 @@ vfio_pci_core_feature_dma_buf(struct vfio_pci_core_device *vdev, u32 flags,\n {\n \treturn -ENOTTY;\n }\n+\n+static inline int\n+vfio_pci_core_feature_dma_buf_tph(struct vfio_pci_core_device *vdev, u32 flags,\n+\t\t\t\t  struct vfio_device_feature_dma_buf_tph __user *arg,\n+\t\t\t\t  size_t argsz)\n+{\n+\treturn -ENOTTY;\n+}\n static inline void vfio_pci_dma_buf_cleanup(struct vfio_pci_core_device *vdev)\n {\n }\ndiff --git a/include/linux/dma-buf.h b/include/linux/dma-buf.h\nindex d1203da56fc5f7..14e8eb61a62c04 100644\n--- a/include/linux/dma-buf.h\n+++ b/include/linux/dma-buf.h\n@@ -113,6 +113,29 @@ struct dma_buf_ops {\n \t */\n \tvoid (*unpin)(struct dma_buf_attachment *attach);\n \n+\t/**\n+\t * @get_pci_tph:\n+\t *\n+\t * Retrieve PCIe TLP Processing Hint (TPH) steering metadata for\n+\t * this buffer so an importer can program a matching ST/PH hint on\n+\t * outbound TLPs targeting the exporter for peer-to-peer DMA.\n+\t *\n+\t * @dmabuf: DMA buffer for which to retrieve TPH metadata\n+\t * @extended: false for 8-bit ST, true for 16-bit Extended ST\n+\t * @steering_tag: Returns the raw TPH steering tag for the requested\n+\t *                namespace\n+\t * @ph: Returns the TPH processing hint (2-bit value)\n+\t *\n+\t * Optional callback for dma_buf_get_pci_tph(). Called with\n+\t * \u0026dma_buf.resv held. Returns 0 on success or -EOPNOTSUPP when\n+\t * the exporter has no metadata for the requested namespace.\n+\t *\n+\t * Returned metadata follows the lifetime rules documented for\n+\t * dma_buf_get_pci_tph().\n+\t */\n+\tint (*get_pci_tph)(struct dma_buf *dmabuf, bool extended,\n+\t\t\t   u16 *steering_tag, u8 *ph);\n+\n \t/**\n \t * @map_dma_buf:\n \t *\n@@ -563,6 +586,8 @@ void dma_buf_detach(struct dma_buf *dmabuf,\n \t\t    struct dma_buf_attachment *attach);\n int dma_buf_pin(struct dma_buf_attachment *attach);\n void dma_buf_unpin(struct dma_buf_attachment *attach);\n+int dma_buf_get_pci_tph(struct dma_buf *dmabuf, bool extended,\n+\t\t\tu16 *steering_tag, u8 *ph);\n \n struct dma_buf *dma_buf_export(const struct dma_buf_export_info *exp_info);\n \ndiff --git a/include/linux/pci-tph.h b/include/linux/pci-tph.h\nindex be68cd17f2f833..aeaf701e7965b0 100644\n--- a/include/linux/pci-tph.h\n+++ b/include/linux/pci-tph.h\n@@ -9,6 +9,8 @@\n #ifndef LINUX_PCI_TPH_H\n #define LINUX_PCI_TPH_H\n \n+#include \u003clinux/pci.h\u003e\n+\n /*\n  * According to the ECN for PCI Firmware Spec, Steering Tag can be different\n  * depending on the memory type: Volatile Memory or Persistent Memory. When a\n@@ -21,26 +23,69 @@ enum tph_mem_type {\n };\n \n #ifdef CONFIG_PCIE_TPH\n+/**\n+ * pcie_std_tph_supported - check standard TPH requester support\n+ * @pdev: PCI device\n+ */\n+#define pcie_std_tph_supported(pdev) \\\n+\t((pdev)-\u003etph_max_type \u003e= PCI_TPH_REQ_TPH_ONLY)\n+/**\n+ * pcie_ext_tph_supported - check extended TPH requester support\n+ * @pdev: PCI device\n+ */\n+#define pcie_ext_tph_supported(pdev) \\\n+\t((pdev)-\u003etph_max_type == PCI_TPH_REQ_EXT_TPH)\n int pcie_tph_set_st_entry(struct pci_dev *pdev,\n \t\t\t  unsigned int index, u16 tag);\n-int pcie_tph_get_cpu_st(struct pci_dev *dev,\n-\t\t\tenum tph_mem_type mem_type,\n+int pcie_tph_set_st_entries(struct pci_dev *pdev, unsigned int start,\n+\t\t\t    unsigned int count, const u16 *tags);\n+int pcie_tph_get_cpu_st_ext(struct pci_dev *dev, enum tph_mem_type mem_type,\n+\t\t\t    u8 req_type, unsigned int cpu, u16 *tag);\n+int pcie_tph_get_cpu_st(struct pci_dev *dev, enum tph_mem_type mem_type,\n \t\t\tunsigned int cpu, u16 *tag);\n void pcie_disable_tph(struct pci_dev *pdev);\n-int pcie_enable_tph(struct pci_dev *pdev, int mode);\n+int pcie_enable_tph_ext(struct pci_dev *pdev, u8 mode, u8 req_type);\n+int pcie_enable_tph(struct pci_dev *pdev, u8 mode);\n u16 pcie_tph_get_st_table_size(struct pci_dev *pdev);\n u32 pcie_tph_get_st_table_loc(struct pci_dev *pdev);\n+u8 pcie_tph_enabled_req_type(struct pci_dev *pdev);\n+u8 pcie_tph_completer_type(struct pci_dev *pdev);\n+bool pcie_tph_dsm_supported(struct pci_dev *pdev);\n #else\n+#define pcie_std_tph_supported(pdev) false\n+#define pcie_ext_tph_supported(pdev) false\n static inline int pcie_tph_set_st_entry(struct pci_dev *pdev,\n \t\t\t\t\tunsigned int index, u16 tag)\n { return -EINVAL; }\n+static inline int pcie_tph_set_st_entries(struct pci_dev *pdev,\n+\t\t\t\t\t  unsigned int start,\n+\t\t\t\t\t  unsigned int count, const u16 *tags)\n+{ return -EINVAL; }\n+static inline int pcie_tph_get_cpu_st_ext(struct pci_dev *dev,\n+\t\t\t\t\t  enum tph_mem_type mem_type,\n+\t\t\t\t\t  u8 req_type, unsigned int cpu,\n+\t\t\t\t\t  u16 *tag)\n+{ return -EINVAL; }\n static inline int pcie_tph_get_cpu_st(struct pci_dev *dev,\n \t\t\t\t      enum tph_mem_type mem_type,\n \t\t\t\t      unsigned int cpu, u16 *tag)\n { return -EINVAL; }\n static inline void pcie_disable_tph(struct pci_dev *pdev) { }\n-static inline int pcie_enable_tph(struct pci_dev *pdev, int mode)\n+static inline int pcie_enable_tph_ext(struct pci_dev *pdev, u8 mode,\n+\t\t\t\t      u8 req_type)\n+{ return -EINVAL; }\n+static inline int pcie_enable_tph(struct pci_dev *pdev, u8 mode)\n { return -EINVAL; }\n+static inline u16 pcie_tph_get_st_table_size(struct pci_dev *pdev)\n+{ return 0; }\n+static inline u32 pcie_tph_get_st_table_loc(struct pci_dev *pdev)\n+{ return PCI_TPH_LOC_NONE; }\n+static inline u8 pcie_tph_enabled_req_type(struct pci_dev *pdev)\n+{ return PCI_TPH_REQ_DISABLE; }\n+static inline u8 pcie_tph_completer_type(struct pci_dev *pdev)\n+{ return PCI_EXP_DEVCAP2_TPH_COMP_NONE; }\n+static inline bool pcie_tph_dsm_supported(struct pci_dev *pdev)\n+{ return false; }\n #endif\n \n #endif /* LINUX_PCI_TPH_H */\ndiff --git a/include/linux/pci.h b/include/linux/pci.h\nindex 64b308b6e61c19..5ed58799a9b129 100644\n--- a/include/linux/pci.h\n+++ b/include/linux/pci.h\n@@ -472,7 +472,6 @@ struct pci_dev {\n \tunsigned int\tats_enabled:1;\t\t/* Address Translation Svc */\n \tunsigned int\tpasid_enabled:1;\t/* Process Address Space ID */\n \tunsigned int\tpri_enabled:1;\t\t/* Page Request Interface */\n-\tunsigned int\ttph_enabled:1;\t\t/* TLP Processing Hints */\n \tunsigned int\tfm_enabled:1;\t\t/* Flit Mode (segment captured) */\n \tunsigned int\tis_managed:1;\t\t/* Managed via devres */\n \tunsigned int\tis_msi_managed:1;\t/* MSI release via devres installed */\n@@ -588,7 +587,10 @@ struct pci_dev {\n \tu8 reset_methods[PCI_NUM_RESET_METHODS]; /* In priority order */\n \n #ifdef CONFIG_PCIE_TPH\n-\tu16\t\ttph_cap;\t/* TPH capability offset */\n+\tu16\t\ttph_cap:13;\t/* TPH capability offset */\n+\tu16\t\ttph_max_type:2; /* Negotiated max TPH requester type\n+\t\t\t\t\t   between device and root port */\n+\tu16\t\ttph_enabled:1;\t/* Whether TPH is enabled */\n \tu8\t\ttph_mode;\t/* TPH mode */\n \tu8\t\ttph_req_type;\t/* TPH requester type */\n #endif\ndiff --git a/include/linux/vfio_pci_core.h b/include/linux/vfio_pci_core.h\nindex 9a1674c152aa29..d6064fa9e80a69 100644\n--- a/include/linux/vfio_pci_core.h\n+++ b/include/linux/vfio_pci_core.h\n@@ -149,6 +149,9 @@ struct vfio_pci_core_device {\n \tstruct notifier_block\tnb;\n \tstruct rw_semaphore\tmemory_lock;\n \tstruct list_head\tdmabufs;\n+\tu8\t\t\ttph_opt_in;\n+\tu8\t\t\ttph_policy;\n+\tu32\t\t\ttph_cap_virt;\n };\n \n enum vfio_pci_io_width {\ndiff --git a/include/uapi/linux/vfio.h b/include/uapi/linux/vfio.h\nindex 5de618a3a5eeee..9bc3cef7a89ac4 100644\n--- a/include/uapi/linux/vfio.h\n+++ b/include/uapi/linux/vfio.h\n@@ -1534,6 +1534,150 @@ struct vfio_device_feature_dma_buf {\n  */\n #define VFIO_DEVICE_FEATURE_MIG_PRECOPY_INFOv2  12\n \n+/**\n+ * Upon VFIO_DEVICE_FEATURE_SET associate TPH (TLP Processing Hints) metadata\n+ * with a vfio-exported dma-buf. The dma-buf must have been created by\n+ * VFIO_DEVICE_FEATURE_DMA_BUF on this device, and the device must report\n+ * TPH Completer support in Device Capabilities 2 (bits 13:12); requests\n+ * carrying VFIO_DMA_BUF_TPH_ST_EXT additionally require the device to\n+ * report the Extended TPH Completer encoding. Otherwise the ioctl\n+ * returns -EOPNOTSUPP.\n+ *\n+ * @dmabuf_fd is the file descriptor returned by VFIO_DEVICE_FEATURE_DMA_BUF.\n+ *\n+ * 8-bit ST (@steering_tag) and 16-bit Extended ST (@steering_tag_ext) are\n+ * distinct namespaces. @flags is the authoritative validity mask for the\n+ * two namespaces: VFIO_DMA_BUF_TPH_ST marks @steering_tag valid and\n+ * VFIO_DMA_BUF_TPH_ST_EXT marks @steering_tag_ext valid. An importer\n+ * requests one namespace and receives the matching value.\n+ *\n+ * Each SET fully replaces the dma-buf's TPH state for future queries\n+ * only. A namespace whose flag bit is clear is reported as unsupported\n+ * to future importers; an importer that has already retrieved a value\n+ * is unaffected. @flags == 0 leaves both namespaces unsupported for\n+ * future importers.\n+ *\n+ * @ph is the 2-bit TLP Processing Hint and must be in the range [0, 3].\n+ *\n+ * Undefined @flags and @ph bits must always be zero.\n+ *\n+ * Userspace must configure TPH before handing the dma-buf fd to an\n+ * importer.\n+ */\n+#define VFIO_DEVICE_FEATURE_DMA_BUF_TPH 13\n+\n+#define VFIO_DMA_BUF_TPH_ST\t\t(1 \u003c\u003c 0)\n+#define VFIO_DMA_BUF_TPH_ST_EXT\t\t(1 \u003c\u003c 1)\n+\n+struct vfio_device_feature_dma_buf_tph {\n+\t__s32\tdmabuf_fd;\n+\t__u32\tflags;\n+\t__u16\tsteering_tag_ext;\n+\t__u8\tsteering_tag;\n+\t__u8\tph;\n+};\n+\n+/* PCIe TPH device feature definitions for VFIO_DEVICE_FEATURE ioctl */\n+#define VFIO_DEVICE_FEATURE_TPH\t\t14\n+#define VFIO_DEVICE_FEATURE_TPH_RESOLVE\t15\n+#define VFIO_DEVICE_FEATURE_TPH_ST\t16\n+\n+/*\n+ * VFIO_DEVICE_FEATURE_TPH - Control and query PCI TPH capabilities\n+ *\n+ * SET: Opt-in to TPH feature for the device; flags must be zero.\n+ * GET: Return supported TPH capability bits (see VFIO_DEVICE_TPH_CAP_*)\n+ *      in flags. Returns error if SET has not been invoked first.\n+ *\n+ * Userspace must first invoke SET on this feature to enable TPH support,\n+ * receive valid capability bits via GET, gain permission for using\n+ * VFIO_DEVICE_FEATURE_TPH_RESOLVE / VFIO_DEVICE_FEATURE_TPH_ST, and access\n+ * the virtualized TPH capability registers via VFIO config space accesses.\n+ */\n+struct vfio_device_feature_tph {\n+\t__u32 flags;\n+};\n+\n+/* DMABUF source resolve processing hint supported */\n+#define VFIO_DEVICE_TPH_CAP_RESOLVE_DMABUF_PH\t(1u \u003c\u003c 0)\n+/* DMABUF source TPH_ST batch programming supported */\n+#define VFIO_DEVICE_TPH_CAP_ST_DMABUF\t\t(1u \u003c\u003c 1)\n+/* CPU source TPH_ST batch programming supported */\n+#define VFIO_DEVICE_TPH_CAP_ST_CPU\t\t(1u \u003c\u003c 2)\n+/* NONE source (means steering tag is zero) TPH_ST batch programming supported */\n+#define VFIO_DEVICE_TPH_CAP_ST_NONE\t\t(1u \u003c\u003c 3)\n+/* DMABUF source resolve steering tag supported */\n+#define VFIO_DEVICE_TPH_CAP_RESOLVE_DMABUF_ST\t(1u \u003c\u003c 4)\n+/* CPU source resolve steering tag supported */\n+#define VFIO_DEVICE_TPH_CAP_RESOLVE_CPU_ST\t(1u \u003c\u003c 5)\n+\n+/*\n+ * VFIO_DEVICE_FEATURE_TPH_RESOLVE - Resolve TPH attributes from given source\n+ *\n+ * GET only\n+ *\n+ * Requires prior successful SET on VFIO_DEVICE_FEATURE_TPH, otherwise\n+ * this feature will return error.\n+ *\n+ * @flags: IN - source type selector (see VFIO_DEVICE_TPH_SRC_*)\n+ *         and steering tag namespace selector (see VFIO_DEVICE_TPH_EXTENDED)\n+ * @src:   IN - source identifier, e.g. dmabuf fd when SRC_DMABUF is set in\n+ *         flags\n+ * @valid: OUT - bitmap indicating valid output fields, see\n+ *         VFIO_DEVICE_TPH_VALID_*\n+ * @ph:    OUT - TPH processing hint (valid when VALID_PH is set in valid)\n+ * @st:    OUT - TPH steering tag (valid when VALID_ST is set in valid)\n+ */\n+struct vfio_device_feature_tph_resolve {\n+\t__u32 flags;\n+\t__u32 src;\n+\t__u8  valid;\n+#define VFIO_DEVICE_TPH_VALID_PH (1u \u003c\u003c 0) /* ph holds valid processing hint */\n+#define VFIO_DEVICE_TPH_VALID_ST (1u \u003c\u003c 1) /* st holds valid steering tag */\n+\t__u8  ph;\n+\t__u16 st;\n+};\n+\n+/*\n+ * VFIO_DEVICE_FEATURE_TPH_ST - Batch program architected steering tag\n+ * table entries\n+ * Return value: \u003e=0 = number of successfully written entries;\n+ *               \u003c0  = negative errno\n+ *\n+ * SET only\n+ *\n+ * Additional constraint: Userspace must complete two prerequisite steps\n+ * before invoking this feature:\n+ * 1. Call VFIO_DEVICE_FEATURE_TPH with SET to enable vfio TPH permission;\n+ * 2. Write the TPH control register to activate device hardware TPH logic.\n+ *\n+ * @flags: IN - source type selector (see VFIO_DEVICE_TPH_SRC_*) and\n+ *         steering tag namespace selector (see VFIO_DEVICE_TPH_EXTENDED) and\n+ *         failure policy (see VFIO_DEVICE_TPH_REQUIRE_ST)\n+ * @start: IN - first steering tag table index / MSI-X vector\n+ * @count: IN - number of contiguous entries to program\n+ * @dests: IN - user pointer to array of source u32 values\n+ */\n+struct vfio_device_feature_tph_st {\n+\t__u32 flags;\n+\t__u16 start;\n+\t__u16 count;\n+\t__aligned_u64 dests;\n+};\n+\n+/* Source holds dma-buf fd */\n+#define VFIO_DEVICE_TPH_SRC_DMABUF      (1u \u003c\u003c 0)\n+/* Use extended 16-bit steering tag namespace */\n+#define VFIO_DEVICE_TPH_EXTENDED\t(1u \u003c\u003c 1)\n+/* Source holds volatile memory CPU ID */\n+#define VFIO_DEVICE_TPH_SRC_CPU_VOLATILE\t(1u \u003c\u003c 2)\n+/* Source holds persistent memory CPU ID */\n+#define VFIO_DEVICE_TPH_SRC_CPU_PERSISTENT\t(1u \u003c\u003c 3)\n+/* TPH_ST only: set steering tag to zero */\n+#define VFIO_DEVICE_TPH_SRC_NONE\t(1u \u003c\u003c 4)\n+/* TPH_ST only: fail if resolved steering tag is zero */\n+#define VFIO_DEVICE_TPH_REQUIRE_ST\t(1u \u003c\u003c 5)\n+\n /* -------- API for Type1 VFIO IOMMU -------- */\n \n /**\n"
}

2/1 2026/07/14 16:05 agent patch-evaluator
1m Model:
gemini-3.1-pro-preview

Args:
null

Results:
{
  "EnableConfigs": [
    "PCIE_TPH",
    "VFIO_PCI",
    "PCI_P2PDMA",
    "DMA_SHARED_BUFFER"
  ],
  "FocusSymbols": [
    "vfio_pci_core_feature_tph",
    "vfio_pci_core_feature_tph_resolve",
    "vfio_pci_core_feature_tph_st",
    "vfio_pci_core_feature_dma_buf_tph",
    "vfio_pci_dma_buf_get_pci_tph",
    "dma_buf_get_pci_tph",
    "pcie_tph_get_cpu_st_ext",
    "pcie_tph_set_st_entries",
    "pcie_enable_tph_ext",
    "vfio_tph_config_write"
  ],
  "Reasoning": "The patch introduces functional changes to VFIO PCI and DMA-BUF to support PCIe TLP Processing Hints (TPH). It adds new VFIO ioctls/features for TPH configuration and resolution, which are reachable from userspace and should be fuzzed.",
  "WorthFuzzing": true
}

Instruction:
You are an expert Linux kernel maintainer.
Your job is to review a provided patch series and determine
if it makes functional changes to the kernel that should be fuzzed.

IMPORTANT: The changes have ALREADY been applied and committed as the HEAD commit in
your workspace. Do NOT rely on your internal knowledge of the kernel. You must actively
use your code access tools to examine the actual source code and confirm any assumptions.

Return WorthFuzzing=false if the patch only contains:
- Modifications to Documentation/, Kconfig files, or code comments.
- Purely decorative changes, such as logging (e.g., pr_err, printk) or tracepoints.
- Changes to numeric constants or macros that do not functionally alter execution flow.
- Code paths that are impossible to reach in virtualized environments like GCE or QEMU,
even when utilizing software-emulated hardware (e.g., usb gadget, mac80211_hwsim).

If it modifies reachable core kernel logic, drivers, or architectures, use your code search
tools to verify the code can be executed, then return WorthFuzzing=true.

When returning WorthFuzzing=true, you MUST ALSO:
1. Extract any specific kernel functions that should be heavily fuzzed into FocusSymbols.
   Avoid listing generic hot-path functions to prevent skewed test distributions.
2. Identify any specific CONFIG_ options required to properly test this new/modified feature.
   Go and look into the Kconfig files and check for ifdefs around the code, do not make assumptions.
   Do not list too generic configs (we already have them enabled). Only list those that
   specifically cover the modified code. List them in the EnableConfigs output array,
   and DO NOT add a 'CONFIG_' prefix (e.g., return "NET_IPV4" instead of "CONFIG_NET_IPV4").
Prefer calling several tools at the same time to save round-trips.


Use set-results tool to provide results of the analysis.
It must be called exactly once before the final reply.
Ignore results of this tool.

Prompt:
For your convenience, here is the diff of the changes:
commit db00c624771fc0764f7771f705c1590f7f644c79
Author: syz-cluster <triage@syzkaller.com>
Date:   Tue Jul 14 16:05:43 2026 +0000

    syz-cluster: applied patch under review

diff --git a/drivers/dma-buf/dma-buf.c b/drivers/dma-buf/dma-buf.c
index d504c636dc294e..e8314ed240d336 100644
--- a/drivers/dma-buf/dma-buf.c
+++ b/drivers/dma-buf/dma-buf.c
@@ -939,6 +939,7 @@ dma_buf_pin_on_map(struct dma_buf_attachment *attach)
  *     - dma_buf_unmap_attachment()
  *     - dma_buf_vmap()
  *     - dma_buf_vunmap()
+ *     - dma_buf_get_pci_tph()
  *
  * 2. Importers must not hold the dma-buf reservation lock when calling these
  *    functions:
@@ -979,6 +980,7 @@ dma_buf_pin_on_map(struct dma_buf_attachment *attach)
  *     - &dma_buf_ops.unmap_dma_buf()
  *     - &dma_buf_ops.vmap()
  *     - &dma_buf_ops.vunmap()
+ *     - &dma_buf_ops.get_pci_tph()
  *
  * 3. Exporters must hold the dma-buf reservation lock when calling these
  *    functions:
@@ -1144,6 +1146,36 @@ void dma_buf_unpin(struct dma_buf_attachment *attach)
 }
 EXPORT_SYMBOL_NS_GPL(dma_buf_unpin, "DMA_BUF");
 
+/**
+ * dma_buf_get_pci_tph - Retrieve PCIe TLP Processing Hint (TPH) metadata
+ * @dmabuf: DMA buffer to query
+ * @extended: false for 8-bit ST, true for 16-bit Extended ST
+ * @steering_tag: returns the raw steering tag for the requested namespace
+ * @ph: returns the TPH processing hint
+ *
+ * Wrapper for the optional &dma_buf_ops.get_pci_tph callback.
+ *
+ * Must be called with &dma_buf.resv held. Returns -EOPNOTSUPP if the
+ * exporter does not implement the callback or has no metadata for the
+ * requested namespace.
+ *
+ * The returned steering tag and processing hint are only valid until the
+ * exporter invalidates the current mapping (signalled to the importer via
+ * &dma_buf_attach_ops.move_notify). Importers must re-query after a new
+ * mapping is established following invalidation.
+ */
+int dma_buf_get_pci_tph(struct dma_buf *dmabuf, bool extended,
+			u16 *steering_tag, u8 *ph)
+{
+	dma_resv_assert_held(dmabuf->resv);
+
+	if (!dmabuf->ops->get_pci_tph)
+		return -EOPNOTSUPP;
+
+	return dmabuf->ops->get_pci_tph(dmabuf, extended, steering_tag, ph);
+}
+EXPORT_SYMBOL_NS_GPL(dma_buf_get_pci_tph, "DMA_BUF");
+
 /**
  * dma_buf_map_attachment - Returns the scatterlist table of the attachment;
  * mapped into _device_ address space. Is a wrapper for map_dma_buf() of the
diff --git a/drivers/pci/tph.c b/drivers/pci/tph.c
index 655ffd60e62f6a..e6a850ab2b037e 100644
--- a/drivers/pci/tph.c
+++ b/drivers/pci/tph.c
@@ -165,14 +165,62 @@ static u8 get_st_modes(struct pci_dev *pdev)
  */
 u32 pcie_tph_get_st_table_loc(struct pci_dev *pdev)
 {
-	u32 reg;
+	u32 reg = 0;
+
+	if (!pdev->tph_cap)
+		return PCI_TPH_LOC_NONE;
 
 	pci_read_config_dword(pdev, pdev->tph_cap + PCI_TPH_CAP, &reg);
 
-	return FIELD_GET(PCI_TPH_CAP_LOC_MASK, reg);
+	return reg & PCI_TPH_CAP_LOC_MASK;
 }
 EXPORT_SYMBOL(pcie_tph_get_st_table_loc);
 
+/**
+ * pcie_tph_enabled_req_type - Return the device's enabled TPH requester type
+ * @pdev: PCI device to query
+ *
+ * Return: PCI_TPH_REQ_DISABLE, PCI_TPH_REQ_TPH_ONLY or PCI_TPH_REQ_EXT_TPH.
+ */
+u8 pcie_tph_enabled_req_type(struct pci_dev *pdev)
+{
+	return pdev->tph_req_type;
+}
+EXPORT_SYMBOL(pcie_tph_enabled_req_type);
+
+/**
+ * pcie_tph_completer_type - Return the device's TPH Completer support
+ * @pdev: PCI device to query
+ *
+ * Reads the "TPH Completer Supported" field (bits 13:12) of Device
+ * Capabilities 2. The reserved 0b10 encoding is folded into
+ * "not supported" so callers only need to compare against the three
+ * defined values.
+ *
+ * Return: one of %PCI_EXP_DEVCAP2_TPH_COMP_NONE,
+ *         %PCI_EXP_DEVCAP2_TPH_COMP_TPH_ONLY or
+ *         %PCI_EXP_DEVCAP2_TPH_COMP_EXT_TPH.
+ */
+u8 pcie_tph_completer_type(struct pci_dev *pdev)
+{
+	u32 reg;
+
+	if (pcie_capability_read_dword(pdev, PCI_EXP_DEVCAP2, &reg))
+		return PCI_EXP_DEVCAP2_TPH_COMP_NONE;
+	if (PCI_POSSIBLE_ERROR(reg))
+		return PCI_EXP_DEVCAP2_TPH_COMP_NONE;
+
+	switch (FIELD_GET(PCI_EXP_DEVCAP2_TPH_COMP_MASK, reg)) {
+	case PCI_EXP_DEVCAP2_TPH_COMP_TPH_ONLY:
+		return PCI_EXP_DEVCAP2_TPH_COMP_TPH_ONLY;
+	case PCI_EXP_DEVCAP2_TPH_COMP_EXT_TPH:
+		return PCI_EXP_DEVCAP2_TPH_COMP_EXT_TPH;
+	default:
+		return PCI_EXP_DEVCAP2_TPH_COMP_NONE;
+	}
+}
+EXPORT_SYMBOL(pcie_tph_completer_type);
+
 /*
  * Return the size of ST table. If ST table is not in TPH Requester Extended
  * Capability space, return 0. Otherwise return the ST Table Size + 1.
@@ -184,9 +232,6 @@ u16 pcie_tph_get_st_table_size(struct pci_dev *pdev)
 
 	/* Check ST table location first */
 	loc = pcie_tph_get_st_table_loc(pdev);
-
-	/* Convert loc to match with PCI_TPH_LOC_* defined in pci_regs.h */
-	loc = FIELD_PREP(PCI_TPH_CAP_LOC_MASK, loc);
 	if (loc != PCI_TPH_LOC_CAP)
 		return 0;
 
@@ -231,20 +276,22 @@ static int write_tag_to_st_table(struct pci_dev *pdev, int index, u16 tag)
 }
 
 /**
- * pcie_tph_get_cpu_st() - Retrieve Steering Tag for a target memory associated
- * with a specific CPU
+ * pcie_tph_get_cpu_st_ext() - Retrieve Steering Tag for a target memory
+ * associated with a specific CPU, with explicit requester type selection
  * @pdev: PCI device
  * @mem_type: target memory type (volatile or persistent RAM)
+ * @req_type: TPH requester type to select ST namespace
  * @cpu: associated CPU id
  * @tag: Steering Tag to be returned
  *
- * Return the Steering Tag for a target memory that is associated with a
- * specific CPU as indicated by cpu.
+ * Return the Steering Tag for a target memory associated with a specific CPU,
+ * using the specified TPH requester type to select the correct ST namespace.
+ * Validates requester type against hardware negotiated capability limits.
  *
  * Return: 0 if success, otherwise negative value (-errno)
  */
-int pcie_tph_get_cpu_st(struct pci_dev *pdev, enum tph_mem_type mem_type,
-			unsigned int cpu, u16 *tag)
+int pcie_tph_get_cpu_st_ext(struct pci_dev *pdev, enum tph_mem_type mem_type,
+			    u8 req_type, unsigned int cpu, u16 *tag)
 {
 #ifdef CONFIG_ACPI
 	struct pci_dev *rp;
@@ -253,6 +300,10 @@ int pcie_tph_get_cpu_st(struct pci_dev *pdev, enum tph_mem_type mem_type,
 	u32 cpu_uid;
 	int ret;
 
+	if (req_type == PCI_TPH_REQ_DISABLE || req_type == 2 ||
+		req_type > pdev->tph_max_type)
+		return -EINVAL;
+
 	ret = acpi_get_cpu_uid(cpu, &cpu_uid);
 	if (ret != 0)
 		return ret;
@@ -268,35 +319,54 @@ int pcie_tph_get_cpu_st(struct pci_dev *pdev, enum tph_mem_type mem_type,
 		return -EINVAL;
 	}
 
-	*tag = tph_extract_tag(mem_type, pdev->tph_req_type, &info);
+	*tag = tph_extract_tag(mem_type, req_type, &info);
 
-	pci_dbg(pdev, "get steering tag: mem_type=%s, cpu=%d, tag=%#04x\n",
+	pci_dbg(pdev, "get steering tag: mem_type=%s, req_type=%u, cpu=%d, tag=%#04x\n",
 		(mem_type == TPH_MEM_TYPE_VM) ? "volatile" : "persistent",
-		cpu, *tag);
+		req_type, cpu, *tag);
 
 	return 0;
 #else
 	return -ENODEV;
 #endif
 }
-EXPORT_SYMBOL(pcie_tph_get_cpu_st);
+EXPORT_SYMBOL(pcie_tph_get_cpu_st_ext);
 
 /**
- * pcie_tph_set_st_entry() - Set Steering Tag in the ST table entry
+ * pcie_tph_get_cpu_st() - Retrieve Steering Tag for a target memory associated
+ * with a specific CPU
  * @pdev: PCI device
- * @index: ST table entry index
- * @tag: Steering Tag to be written
+ * @mem_type: target memory type (volatile or persistent RAM)
+ * @cpu: associated CPU id
+ * @tag: Steering Tag to be returned
  *
- * Figure out the proper location of ST table, either in the MSI-X table or
- * in the TPH Extended Capability space, and write the Steering Tag into
- * the ST entry pointed by index.
+ * Return the Steering Tag for a target memory associated with a specific CPU,
+ * using the default negotiated TPH requester type.
  *
  * Return: 0 if success, otherwise negative value (-errno)
  */
-int pcie_tph_set_st_entry(struct pci_dev *pdev, unsigned int index, u16 tag)
+int pcie_tph_get_cpu_st(struct pci_dev *pdev, enum tph_mem_type mem_type,
+			unsigned int cpu, u16 *tag)
+{
+	return pcie_tph_get_cpu_st_ext(pdev, mem_type, pdev->tph_max_type,
+				       cpu, tag);
+}
+EXPORT_SYMBOL(pcie_tph_get_cpu_st);
+
+/**
+ * pcie_tph_set_st_entries - Batch set Steering Tag entries in the ST table
+ * @pdev: PCI device
+ * @start: starting ST table index
+ * @count: number of contiguous entries to write
+ * @tags: array of u16 tag values to program
+ *
+ * Return: number of successfully written entries, or negative errno
+ */
+int pcie_tph_set_st_entries(struct pci_dev *pdev, unsigned int start,
+			    unsigned int count, const u16 *tags)
 {
-	u32 loc;
 	int err = 0;
+	u32 loc, i;
 
 	if (!pdev->tph_cap)
 		return -EINVAL;
@@ -306,7 +376,7 @@ int pcie_tph_set_st_entry(struct pci_dev *pdev, unsigned int index, u16 tag)
 
 	/* No need to write tag if device is in "No ST Mode" */
 	if (pdev->tph_mode == PCI_TPH_ST_NS_MODE)
-		return 0;
+		return count;
 
 	/*
 	 * Disable TPH before updating ST to avoid potential instability as
@@ -315,31 +385,47 @@ int pcie_tph_set_st_entry(struct pci_dev *pdev, unsigned int index, u16 tag)
 	set_ctrl_reg_req_en(pdev, PCI_TPH_REQ_DISABLE);
 
 	loc = pcie_tph_get_st_table_loc(pdev);
-	/* Convert loc to match with PCI_TPH_LOC_* */
-	loc = FIELD_PREP(PCI_TPH_CAP_LOC_MASK, loc);
-
-	switch (loc) {
-	case PCI_TPH_LOC_MSIX:
-		err = pci_msix_write_tph_tag(pdev, index, tag);
-		break;
-	case PCI_TPH_LOC_CAP:
-		err = write_tag_to_st_table(pdev, index, tag);
-		break;
-	default:
-		err = -EINVAL;
-	}
-
-	if (err) {
-		pcie_disable_tph(pdev);
-		return err;
+	for (i = 0; i < count; i++) {
+		switch (loc) {
+		case PCI_TPH_LOC_MSIX:
+			err = pci_msix_write_tph_tag(pdev, start + i, tags[i]);
+			break;
+		case PCI_TPH_LOC_CAP:
+			err = write_tag_to_st_table(pdev, start + i, tags[i]);
+			break;
+		default:
+			err = -EINVAL;
+		}
+		if (err != 0) {
+			pci_err(pdev, "failed to set steering tag: %s table, index=%u, tags=%u, ret=%d\n",
+				(loc == PCI_TPH_LOC_MSIX) ? "MSI-X" : "ST",
+				start + i, tags[i], err);
+			pcie_disable_tph(pdev);
+			return i > 0 ? i : err;
+		}
 	}
 
 	set_ctrl_reg_req_en(pdev, pdev->tph_req_type);
 
-	pci_dbg(pdev, "set steering tag: %s table, index=%d, tag=%#04x\n",
-		(loc == PCI_TPH_LOC_MSIX) ? "MSI-X" : "ST", index, tag);
+	pci_dbg(pdev, "batch set steering tag: %s table, start=%u, count=%u success!\n",
+		(loc == PCI_TPH_LOC_MSIX) ? "MSI-X" : "ST", start, count);
 
-	return 0;
+	return count;
+}
+EXPORT_SYMBOL(pcie_tph_set_st_entries);
+
+/**
+ * pcie_tph_set_st_entry() - Set Steering Tag in the ST table entry
+ * @pdev: PCI device
+ * @index: ST table entry index
+ * @tag: Steering Tag to be written
+ *
+ * Return: 0 if success, otherwise negative value (-errno)
+ */
+int pcie_tph_set_st_entry(struct pci_dev *pdev, unsigned int index, u16 tag)
+{
+	int ret = pcie_tph_set_st_entries(pdev, index, 1, &tag);
+	return ret == 1 ? 0 : ret;
 }
 EXPORT_SYMBOL(pcie_tph_set_st_entry);
 
@@ -366,26 +452,21 @@ void pcie_disable_tph(struct pci_dev *pdev)
 EXPORT_SYMBOL(pcie_disable_tph);
 
 /**
- * pcie_enable_tph - Enable TPH support for device using a specific ST mode
+ * pcie_enable_tph_ext - Enable TPH support with specific ST mode and
+ * requester type
  * @pdev: PCI device
- * @mode: ST mode to enable. Current supported modes include:
- *
- *   - PCI_TPH_ST_NS_MODE: NO ST Mode
- *   - PCI_TPH_ST_IV_MODE: Interrupt Vector Mode
- *   - PCI_TPH_ST_DS_MODE: Device Specific Mode
+ * @mode: ST mode to enable: PCI_TPH_ST_(NS_MODE / IV_MODE / DS_MODE)
+ * @req_type: Requester type to enable: PCI_TPH_REQ_(TPH_ONLY / EXT_TPH)
  *
- * Check whether the mode is actually supported by the device before enabling
- * and return an error if not. Additionally determine what types of requests,
- * TPH or extended TPH, can be issued by the device based on its TPH requester
- * capability and the Root Port's completer capability.
+ * Check whether the mode and requester type is actually supported by the
+ * device before enabling and return an error if not.
  *
  * Return: 0 on success, otherwise negative value (-errno)
  */
-int pcie_enable_tph(struct pci_dev *pdev, int mode)
+int pcie_enable_tph_ext(struct pci_dev *pdev, u8 mode, u8 req_type)
 {
 	u32 reg;
 	u8 dev_modes;
-	u8 rp_req_type;
 
 	/* Honor "notph" kernel parameter */
 	if (pci_tph_disabled)
@@ -397,6 +478,14 @@ int pcie_enable_tph(struct pci_dev *pdev, int mode)
 	if (pdev->tph_enabled)
 		return -EBUSY;
 
+	if (pdev->tph_max_type == PCI_TPH_REQ_DISABLE)
+		return -EINVAL;
+
+	/* Sanitize and check TPH requester type compatibility */
+	if (req_type == PCI_TPH_REQ_DISABLE || req_type == 2 ||
+		req_type > pdev->tph_max_type)
+		return -EINVAL;
+
 	/* Sanitize and check ST mode compatibility */
 	mode &= PCI_TPH_CTRL_MODE_SEL_MASK;
 	dev_modes = get_st_modes(pdev);
@@ -405,23 +494,7 @@ int pcie_enable_tph(struct pci_dev *pdev, int mode)
 
 	pdev->tph_mode = mode;
 
-	/* Get req_type supported by device and its Root Port */
-	pci_read_config_dword(pdev, pdev->tph_cap + PCI_TPH_CAP, &reg);
-	if (FIELD_GET(PCI_TPH_CAP_EXT_TPH, reg))
-		pdev->tph_req_type = PCI_TPH_REQ_EXT_TPH;
-	else
-		pdev->tph_req_type = PCI_TPH_REQ_TPH_ONLY;
-
-	/* Check if the device is behind a Root Port */
-	if (pci_pcie_type(pdev) != PCI_EXP_TYPE_RC_END) {
-		rp_req_type = get_rp_completer_type(pdev);
-
-		/* Final req_type is the smallest value of two */
-		pdev->tph_req_type = min(pdev->tph_req_type, rp_req_type);
-	}
-
-	if (pdev->tph_req_type == PCI_TPH_REQ_DISABLE)
-		return -EINVAL;
+	pdev->tph_req_type = req_type;
 
 	/* Write them into TPH control register */
 	pci_read_config_dword(pdev, pdev->tph_cap + PCI_TPH_CTRL, &reg);
@@ -435,6 +508,23 @@ int pcie_enable_tph(struct pci_dev *pdev, int mode)
 
 	return 0;
 }
+EXPORT_SYMBOL(pcie_enable_tph_ext);
+
+/**
+ * pcie_enable_tph - Enable TPH support for device using a specific ST mode
+ * @pdev: PCI device
+ * @mode: ST mode to enable: PCI_TPH_ST_(NS_MODE / IV_MODE / DS_MODE)
+ *
+ * Wrapper for pcie_enable_tph_ext(), automatically uses the pre-cached
+ * tph_max_type value (negotiated maximum requester type between device and
+ * root port).
+ *
+ * Return: 0 on success, otherwise negative value (-errno)
+ */
+int pcie_enable_tph(struct pci_dev *pdev, u8 mode)
+{
+	return pcie_enable_tph_ext(pdev, mode, pdev->tph_max_type);
+}
 EXPORT_SYMBOL(pcie_enable_tph);
 
 void pci_restore_tph_state(struct pci_dev *pdev)
@@ -508,14 +598,60 @@ void pci_no_tph(void)
 
 void pci_tph_init(struct pci_dev *pdev)
 {
+	u8 tph_req_type, rp_req_type;
 	int num_entries;
 	u32 save_size;
+	u32 reg = 0;
 
 	pdev->tph_cap = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_TPH);
 	if (!pdev->tph_cap)
 		return;
 
+	/* Get req_type supported by device and its Root Port */
+	pci_read_config_dword(pdev, pdev->tph_cap + PCI_TPH_CAP, &reg);
+	if (FIELD_GET(PCI_TPH_CAP_EXT_TPH, reg))
+		tph_req_type = PCI_TPH_REQ_EXT_TPH;
+	else
+		tph_req_type = PCI_TPH_REQ_TPH_ONLY;
+
+	/* Check if the device is behind a Root Port */
+	if (pci_pcie_type(pdev) != PCI_EXP_TYPE_RC_END) {
+		rp_req_type = get_rp_completer_type(pdev);
+		/* Final req_type is the smallest value of two */
+		tph_req_type = min(tph_req_type, rp_req_type);
+	}
+
+	pdev->tph_max_type = tph_req_type;
+
 	num_entries = pcie_tph_get_st_table_size(pdev);
 	save_size = sizeof(u32) + num_entries * sizeof(u16);
 	pci_add_ext_cap_save_buffer(pdev, PCI_EXT_CAP_ID_TPH, save_size);
 }
+
+/**
+ * pcie_tph_dsm_supported - Check if root port exposes TPH ST _DSM
+ * @pdev: target PCI device
+ *
+ * Return true if root port of @pdev provides TPH ST DSM function,
+ * false otherwise (ACPI disabled or DSM missing).
+ */
+bool pcie_tph_dsm_supported(struct pci_dev *pdev)
+{
+#ifdef CONFIG_ACPI
+	struct pci_dev *rp = pcie_find_root_port(pdev);
+	acpi_handle handle;
+
+	if (!rp || !rp->bus || !rp->bus->bridge)
+		return false;
+
+	handle = ACPI_HANDLE(rp->bus->bridge);
+	if (!handle)
+		return false;
+
+	return acpi_check_dsm(handle, &pci_acpi_dsm_guid, 7,
+			      BIT(TPH_ST_DSM_FUNC_INDEX));
+#else
+	return false;
+#endif
+}
+EXPORT_SYMBOL_GPL(pcie_tph_dsm_supported);
diff --git a/drivers/vfio/pci/vfio_pci.c b/drivers/vfio/pci/vfio_pci.c
index 830369ff878dbe..c407c217b649fa 100644
--- a/drivers/vfio/pci/vfio_pci.c
+++ b/drivers/vfio/pci/vfio_pci.c
@@ -60,6 +60,26 @@ static bool disable_denylist;
 module_param(disable_denylist, bool, 0444);
 MODULE_PARM_DESC(disable_denylist, "Disable use of device denylist. Disabling the denylist allows binding to devices with known errata that may lead to exploitable stability or security issues when accessed by untrusted users.");
 
+static unsigned int tph_policy;
+static int tph_policy_set(const char *val, const struct kernel_param *kp)
+{
+	return param_set_uint_minmax(val, kp, VFIO_PCI_TPH_POLICY_NO_ST,
+				     VFIO_PCI_TPH_POLICY_DS_ST);
+}
+static const struct kernel_param_ops tph_param_ops = {
+	.set = tph_policy_set,
+	.get = param_get_uint,
+};
+module_param_cb(tph_policy, &tph_param_ops, &tph_policy, 0644);
+MODULE_PARM_DESC(tph_policy,
+	"Global TPH policy level (0=default No-ST):\n"
+	"0 = No-ST mode: RESOLVE only returns PH for DMABUF source;\n"
+	"                ST programming unavailable\n"
+	"1 = Add IV mode: RESOLVE only returns PH for DMABUF source;\n"
+	"                 ST program supports NONE/DMABUF/CPU sources\n"
+	"2 = Add DS mode: RESOLVE returns PH for DMABUF source, ST for DMABUF/CPU sources;\n"
+	"                 ST program supports NONE/DMABUF/CPU sources\n");
+
 static bool vfio_pci_dev_in_denylist(struct pci_dev *pdev)
 {
 	switch (pdev->vendor) {
@@ -142,6 +162,9 @@ static int vfio_pci_init_dev(struct vfio_device *core_vdev)
 #ifdef CONFIG_VFIO_PCI_VGA
 	vdev->disable_vga = disable_vga;
 #endif
+#ifdef CONFIG_PCIE_TPH
+	vdev->tph_policy = tph_policy;
+#endif
 
 	return vfio_pci_core_init_dev(core_vdev);
 }
diff --git a/drivers/vfio/pci/vfio_pci_config.c b/drivers/vfio/pci/vfio_pci_config.c
index a10ed733f0e3af..97dad6db69e39d 100644
--- a/drivers/vfio/pci/vfio_pci_config.c
+++ b/drivers/vfio/pci/vfio_pci_config.c
@@ -22,6 +22,7 @@
 
 #include <linux/fs.h>
 #include <linux/pci.h>
+#include <linux/pci-tph.h>
 #include <linux/uaccess.h>
 #include <linux/vfio.h>
 #include <linux/slab.h>
@@ -1085,6 +1086,146 @@ static int __init init_pci_ext_cap_pwr_perm(struct perm_bits *perm)
 	return 0;
 }
 
+static int vfio_find_cap_start(struct vfio_pci_core_device *vdev, int pos)
+{
+	u8 cap;
+	int base = (pos >= PCI_CFG_SPACE_SIZE) ? PCI_CFG_SPACE_SIZE :
+						 PCI_STD_HEADER_SIZEOF;
+	cap = vdev->pci_config_map[pos];
+
+	if (cap == PCI_CAP_ID_BASIC)
+		return 0;
+
+	/* XXX Can we have to abutting capabilities of the same type? */
+	while (pos - 1 >= base && vdev->pci_config_map[pos - 1] == cap)
+		pos--;
+
+	return pos;
+}
+
+static void vfio_update_tph_vconfig_bytes(struct vfio_pci_core_device *vdev,
+					  int pos)
+{
+	__le32 *vptr = (__le32 *)&vdev->vconfig[pos + PCI_TPH_CAP];
+	struct pci_dev *pdev = vdev->pdev;
+	u32 val = le32_to_cpu(*vptr);
+
+	if (!pcie_ext_tph_supported(pdev)) {
+		/* Remove extended TPH if root-port doesn't support */
+		val &= ~PCI_TPH_CAP_EXT_TPH;
+	}
+
+	if (vdev->tph_policy == VFIO_PCI_TPH_POLICY_NO_ST) {
+		/* Report only No-ST mode supported */
+		val &= ~(PCI_TPH_CAP_ST_IV | PCI_TPH_CAP_ST_DS |
+			 PCI_TPH_CAP_LOC_MASK | PCI_TPH_CAP_ST_MASK);
+	} else if (vdev->tph_policy == VFIO_PCI_TPH_POLICY_IV_ST) {
+		/* Report only No-ST and IV modes supported */
+		val &= ~PCI_TPH_CAP_ST_DS;
+		/* Remove ST location and size if dev doesn't support IV mode */
+		if (!(val & PCI_TPH_CAP_ST_IV))
+			val &= ~(PCI_TPH_CAP_LOC_MASK | PCI_TPH_CAP_ST_MASK);
+	}
+
+	*vptr = cpu_to_le32(val);
+
+	/* Backup updated capability register value */
+	vdev->tph_cap_virt = val;
+}
+
+/* Permissions for TPH extended capability */
+static int __init init_pci_ext_cap_tph_perm(struct perm_bits *perm)
+{
+	/*
+	 * We only virtualize the TPH header (excluding the ST table).
+	 * For the ST table (if any), the read operation returns 0 by
+	 * default, and the write operation is discarded by default.
+	 */
+	if (alloc_perm_bits(perm, PCI_TPH_BASE_SIZEOF))
+		return -ENOMEM;
+
+	p_setd(perm, 0, ALL_VIRT, NO_WRITE);
+	p_setd(perm, PCI_TPH_CAP, ALL_VIRT, NO_WRITE);
+
+	p_setd(perm, PCI_TPH_CTRL, ALL_VIRT,
+	       PCI_TPH_CTRL_MODE_SEL_MASK | PCI_TPH_CTRL_REQ_EN_MASK);
+
+	return 0;
+}
+
+static int vfio_tph_config_read(struct vfio_pci_core_device *vdev, int pos,
+				int count, struct perm_bits *perm,
+				int offset, __le32 *val)
+{
+	if (offset >= PCI_TPH_BASE_SIZEOF) {
+		*val = cpu_to_le32(0);
+		return count;
+	}
+
+	if (offset >= PCI_TPH_CAP && !vdev->tph_opt_in) {
+		*val = cpu_to_le32(0);
+		return count;
+	}
+
+	return vfio_default_config_read(vdev, pos, count, perm, offset, val);
+}
+
+static int vfio_tph_config_write(struct vfio_pci_core_device *vdev, int pos,
+				 int count, struct perm_bits *perm,
+				 int offset, __le32 val)
+{
+	u16 start = vfio_find_cap_start(vdev, pos);
+	struct pci_dev *pdev = vdev->pdev;
+	u32 org_ctrl, new_ctrl, cap;
+	u8 mode, req, org_req;
+	__le32 org_val = 0;
+	u16 cmd;
+	int ret;
+
+	if (!vdev->tph_opt_in || offset >= PCI_TPH_BASE_SIZEOF)
+		return count;
+
+	cmd = vfio_pci_memory_lock_and_enable(vdev);
+
+	org_ctrl = le32_to_cpu(*(__le32 *)&vdev->vconfig[start + PCI_TPH_CTRL]);
+	vfio_default_config_read(vdev, pos, count, perm, offset, &org_val);
+
+	ret = vfio_default_config_write(vdev, pos, count, perm, offset, val);
+	if (ret != count)
+		goto out;
+
+	new_ctrl = le32_to_cpu(*(__le32 *)&vdev->vconfig[start + PCI_TPH_CTRL]);
+	if (new_ctrl == org_ctrl)
+		goto out; /* Only care about changes in TPH_CTRL. */
+
+	cap = le32_to_cpu(*(__le32 *)&vdev->vconfig[start + PCI_TPH_CAP]);
+	mode = FIELD_GET(PCI_TPH_CTRL_MODE_SEL_MASK, new_ctrl);
+	req = FIELD_GET(PCI_TPH_CTRL_REQ_EN_MASK, new_ctrl);
+	if (mode > PCI_TPH_ST_DS_MODE || !(cap & (1u << mode)) || req == 0x2 ||
+		(req == PCI_TPH_REQ_EXT_TPH && !(cap & PCI_TPH_CAP_EXT_TPH)))
+		goto restore; /* Drop invalid or unsupported write value */
+
+	org_req = FIELD_GET(PCI_TPH_CTRL_REQ_EN_MASK, org_ctrl);
+	if (req == org_req)
+		goto restore; /* Only care about requester enable */
+
+	if (req == PCI_TPH_REQ_TPH_ONLY || req == PCI_TPH_REQ_EXT_TPH) {
+		ret = pcie_enable_tph_ext(pdev, mode, req);
+		if (ret)
+			goto restore;
+	} else if (req == PCI_TPH_REQ_DISABLE) {
+		pcie_disable_tph(vdev->pdev);
+	}
+
+	goto out;
+
+restore:
+	vfio_default_config_write(vdev, pos, count, perm, offset, org_val);
+out:
+	vfio_pci_memory_unlock_and_restore(vdev, cmd);
+	return count;
+}
+
 /*
  * Initialize the shared permission tables
  */
@@ -1100,6 +1241,7 @@ void vfio_pci_uninit_perm_bits(void)
 
 	free_perm_bits(&ecap_perms[PCI_EXT_CAP_ID_ERR]);
 	free_perm_bits(&ecap_perms[PCI_EXT_CAP_ID_PWR]);
+	free_perm_bits(&ecap_perms[PCI_EXT_CAP_ID_TPH]);
 }
 
 int __init vfio_pci_init_perm_bits(void)
@@ -1120,6 +1262,9 @@ int __init vfio_pci_init_perm_bits(void)
 	/* Extended capabilities */
 	ret |= init_pci_ext_cap_err_perm(&ecap_perms[PCI_EXT_CAP_ID_ERR]);
 	ret |= init_pci_ext_cap_pwr_perm(&ecap_perms[PCI_EXT_CAP_ID_PWR]);
+	ret |= init_pci_ext_cap_tph_perm(&ecap_perms[PCI_EXT_CAP_ID_TPH]);
+	ecap_perms[PCI_EXT_CAP_ID_TPH].readfn = vfio_tph_config_read;
+	ecap_perms[PCI_EXT_CAP_ID_TPH].writefn = vfio_tph_config_write;
 	ecap_perms[PCI_EXT_CAP_ID_VNDR].writefn = vfio_raw_config_write;
 	ecap_perms[PCI_EXT_CAP_ID_DVSEC].writefn = vfio_raw_config_write;
 
@@ -1129,23 +1274,6 @@ int __init vfio_pci_init_perm_bits(void)
 	return ret;
 }
 
-static int vfio_find_cap_start(struct vfio_pci_core_device *vdev, int pos)
-{
-	u8 cap;
-	int base = (pos >= PCI_CFG_SPACE_SIZE) ? PCI_CFG_SPACE_SIZE :
-						 PCI_STD_HEADER_SIZEOF;
-	cap = vdev->pci_config_map[pos];
-
-	if (cap == PCI_CAP_ID_BASIC)
-		return 0;
-
-	/* XXX Can we have to abutting capabilities of the same type? */
-	while (pos - 1 >= base && vdev->pci_config_map[pos - 1] == cap)
-		pos--;
-
-	return pos;
-}
-
 static int vfio_msi_config_read(struct vfio_pci_core_device *vdev, int pos,
 				int count, struct perm_bits *perm,
 				int offset, __le32 *val)
@@ -1701,6 +1829,8 @@ static int vfio_ecap_init(struct vfio_pci_core_device *vdev)
 		ret = vfio_fill_vconfig_bytes(vdev, epos, len);
 		if (ret)
 			return ret;
+		if (ecap == PCI_EXT_CAP_ID_TPH)
+			vfio_update_tph_vconfig_bytes(vdev, epos);
 
 		/*
 		 * If we're just using this capability to anchor the list,
diff --git a/drivers/vfio/pci/vfio_pci_core.c b/drivers/vfio/pci/vfio_pci_core.c
index 3f11a9624b9c03..b8d9d840a93b57 100644
--- a/drivers/vfio/pci/vfio_pci_core.c
+++ b/drivers/vfio/pci/vfio_pci_core.c
@@ -13,6 +13,8 @@
 #include <linux/aperture.h>
 #include <linux/debugfs.h>
 #include <linux/device.h>
+#include <linux/dma-buf.h>
+#include <linux/dma-resv.h>
 #include <linux/eventfd.h>
 #include <linux/file.h>
 #include <linux/interrupt.h>
@@ -30,6 +32,7 @@
 #include <linux/sched/mm.h>
 #include <linux/iommufd.h>
 #include <linux/pci-p2pdma.h>
+#include <linux/pci-tph.h>
 #include <linux/seq_file.h>
 #if IS_ENABLED(CONFIG_EEH)
 #include <asm/eeh.h>
@@ -127,10 +130,20 @@ static int vfio_pci_core_debugfs_disable_idle_d3(struct seq_file *seq,
 	return 0;
 }
 
+static int vfio_pci_core_debugfs_tph(struct seq_file *seq,
+					    void *data)
+{
+	struct vfio_pci_core_device *vdev = vfio_pci_core_debugfs_private(seq);
+
+	seq_printf(seq, "policy=%u cap-virt=0x%x opt-in=%u\n",
+		vdev->tph_policy, vdev->tph_cap_virt, vdev->tph_opt_in);
+	return 0;
+}
+
 /*
- * disable_idle_d3 and nointxmask are writable module parameters latched
- * per device at init, so a device's effective value can differ from the
- * current parameter setting.  Expose the per-device (read-only) values
+ * disable_idle_d3, nointxmask and tph_policy are writable module parameters
+ * latched per device at init, so a device's effective value can differ from
+ * the current parameter setting.  Expose the per-device (read-only) values
  * here for visibility; read-only parameters can't drift and are omitted.
  */
 static void vfio_pci_core_debugfs_init(struct vfio_pci_core_device *vdev)
@@ -146,6 +159,8 @@ static void vfio_pci_core_debugfs_init(struct vfio_pci_core_device *vdev)
 				    vfio_pci_core_debugfs_nointxmask);
 	debugfs_create_devm_seqfile(dev, "disable_idle_d3", pci_dir,
 				    vfio_pci_core_debugfs_disable_idle_d3);
+	debugfs_create_devm_seqfile(dev, "tph", pci_dir,
+				    vfio_pci_core_debugfs_tph);
 }
 #else
 static inline void vfio_pci_core_debugfs_init(struct vfio_pci_core_device *vdev)
@@ -610,6 +625,9 @@ int vfio_pci_core_enable(struct vfio_pci_core_device *vdev)
 		goto out_disable_device;
 
 	vdev->reset_works = !ret;
+	vdev->tph_opt_in = false;
+	/* Reset TPH status on new user session */
+	pcie_disable_tph(vdev->pdev);
 	pci_save_state(pdev);
 	vdev->pci_saved_state = pci_store_saved_state(pdev);
 	if (!vdev->pci_saved_state)
@@ -757,6 +775,9 @@ void vfio_pci_core_disable(struct vfio_pci_core_device *vdev)
 
 	vfio_pci_zdev_close_device(vdev);
 
+	/* Reset TPH status on exit user session */
+	pcie_disable_tph(vdev->pdev);
+
 	/*
 	 * If we have saved state, restore it.  If we can reset the device,
 	 * even better.  Resetting with current state seems better than
@@ -1558,6 +1579,7 @@ long vfio_pci_core_ioctl(struct vfio_device *core_vdev, unsigned int cmd,
 	struct vfio_pci_core_device *vdev =
 		container_of(core_vdev, struct vfio_pci_core_device, vdev);
 	void __user *uarg = (void __user *)arg;
+	int ret;
 
 	switch (cmd) {
 	case VFIO_DEVICE_GET_INFO:
@@ -1569,9 +1591,15 @@ long vfio_pci_core_ioctl(struct vfio_device *core_vdev, unsigned int cmd,
 	case VFIO_DEVICE_IOEVENTFD:
 		return vfio_pci_ioctl_ioeventfd(vdev, uarg);
 	case VFIO_DEVICE_PCI_HOT_RESET:
-		return vfio_pci_ioctl_pci_hot_reset(vdev, uarg);
+		ret = vfio_pci_ioctl_pci_hot_reset(vdev, uarg);
+		if (!ret)
+			pcie_disable_tph(vdev->pdev);
+		return ret;
 	case VFIO_DEVICE_RESET:
-		return vfio_pci_ioctl_reset(vdev, uarg);
+		ret = vfio_pci_ioctl_reset(vdev, uarg);
+		if (!ret)
+			pcie_disable_tph(vdev->pdev);
+		return ret;
 	case VFIO_DEVICE_SET_IRQS:
 		return vfio_pci_ioctl_set_irqs(vdev, uarg);
 	default:
@@ -1607,6 +1635,299 @@ static int vfio_pci_core_feature_token(struct vfio_pci_core_device *vdev,
 	return 0;
 }
 
+static u32 vfio_pci_get_tph_st_size(struct vfio_pci_core_device *vdev)
+{
+	struct pci_dev *pdev = vdev->pdev;
+	u32 loc = pcie_tph_get_st_table_loc(pdev);
+	int ret;
+
+	if (loc == PCI_TPH_LOC_CAP) {
+		return pcie_tph_get_st_table_size(pdev);
+	} else if (loc == PCI_TPH_LOC_MSIX) {
+		ret = pci_msix_vec_count(pdev);
+		if (ret < 0)
+			return 0;
+		return ret;
+	} else {
+		return 0;
+	}
+}
+
+static u32 vfio_pci_get_tph_capability(struct vfio_pci_core_device *vdev)
+{
+	u32 flags = VFIO_DEVICE_TPH_CAP_RESOLVE_DMABUF_PH;
+
+	if (vdev->tph_policy >= VFIO_PCI_TPH_POLICY_IV_ST &&
+		(vdev->tph_cap_virt & PCI_TPH_CAP_ST_IV)) {
+		flags |= VFIO_DEVICE_TPH_CAP_ST_NONE;
+		flags |= VFIO_DEVICE_TPH_CAP_ST_DMABUF;
+		if (pcie_tph_dsm_supported(vdev->pdev))
+			flags |= VFIO_DEVICE_TPH_CAP_ST_CPU;
+	}
+	if (vdev->tph_policy >= VFIO_PCI_TPH_POLICY_DS_ST &&
+		(vdev->tph_cap_virt & PCI_TPH_CAP_ST_DS)) {
+		if (vfio_pci_get_tph_st_size(vdev) > 0) {
+			flags |= VFIO_DEVICE_TPH_CAP_ST_NONE;
+			flags |= VFIO_DEVICE_TPH_CAP_ST_DMABUF;
+			if (pcie_tph_dsm_supported(vdev->pdev))
+				flags |= VFIO_DEVICE_TPH_CAP_ST_CPU;
+		}
+		if (pcie_tph_dsm_supported(vdev->pdev))
+			flags |= VFIO_DEVICE_TPH_CAP_RESOLVE_CPU_ST;
+	}
+
+	return flags;
+}
+
+static int vfio_pci_core_feature_tph(struct vfio_pci_core_device *vdev,
+				     u32 flags,
+				     struct vfio_device_feature_tph __user *arg,
+				     size_t argsz)
+{
+	struct vfio_device_feature_tph tph;
+	int ret;
+
+	if (!pcie_std_tph_supported(vdev->pdev))
+		return -EOPNOTSUPP;
+
+	ret = vfio_check_feature(flags, argsz,
+			VFIO_DEVICE_FEATURE_GET | VFIO_DEVICE_FEATURE_SET,
+			sizeof(tph));
+	if (ret != 1)
+		return ret;
+
+	if (copy_from_user(&tph, arg, sizeof(tph)))
+		return -EFAULT;
+
+	if (flags & VFIO_DEVICE_FEATURE_SET) {
+		if (tph.flags != 0)
+			return -EINVAL;
+		vdev->tph_opt_in = 1;
+		return 0;
+	}
+
+	if (!vdev->tph_opt_in)
+		return -EINVAL;
+
+	tph.flags = vfio_pci_get_tph_capability(vdev);
+
+	return copy_to_user(arg, &tph, sizeof(tph)) ? -EFAULT : 0;
+}
+
+static u32 vfio_pci_get_tph_resolve_allow_src(struct vfio_pci_core_device *vdev)
+{
+	u32 flags = vfio_pci_get_tph_capability(vdev);
+	u32 allow_src = 0;
+
+	if (flags & (VFIO_DEVICE_TPH_CAP_RESOLVE_DMABUF_PH |
+		     VFIO_DEVICE_TPH_CAP_RESOLVE_DMABUF_ST))
+		allow_src |= VFIO_DEVICE_TPH_SRC_DMABUF;
+	if (flags & VFIO_DEVICE_TPH_CAP_RESOLVE_CPU_ST)
+		allow_src |= VFIO_DEVICE_TPH_SRC_CPU_VOLATILE |
+			     VFIO_DEVICE_TPH_SRC_CPU_PERSISTENT;
+
+	return allow_src;
+}
+
+static int vfio_pci_get_dmabuf_tph(int fd, bool extended, u16 *st, u8 *ph)
+{
+	struct dma_buf *dmabuf;
+	int ret;
+
+	dmabuf = dma_buf_get(fd);
+	if (IS_ERR(dmabuf))
+		return PTR_ERR(dmabuf);
+
+	ret = dma_resv_lock_interruptible(dmabuf->resv, NULL);
+	if (ret == 0)
+		ret = dma_buf_get_pci_tph(dmabuf, extended, st, ph);
+	dma_resv_unlock(dmabuf->resv);
+
+	dma_buf_put(dmabuf);
+	return ret;
+}
+
+static int vfio_pci_get_tph_st_tag(struct pci_dev *pdev, u32 src_bits,
+				   u32 src_hndl, bool extended, u16 *st)
+{
+	u8 req_type = extended ? PCI_TPH_REQ_EXT_TPH : PCI_TPH_REQ_TPH_ONLY;
+	int ret = 0;
+	u8 ph;
+
+	if (src_bits & VFIO_DEVICE_TPH_SRC_DMABUF)
+		ret = vfio_pci_get_dmabuf_tph(src_hndl, extended, st, &ph);
+	else if (src_bits & VFIO_DEVICE_TPH_SRC_CPU_VOLATILE)
+		ret = pcie_tph_get_cpu_st_ext(pdev, TPH_MEM_TYPE_VM,
+					      req_type, src_hndl, st);
+	else if (src_bits & VFIO_DEVICE_TPH_SRC_CPU_PERSISTENT)
+		ret = pcie_tph_get_cpu_st_ext(pdev, TPH_MEM_TYPE_PM,
+					      req_type, src_hndl, st);
+	else if (src_bits & VFIO_DEVICE_TPH_SRC_NONE)
+		*st = 0;
+	else
+		ret = -EINVAL;
+
+	if (ret != 0)
+		*st = 0;
+
+	return ret;
+}
+
+static int vfio_pci_core_feature_tph_resolve(struct vfio_pci_core_device *vdev,
+			u32 flags,
+			struct vfio_device_feature_tph_resolve __user *arg,
+			size_t argsz)
+{
+	struct vfio_device_feature_tph_resolve resolve;
+	u32 user_flags, allow_src;
+	bool extended;
+	u16 tag = 0;
+	u8 ph = 0;
+	int ret;
+
+	if (!vdev->tph_opt_in)
+		return -EOPNOTSUPP;
+
+	ret = vfio_check_feature(flags, argsz, VFIO_DEVICE_FEATURE_GET,
+				 sizeof(resolve));
+	if (ret != 1)
+		return ret;
+
+	if (copy_from_user(&resolve, arg, sizeof(resolve)))
+		return -EFAULT;
+
+	user_flags = resolve.flags;
+	extended = !!(user_flags & VFIO_DEVICE_TPH_EXTENDED);
+	if (extended && !pcie_ext_tph_supported(vdev->pdev))
+		return -EOPNOTSUPP;
+	user_flags &= ~VFIO_DEVICE_TPH_EXTENDED;
+
+	allow_src = vfio_pci_get_tph_resolve_allow_src(vdev);
+	if (user_flags & ~allow_src || !is_power_of_2(user_flags))
+		return -EINVAL;
+
+	resolve.valid = 0;
+	resolve.ph = 0;
+	resolve.st = 0;
+
+	if (user_flags & VFIO_DEVICE_TPH_SRC_DMABUF) {
+		ret = vfio_pci_get_dmabuf_tph(resolve.src, extended,
+					      &tag, &ph);
+		if (ret)
+			return ret;
+		resolve.ph = ph;
+		resolve.valid = VFIO_DEVICE_TPH_VALID_PH;
+		if (vdev->tph_policy >= VFIO_PCI_TPH_POLICY_DS_ST) {
+			resolve.st = tag;
+			resolve.valid |= VFIO_DEVICE_TPH_VALID_ST;
+		}
+		goto out;
+	}
+
+	ret = vfio_pci_get_tph_st_tag(vdev->pdev, user_flags, resolve.src,
+				      extended, &tag);
+	if (ret)
+		return ret;
+	resolve.valid |= VFIO_DEVICE_TPH_VALID_ST;
+	resolve.st = tag;
+
+out:
+	return copy_to_user(arg, &resolve, sizeof(resolve)) ? -EFAULT : 0;
+}
+
+static u32 vfio_pci_get_tph_st_allow_src(struct vfio_pci_core_device *vdev)
+{
+	u32 flags = vfio_pci_get_tph_capability(vdev);
+	u32 allow_src = 0;
+
+	if (flags & VFIO_DEVICE_TPH_CAP_ST_DMABUF)
+		allow_src |= VFIO_DEVICE_TPH_SRC_DMABUF;
+	if (flags & VFIO_DEVICE_TPH_CAP_ST_CPU)
+		allow_src |= VFIO_DEVICE_TPH_SRC_CPU_VOLATILE |
+			     VFIO_DEVICE_TPH_SRC_CPU_PERSISTENT;
+	if (flags & VFIO_DEVICE_TPH_CAP_ST_NONE)
+		allow_src |= VFIO_DEVICE_TPH_SRC_NONE;
+
+	return allow_src;
+}
+
+static int vfio_pci_core_feature_tph_st(struct vfio_pci_core_device *vdev,
+				u32 flags,
+				struct vfio_device_feature_tph_st __user *arg,
+				size_t argsz)
+{
+	struct vfio_device_feature_tph_st tph_st;
+	u32 user_flags, allow_src, st_size;
+	struct pci_dev *pdev = vdev->pdev;
+	bool extended, stop_on_zero;
+	u32 *src_hndl = NULL;
+	u16 *tags = NULL;
+	int ret, i;
+	u16 cmd;
+
+	if (!vdev->tph_opt_in || vdev->tph_policy == VFIO_PCI_TPH_POLICY_NO_ST)
+		return -EOPNOTSUPP;
+
+	ret = vfio_check_feature(flags, argsz, VFIO_DEVICE_FEATURE_SET,
+				 sizeof(tph_st));
+	if (ret != 1)
+		return ret;
+
+	if (copy_from_user(&tph_st, arg, sizeof(tph_st)))
+		return -EFAULT;
+
+	user_flags = tph_st.flags;
+	extended = !!(user_flags & VFIO_DEVICE_TPH_EXTENDED);
+	if (extended && !pcie_ext_tph_supported(vdev->pdev))
+		return -EOPNOTSUPP;
+	user_flags &= ~VFIO_DEVICE_TPH_EXTENDED;
+
+	stop_on_zero = !!(user_flags & VFIO_DEVICE_TPH_REQUIRE_ST);
+	if (stop_on_zero && (user_flags & VFIO_DEVICE_TPH_SRC_NONE))
+		return -EINVAL;
+	user_flags &= ~VFIO_DEVICE_TPH_REQUIRE_ST;
+
+	allow_src = vfio_pci_get_tph_st_allow_src(vdev);
+	if (user_flags & ~allow_src || !is_power_of_2(user_flags))
+		return -EINVAL;
+
+	st_size = vfio_pci_get_tph_st_size(vdev);
+	if (tph_st.start >= st_size || tph_st.count > st_size - tph_st.start ||
+		tph_st.count == 0)
+		return -EINVAL;
+
+	if (!(user_flags & VFIO_DEVICE_TPH_SRC_NONE)) {
+		src_hndl = memdup_array_user(u64_to_user_ptr(tph_st.dests),
+					     tph_st.count, sizeof(u32));
+		if (IS_ERR(src_hndl))
+			return PTR_ERR(src_hndl);
+	}
+	tags = kcalloc(tph_st.count, sizeof(u16), GFP_KERNEL_ACCOUNT);
+	if (!tags) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	for (i = 0; i < tph_st.count; i++) {
+		ret = vfio_pci_get_tph_st_tag(pdev, user_flags,
+					      src_hndl ? src_hndl[i] : 0,
+					      extended, &tags[i]);
+		if (ret || (stop_on_zero && tags[i] == 0))
+			break;
+	}
+
+	if (i > 0) {
+		cmd = vfio_pci_memory_lock_and_enable(vdev);
+		ret = pcie_tph_set_st_entries(pdev, tph_st.start, i, tags);
+		vfio_pci_memory_unlock_and_restore(vdev, cmd);
+	}
+
+out:
+	kfree(tags);
+	kfree(src_hndl);
+	return ret;
+}
+
 int vfio_pci_core_ioctl_feature(struct vfio_device *device, u32 flags,
 				void __user *arg, size_t argsz)
 {
@@ -1625,6 +1946,16 @@ 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_DMA_BUF_TPH:
+		return vfio_pci_core_feature_dma_buf_tph(vdev, flags, arg,
+							 argsz);
+	case VFIO_DEVICE_FEATURE_TPH:
+		return vfio_pci_core_feature_tph(vdev, flags, arg, argsz);
+	case VFIO_DEVICE_FEATURE_TPH_RESOLVE:
+		return vfio_pci_core_feature_tph_resolve(vdev, flags,
+							 arg, argsz);
+	case VFIO_DEVICE_FEATURE_TPH_ST:
+		return vfio_pci_core_feature_tph_st(vdev, flags, arg, argsz);
 	default:
 		return -ENOTTY;
 	}
diff --git a/drivers/vfio/pci/vfio_pci_dmabuf.c b/drivers/vfio/pci/vfio_pci_dmabuf.c
index c16f460c01d681..5351839c1338af 100644
--- a/drivers/vfio/pci/vfio_pci_dmabuf.c
+++ b/drivers/vfio/pci/vfio_pci_dmabuf.c
@@ -3,6 +3,7 @@
  */
 #include <linux/dma-buf-mapping.h>
 #include <linux/pci-p2pdma.h>
+#include <linux/pci-tph.h>
 #include <linux/dma-resv.h>
 
 #include "vfio_pci_priv.h"
@@ -19,7 +20,17 @@ struct vfio_pci_dma_buf {
 	u32 nr_ranges;
 	struct kref kref;
 	struct completion comp;
-	u8 revoked : 1;
+
+	/*
+	 * Updates protected by dmabuf->resv, @revoked additionally
+	 * protected by memory_lock.
+	 */
+	u16 tph_st_ext;
+	u8 tph_st;
+	bool revoked;
+	u8 tph_st_valid:1;
+	u8 tph_st_ext_valid:1;
+	u8 tph_ph:2;
 };
 
 static int vfio_pci_dma_buf_attach(struct dma_buf *dmabuf,
@@ -30,7 +41,7 @@ static int vfio_pci_dma_buf_attach(struct dma_buf *dmabuf,
 	if (!attachment->peer2peer)
 		return -EOPNOTSUPP;
 
-	if (priv->revoked)
+	if (READ_ONCE(priv->revoked))
 		return -ENODEV;
 
 	if (!dma_buf_attach_revocable(attachment))
@@ -69,6 +80,26 @@ vfio_pci_dma_buf_map(struct dma_buf_attachment *attachment,
 	return ret;
 }
 
+static int vfio_pci_dma_buf_get_pci_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 +132,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_pci_tph = vfio_pci_dma_buf_get_pci_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 +365,72 @@ 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;
+
+	comp = pcie_tph_completer_type(vdev->pdev);
+	if (comp == PCI_EXP_DEVCAP2_TPH_COMP_NONE)
+		return -EOPNOTSUPP;
+
+	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;
+
+	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 fca9d0dfac90f8..03c4a0414264ef 100644
--- a/drivers/vfio/pci/vfio_pci_priv.h
+++ b/drivers/vfio/pci/vfio_pci_priv.h
@@ -11,6 +11,12 @@
 /* Cap maximum number of ioeventfds per device (arbitrary) */
 #define VFIO_PCI_IOEVENTFD_MAX		1000
 
+enum vfio_pci_tph_policy {
+	VFIO_PCI_TPH_POLICY_NO_ST = 0,
+	VFIO_PCI_TPH_POLICY_IV_ST = 1,
+	VFIO_PCI_TPH_POLICY_DS_ST = 2,
+};
+
 struct vfio_pci_ioeventfd {
 	struct list_head	next;
 	struct vfio_pci_core_device	*vdev;
@@ -118,6 +124,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 +138,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/linux/dma-buf.h b/include/linux/dma-buf.h
index d1203da56fc5f7..14e8eb61a62c04 100644
--- a/include/linux/dma-buf.h
+++ b/include/linux/dma-buf.h
@@ -113,6 +113,29 @@ struct dma_buf_ops {
 	 */
 	void (*unpin)(struct dma_buf_attachment *attach);
 
+	/**
+	 * @get_pci_tph:
+	 *
+	 * Retrieve PCIe TLP Processing Hint (TPH) steering metadata for
+	 * this buffer so an importer can program a matching ST/PH hint on
+	 * outbound TLPs targeting the exporter for peer-to-peer DMA.
+	 *
+	 * @dmabuf: DMA buffer for which to retrieve TPH metadata
+	 * @extended: false for 8-bit ST, true for 16-bit Extended ST
+	 * @steering_tag: Returns the raw TPH steering tag for the requested
+	 *                namespace
+	 * @ph: Returns the TPH processing hint (2-bit value)
+	 *
+	 * Optional callback for dma_buf_get_pci_tph(). Called with
+	 * &dma_buf.resv held. Returns 0 on success or -EOPNOTSUPP when
+	 * the exporter has no metadata for the requested namespace.
+	 *
+	 * Returned metadata follows the lifetime rules documented for
+	 * dma_buf_get_pci_tph().
+	 */
+	int (*get_pci_tph)(struct dma_buf *dmabuf, bool extended,
+			   u16 *steering_tag, u8 *ph);
+
 	/**
 	 * @map_dma_buf:
 	 *
@@ -563,6 +586,8 @@ void dma_buf_detach(struct dma_buf *dmabuf,
 		    struct dma_buf_attachment *attach);
 int dma_buf_pin(struct dma_buf_attachment *attach);
 void dma_buf_unpin(struct dma_buf_attachment *attach);
+int dma_buf_get_pci_tph(struct dma_buf *dmabuf, bool extended,
+			u16 *steering_tag, u8 *ph);
 
 struct dma_buf *dma_buf_export(const struct dma_buf_export_info *exp_info);
 
diff --git a/include/linux/pci-tph.h b/include/linux/pci-tph.h
index be68cd17f2f833..aeaf701e7965b0 100644
--- a/include/linux/pci-tph.h
+++ b/include/linux/pci-tph.h
@@ -9,6 +9,8 @@
 #ifndef LINUX_PCI_TPH_H
 #define LINUX_PCI_TPH_H
 
+#include <linux/pci.h>
+
 /*
  * According to the ECN for PCI Firmware Spec, Steering Tag can be different
  * depending on the memory type: Volatile Memory or Persistent Memory. When a
@@ -21,26 +23,69 @@ enum tph_mem_type {
 };
 
 #ifdef CONFIG_PCIE_TPH
+/**
+ * pcie_std_tph_supported - check standard TPH requester support
+ * @pdev: PCI device
+ */
+#define pcie_std_tph_supported(pdev) \
+	((pdev)->tph_max_type >= PCI_TPH_REQ_TPH_ONLY)
+/**
+ * pcie_ext_tph_supported - check extended TPH requester support
+ * @pdev: PCI device
+ */
+#define pcie_ext_tph_supported(pdev) \
+	((pdev)->tph_max_type == PCI_TPH_REQ_EXT_TPH)
 int pcie_tph_set_st_entry(struct pci_dev *pdev,
 			  unsigned int index, u16 tag);
-int pcie_tph_get_cpu_st(struct pci_dev *dev,
-			enum tph_mem_type mem_type,
+int pcie_tph_set_st_entries(struct pci_dev *pdev, unsigned int start,
+			    unsigned int count, const u16 *tags);
+int pcie_tph_get_cpu_st_ext(struct pci_dev *dev, enum tph_mem_type mem_type,
+			    u8 req_type, unsigned int cpu, u16 *tag);
+int pcie_tph_get_cpu_st(struct pci_dev *dev, enum tph_mem_type mem_type,
 			unsigned int cpu, u16 *tag);
 void pcie_disable_tph(struct pci_dev *pdev);
-int pcie_enable_tph(struct pci_dev *pdev, int mode);
+int pcie_enable_tph_ext(struct pci_dev *pdev, u8 mode, u8 req_type);
+int pcie_enable_tph(struct pci_dev *pdev, u8 mode);
 u16 pcie_tph_get_st_table_size(struct pci_dev *pdev);
 u32 pcie_tph_get_st_table_loc(struct pci_dev *pdev);
+u8 pcie_tph_enabled_req_type(struct pci_dev *pdev);
+u8 pcie_tph_completer_type(struct pci_dev *pdev);
+bool pcie_tph_dsm_supported(struct pci_dev *pdev);
 #else
+#define pcie_std_tph_supported(pdev) false
+#define pcie_ext_tph_supported(pdev) false
 static inline int pcie_tph_set_st_entry(struct pci_dev *pdev,
 					unsigned int index, u16 tag)
 { return -EINVAL; }
+static inline int pcie_tph_set_st_entries(struct pci_dev *pdev,
+					  unsigned int start,
+					  unsigned int count, const u16 *tags)
+{ return -EINVAL; }
+static inline int pcie_tph_get_cpu_st_ext(struct pci_dev *dev,
+					  enum tph_mem_type mem_type,
+					  u8 req_type, unsigned int cpu,
+					  u16 *tag)
+{ return -EINVAL; }
 static inline int pcie_tph_get_cpu_st(struct pci_dev *dev,
 				      enum tph_mem_type mem_type,
 				      unsigned int cpu, u16 *tag)
 { return -EINVAL; }
 static inline void pcie_disable_tph(struct pci_dev *pdev) { }
-static inline int pcie_enable_tph(struct pci_dev *pdev, int mode)
+static inline int pcie_enable_tph_ext(struct pci_dev *pdev, u8 mode,
+				      u8 req_type)
+{ return -EINVAL; }
+static inline int pcie_enable_tph(struct pci_dev *pdev, u8 mode)
 { return -EINVAL; }
+static inline u16 pcie_tph_get_st_table_size(struct pci_dev *pdev)
+{ return 0; }
+static inline u32 pcie_tph_get_st_table_loc(struct pci_dev *pdev)
+{ return PCI_TPH_LOC_NONE; }
+static inline u8 pcie_tph_enabled_req_type(struct pci_dev *pdev)
+{ return PCI_TPH_REQ_DISABLE; }
+static inline u8 pcie_tph_completer_type(struct pci_dev *pdev)
+{ return PCI_EXP_DEVCAP2_TPH_COMP_NONE; }
+static inline bool pcie_tph_dsm_supported(struct pci_dev *pdev)
+{ return false; }
 #endif
 
 #endif /* LINUX_PCI_TPH_H */
diff --git a/include/linux/pci.h b/include/linux/pci.h
index 64b308b6e61c19..5ed58799a9b129 100644
--- a/include/linux/pci.h
+++ b/include/linux/pci.h
@@ -472,7 +472,6 @@ struct pci_dev {
 	unsigned int	ats_enabled:1;		/* Address Translation Svc */
 	unsigned int	pasid_enabled:1;	/* Process Address Space ID */
 	unsigned int	pri_enabled:1;		/* Page Request Interface */
-	unsigned int	tph_enabled:1;		/* TLP Processing Hints */
 	unsigned int	fm_enabled:1;		/* Flit Mode (segment captured) */
 	unsigned int	is_managed:1;		/* Managed via devres */
 	unsigned int	is_msi_managed:1;	/* MSI release via devres installed */
@@ -588,7 +587,10 @@ struct pci_dev {
 	u8 reset_methods[PCI_NUM_RESET_METHODS]; /* In priority order */
 
 #ifdef CONFIG_PCIE_TPH
-	u16		tph_cap;	/* TPH capability offset */
+	u16		tph_cap:13;	/* TPH capability offset */
+	u16		tph_max_type:2; /* Negotiated max TPH requester type
+					   between device and root port */
+	u16		tph_enabled:1;	/* Whether TPH is enabled */
 	u8		tph_mode;	/* TPH mode */
 	u8		tph_req_type;	/* TPH requester type */
 #endif
diff --git a/include/linux/vfio_pci_core.h b/include/linux/vfio_pci_core.h
index 9a1674c152aa29..d6064fa9e80a69 100644
--- a/include/linux/vfio_pci_core.h
+++ b/include/linux/vfio_pci_core.h
@@ -149,6 +149,9 @@ struct vfio_pci_core_device {
 	struct notifier_block	nb;
 	struct rw_semaphore	memory_lock;
 	struct list_head	dmabufs;
+	u8			tph_opt_in;
+	u8			tph_policy;
+	u32			tph_cap_virt;
 };
 
 enum vfio_pci_io_width {
diff --git a/include/uapi/linux/vfio.h b/include/uapi/linux/vfio.h
index 5de618a3a5eeee..9bc3cef7a89ac4 100644
--- a/include/uapi/linux/vfio.h
+++ b/include/uapi/linux/vfio.h
@@ -1534,6 +1534,150 @@ 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. @flags is the authoritative validity mask for the
+ * two namespaces: VFIO_DMA_BUF_TPH_ST marks @steering_tag valid and
+ * VFIO_DMA_BUF_TPH_ST_EXT marks @steering_tag_ext valid. An importer
+ * requests one namespace and receives the matching value.
+ *
+ * Each SET fully replaces the dma-buf's TPH state for future queries
+ * only. A namespace whose flag bit is clear is reported as unsupported
+ * to future importers; an importer that has already retrieved a value
+ * is unaffected. @flags == 0 leaves both namespaces unsupported for
+ * future importers.
+ *
+ * @ph is the 2-bit TLP Processing Hint and must be in the range [0, 3].
+ *
+ * Undefined @flags and @ph bits must always be zero.
+ *
+ * Userspace must configure TPH before handing the dma-buf fd to an
+ * importer.
+ */
+#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;
+};
+
+/* PCIe TPH device feature definitions for VFIO_DEVICE_FEATURE ioctl */
+#define VFIO_DEVICE_FEATURE_TPH		14
+#define VFIO_DEVICE_FEATURE_TPH_RESOLVE	15
+#define VFIO_DEVICE_FEATURE_TPH_ST	16
+
+/*
+ * VFIO_DEVICE_FEATURE_TPH - Control and query PCI TPH capabilities
+ *
+ * SET: Opt-in to TPH feature for the device; flags must be zero.
+ * GET: Return supported TPH capability bits (see VFIO_DEVICE_TPH_CAP_*)
+ *      in flags. Returns error if SET has not been invoked first.
+ *
+ * Userspace must first invoke SET on this feature to enable TPH support,
+ * receive valid capability bits via GET, gain permission for using
+ * VFIO_DEVICE_FEATURE_TPH_RESOLVE / VFIO_DEVICE_FEATURE_TPH_ST, and access
+ * the virtualized TPH capability registers via VFIO config space accesses.
+ */
+struct vfio_device_feature_tph {
+	__u32 flags;
+};
+
+/* DMABUF source resolve processing hint supported */
+#define VFIO_DEVICE_TPH_CAP_RESOLVE_DMABUF_PH	(1u << 0)
+/* DMABUF source TPH_ST batch programming supported */
+#define VFIO_DEVICE_TPH_CAP_ST_DMABUF		(1u << 1)
+/* CPU source TPH_ST batch programming supported */
+#define VFIO_DEVICE_TPH_CAP_ST_CPU		(1u << 2)
+/* NONE source (means steering tag is zero) TPH_ST batch programming supported */
+#define VFIO_DEVICE_TPH_CAP_ST_NONE		(1u << 3)
+/* DMABUF source resolve steering tag supported */
+#define VFIO_DEVICE_TPH_CAP_RESOLVE_DMABUF_ST	(1u << 4)
+/* CPU source resolve steering tag supported */
+#define VFIO_DEVICE_TPH_CAP_RESOLVE_CPU_ST	(1u << 5)
+
+/*
+ * VFIO_DEVICE_FEATURE_TPH_RESOLVE - Resolve TPH attributes from given source
+ *
+ * GET only
+ *
+ * Requires prior successful SET on VFIO_DEVICE_FEATURE_TPH, otherwise
+ * this feature will return error.
+ *
+ * @flags: IN - source type selector (see VFIO_DEVICE_TPH_SRC_*)
+ *         and steering tag namespace selector (see VFIO_DEVICE_TPH_EXTENDED)
+ * @src:   IN - source identifier, e.g. dmabuf fd when SRC_DMABUF is set in
+ *         flags
+ * @valid: OUT - bitmap indicating valid output fields, see
+ *         VFIO_DEVICE_TPH_VALID_*
+ * @ph:    OUT - TPH processing hint (valid when VALID_PH is set in valid)
+ * @st:    OUT - TPH steering tag (valid when VALID_ST is set in valid)
+ */
+struct vfio_device_feature_tph_resolve {
+	__u32 flags;
+	__u32 src;
+	__u8  valid;
+#define VFIO_DEVICE_TPH_VALID_PH (1u << 0) /* ph holds valid processing hint */
+#define VFIO_DEVICE_TPH_VALID_ST (1u << 1) /* st holds valid steering tag */
+	__u8  ph;
+	__u16 st;
+};
+
+/*
+ * VFIO_DEVICE_FEATURE_TPH_ST - Batch program architected steering tag
+ * table entries
+ * Return value: >=0 = number of successfully written entries;
+ *               <0  = negative errno
+ *
+ * SET only
+ *
+ * Additional constraint: Userspace must complete two prerequisite steps
+ * before invoking this feature:
+ * 1. Call VFIO_DEVICE_FEATURE_TPH with SET to enable vfio TPH permission;
+ * 2. Write the TPH control register to activate device hardware TPH logic.
+ *
+ * @flags: IN - source type selector (see VFIO_DEVICE_TPH_SRC_*) and
+ *         steering tag namespace selector (see VFIO_DEVICE_TPH_EXTENDED) and
+ *         failure policy (see VFIO_DEVICE_TPH_REQUIRE_ST)
+ * @start: IN - first steering tag table index / MSI-X vector
+ * @count: IN - number of contiguous entries to program
+ * @dests: IN - user pointer to array of source u32 values
+ */
+struct vfio_device_feature_tph_st {
+	__u32 flags;
+	__u16 start;
+	__u16 count;
+	__aligned_u64 dests;
+};
+
+/* Source holds dma-buf fd */
+#define VFIO_DEVICE_TPH_SRC_DMABUF      (1u << 0)
+/* Use extended 16-bit steering tag namespace */
+#define VFIO_DEVICE_TPH_EXTENDED	(1u << 1)
+/* Source holds volatile memory CPU ID */
+#define VFIO_DEVICE_TPH_SRC_CPU_VOLATILE	(1u << 2)
+/* Source holds persistent memory CPU ID */
+#define VFIO_DEVICE_TPH_SRC_CPU_PERSISTENT	(1u << 3)
+/* TPH_ST only: set steering tag to zero */
+#define VFIO_DEVICE_TPH_SRC_NONE	(1u << 4)
+/* TPH_ST only: fail if resolved steering tag is zero */
+#define VFIO_DEVICE_TPH_REQUIRE_ST	(1u << 5)
+
 /* -------- API for Type1 VFIO IOMMU -------- */
 
 /**

3/2 2026/07/14 16:05 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 25391
							output: 20
							thoughts: 500
						

Thoughts:
**My Assessment of the PCIe TLP Processing Hints (TPH) and DMA-BUF Patch Series**

Okay, so I'm looking at this patch series, and it's all about integrating PCIe TPH with DMA-BUF, specifically within the VFIO framework. My initial impression is that it's a non-trivial piece of work, touching a good chunk of the kernel: `dma-buf.c`, the `pci/tph.c` and a significant portion of the VFIO PCI code, as well as several header files. The breadth of the changes immediately signals some complexity and the need for careful scrutiny.

It's clear that the aim here is to expose TPH functionality to userspace through VFIO. I see new VFIO ioctls being introduced: `VFIO_DEVICE_FEATURE_DMA_BUF_TPH`, `VFIO_DEVICE_FEATURE_TPH`, `VFIO_DEVICE_FEATURE_TPH_RESOLVE`, and `VFIO_DEVICE_FEATURE_TPH_ST`. The addition of `dma_buf_get_pci_tph` in `dma-buf.c` and the modifications to `pci/tph.c` including new functions like `pcie_tph_get_cpu_st_ext`, `pcie_tph_set_st_entries`, and the enablement/support checks, all seem to be plumbing the necessary pieces together. It appears functional, and reachable via VFIO ioctls. This means we *absolutely* need to fuzz the new ioctls aggressively; a misinterpretation could have severe repercussions.

Before I delve deeper, let's identify the config options we need to know about. The code uses `CONFIG_PCIE_TPH` across several files, including the main VFIO PCI driver, as well as the pci and tph headers. `CONFIG_VFIO_PCI` is clearly the backbone config for VFIO PCI, and it's unsurprising to see that `CONFIG_DMA_SHARED_BUFFER` is required, given the DMA-BUF integration. Next, I should look at where `CONFIG_PCIE_TPH` is defined within the Kconfig files to fully understand what is needed for this new feature.



4/2 2026/07/14 16:05 tool grepper
0m Args:
{
  "Expression": "config PCIE_TPH"
}

Results:
{
  "Output": "drivers/pci/Kconfig=185=config PCI_PASID\n--\ndrivers/pci/Kconfig-196-\ndrivers/pci/Kconfig:197:config PCIE_TPH\ndrivers/pci/Kconfig-198-\tbool \"TLP Processing Hints\"\n"
}

5/2 2026/07/14 16:06 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 26475
							output: 19
							thoughts: 23
						

Thoughts:
Here's my thought process, as requested:

**PCIe TPH and VFIO-PCI Considerations**

Okay, so I'm looking at this code and first thing, `PCIE_TPH` is defined as a boolean. That's good to keep in mind, right? Now, I need to understand its interaction with the rest of the system. Let's see... the most obvious place to start is with `VFIO_PCI`. I want to understand how the translation of the PCIe TPH is handled. Perhaps I should also look for VFIO-specific code paths, to see how to deal with this configuration. I should also check its associated configurations to see if I am missing something obvious.



6/2 2026/07/14 16:06 tool grepper
0m Args:
{
  "Expression": "config VFIO_PCI"
}

Results:
{
  "Output": "drivers/vfio/pci/Kconfig=2=menu \"VFIO support for PCI devices\"\n--\ndrivers/vfio/pci/Kconfig-4-\ndrivers/vfio/pci/Kconfig:5:config VFIO_PCI_CORE\ndrivers/vfio/pci/Kconfig-6-\ttristate\n--\ndrivers/vfio/pci/Kconfig-9-\ndrivers/vfio/pci/Kconfig:10:config VFIO_PCI_INTX\ndrivers/vfio/pci/Kconfig-11-\tdef_bool y if !S390\n--\ndrivers/vfio/pci/Kconfig-13-\ndrivers/vfio/pci/Kconfig:14:config VFIO_PCI\ndrivers/vfio/pci/Kconfig-15-\ttristate \"Generic VFIO support for any PCI device\"\n--\ndrivers/vfio/pci/Kconfig=23=if VFIO_PCI\ndrivers/vfio/pci/Kconfig:24:config VFIO_PCI_VGA\ndrivers/vfio/pci/Kconfig-25-\tbool \"Generic VFIO PCI support for VGA devices\"\n--\ndrivers/vfio/pci/Kconfig-33-\ndrivers/vfio/pci/Kconfig:34:config VFIO_PCI_IGD\ndrivers/vfio/pci/Kconfig-35-\tbool \"Generic VFIO PCI extensions for Intel graphics (GVT-d)\"\n--\ndrivers/vfio/pci/Kconfig=45=endif\ndrivers/vfio/pci/Kconfig-46-\ndrivers/vfio/pci/Kconfig:47:config VFIO_PCI_ZDEV_KVM\ndrivers/vfio/pci/Kconfig-48-\tbool \"VFIO PCI extensions for s390x KVM passthrough\"\n--\ndrivers/vfio/pci/Kconfig-57-\ndrivers/vfio/pci/Kconfig:58:config VFIO_PCI_DMABUF\ndrivers/vfio/pci/Kconfig-59-\tdef_bool y if VFIO_PCI_CORE \u0026\u0026 PCI_P2PDMA \u0026\u0026 DMA_SHARED_BUFFER\n"
}

7/2 2026/07/14 16:06 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 27205
							output: 21
							thoughts: 101
						

Thoughts:
**My VFIO Configuration Thoughts**

Okay, so I'm thinking about the VFIO setup here. Looks like `VFIO_PCI` is the core, the main module we need for PCI passthrough, naturally. But, if we want to leverage the dmabuf features, then we'll definitely need `VFIO_PCI_DMABUF` as well. That one, I see, depends on `VFIO_PCI_CORE`, `PCI_P2PDMA` and `DMA_SHARED_BUFFER`.

So, to be explicit and cover all the bases, I'm going to make sure we enable `PCIE_TPH`, then enable `VFIO_PCI`, followed by `PCI_P2PDMA`, and, finally, `DMA_SHARED_BUFFER`. I want to double-check that `PCI_P2PDMA` is exactly what I expect.



8/2 2026/07/14 16:06 tool grepper
0m Args:
{
  "Expression": "config PCI_P2PDMA"
}

Results:
{
  "Output": "drivers/pci/Kconfig=197=config PCIE_TPH\n--\ndrivers/pci/Kconfig-205-\ndrivers/pci/Kconfig:206:config PCI_P2PDMA\ndrivers/pci/Kconfig-207-\tbool \"PCI peer-to-peer transfer support\"\n"
}

9/2 2026/07/14 16:06 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 27607
							output: 26
							thoughts: 169
						

Thoughts:
**My Initial Assessment of the TPH Implementation**

Alright, let's get down to brass tacks. I need to get a handle on the key functions driving this TPH implementation. My initial scan of the code suggests these are the critical pieces: `vfio_pci_core_feature_tph`, `vfio_pci_core_feature_tph_resolve`, `vfio_pci_core_feature_tph_st`, `vfio_pci_core_feature_dma_buf_tph`, `vfio_pci_dma_buf_get_pci_tph`, `dma_buf_get_pci_tph`, `pcie_tph_get_cpu_st_ext`, `pcie_tph_set_st_entries`, and finally `pcie_enable_tph_ext`. These are the functions I'll need to drill down into to fully grasp the control flow. First, though, I'll need to confirm that these functions are actually implemented and that they're correctly connected within the overall architecture. Gotta make sure the building blocks are solid before I start assembling.



10/2 2026/07/14 16:06 tool grepper
0m Args:
{
  "Expression": "vfio_pci_core_feature_tph"
}

Results:
{
  "Output": "drivers/vfio/pci/vfio_pci_core.c=1656=static u32 vfio_pci_get_tph_capability(struct vfio_pci_core_device *vdev)\n--\ndrivers/vfio/pci/vfio_pci_core.c-1681-\ndrivers/vfio/pci/vfio_pci_core.c:1682:static int vfio_pci_core_feature_tph(struct vfio_pci_core_device *vdev,\ndrivers/vfio/pci/vfio_pci_core.c-1683-\t\t\t\t     u32 flags,\n--\ndrivers/vfio/pci/vfio_pci_core.c=1750=static int vfio_pci_get_tph_st_tag(struct pci_dev *pdev, u32 src_bits,\n--\ndrivers/vfio/pci/vfio_pci_core.c-1775-\ndrivers/vfio/pci/vfio_pci_core.c:1776:static int vfio_pci_core_feature_tph_resolve(struct vfio_pci_core_device *vdev,\ndrivers/vfio/pci/vfio_pci_core.c-1777-\t\t\tu32 flags,\n--\ndrivers/vfio/pci/vfio_pci_core.c=1838=static u32 vfio_pci_get_tph_st_allow_src(struct vfio_pci_core_device *vdev)\n--\ndrivers/vfio/pci/vfio_pci_core.c-1853-\ndrivers/vfio/pci/vfio_pci_core.c:1854:static int vfio_pci_core_feature_tph_st(struct vfio_pci_core_device *vdev,\ndrivers/vfio/pci/vfio_pci_core.c-1855-\t\t\t\tu32 flags,\n--\ndrivers/vfio/pci/vfio_pci_core.c=1931=int vfio_pci_core_ioctl_feature(struct vfio_device *device, u32 flags,\n--\ndrivers/vfio/pci/vfio_pci_core.c-1952-\tcase VFIO_DEVICE_FEATURE_TPH:\ndrivers/vfio/pci/vfio_pci_core.c:1953:\t\treturn vfio_pci_core_feature_tph(vdev, flags, arg, argsz);\ndrivers/vfio/pci/vfio_pci_core.c-1954-\tcase VFIO_DEVICE_FEATURE_TPH_RESOLVE:\ndrivers/vfio/pci/vfio_pci_core.c:1955:\t\treturn vfio_pci_core_feature_tph_resolve(vdev, flags,\ndrivers/vfio/pci/vfio_pci_core.c-1956-\t\t\t\t\t\t\t arg, argsz);\ndrivers/vfio/pci/vfio_pci_core.c-1957-\tcase VFIO_DEVICE_FEATURE_TPH_ST:\ndrivers/vfio/pci/vfio_pci_core.c:1958:\t\treturn vfio_pci_core_feature_tph_st(vdev, flags, arg, argsz);\ndrivers/vfio/pci/vfio_pci_core.c-1959-\tdefault:\n"
}

11/2 2026/07/14 16:06 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 28944
							output: 31
							thoughts: 30
						

Thoughts:
Okay, here's my interpretation of those thoughts, tailored for an expert audience, presented in the first person:

**Confirming and Refining: A Quick Check**

Right, *they* are there. I can sense it. Now, let me quickly confirm that hypothesis. I need to make sure my initial assessment is correct and I have all the pieces I require.

Let's check the `vfio_pci_core_feature_dma_buf_tph` structure. Specifically, I'm verifying the implications of its presence and making sure my understanding of this crucial component in the context of the larger architecture is sound. It's a key element and I need to make sure I'm on solid ground regarding its functionality and expected behavior. It's a quick sanity check but a vital step to avoid any future surprises.



12/2 2026/07/14 16:06 tool grepper
0m Args:
{
  "Expression": "vfio_pci_core_feature_dma_buf_tph"
}

Results:
{
  "Output": "drivers/vfio/pci/vfio_pci_core.c=1931=int vfio_pci_core_ioctl_feature(struct vfio_device *device, u32 flags,\n--\ndrivers/vfio/pci/vfio_pci_core.c-1949-\tcase VFIO_DEVICE_FEATURE_DMA_BUF_TPH:\ndrivers/vfio/pci/vfio_pci_core.c:1950:\t\treturn vfio_pci_core_feature_dma_buf_tph(vdev, flags, arg,\ndrivers/vfio/pci/vfio_pci_core.c-1951-\t\t\t\t\t\t\t argsz);\n--\ndrivers/vfio/pci/vfio_pci_dmabuf.c=253=int vfio_pci_core_feature_dma_buf(struct vfio_pci_core_device *vdev, u32 flags,\n--\ndrivers/vfio/pci/vfio_pci_dmabuf.c-367-\ndrivers/vfio/pci/vfio_pci_dmabuf.c:368:int vfio_pci_core_feature_dma_buf_tph(struct vfio_pci_core_device *vdev,\ndrivers/vfio/pci/vfio_pci_dmabuf.c-369-\t\t\t\t      u32 flags,\n--\ndrivers/vfio/pci/vfio_pci_priv.h=124=int vfio_pci_core_feature_dma_buf(struct vfio_pci_core_device *vdev, u32 flags,\n--\ndrivers/vfio/pci/vfio_pci_priv.h-126-\t\t\t\t  size_t argsz);\ndrivers/vfio/pci/vfio_pci_priv.h:127:int vfio_pci_core_feature_dma_buf_tph(struct vfio_pci_core_device *vdev,\ndrivers/vfio/pci/vfio_pci_priv.h-128-\t\t\t\t      u32 flags,\n--\ndrivers/vfio/pci/vfio_pci_priv.h=142=static inline int\ndrivers/vfio/pci/vfio_pci_priv.h:143:vfio_pci_core_feature_dma_buf_tph(struct vfio_pci_core_device *vdev, u32 flags,\ndrivers/vfio/pci/vfio_pci_priv.h-144-\t\t\t\t  struct vfio_device_feature_dma_buf_tph __user *arg,\n"
}

13/2 2026/07/14 16:06 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 29855
							output: 23
							thoughts: 281
						

Thoughts:
**My Initial Assessment of the VFIO PCIe TPH Patch**

Okay, so this patch introduces some functional changes within VFIO PCI and DMA-BUF to enable support for PCIe TPH. My initial gut feeling? This screams "fuzzing target." We're likely dealing with some interesting interactions between the VFIO driver, the DMA-BUF framework, and the PCIe hierarchy. Any new functionality in this space is always worth a thorough look.

Now, for the setup. I'll need to make sure the following configs are enabled: `PCIE_TPH`, `VFIO_PCI`, and, crucially, `VFIO_PCI_DMABUF`, which means enabling the dependencies, `PCI_P2PDMA` and `DMA_SHARED_BUFFER`. These are my ground zero.

Looking at the code, these symbols seem like good points to concentrate on:
*   `vfio_pci_core_feature_tph`, `vfio_pci_core_feature_tph_resolve`, `vfio_pci_core_feature_tph_st` - These likely deal with the core TPH feature setup and handling.
*   `vfio_pci_core_feature_dma_buf_tph`, `vfio_pci_dma_buf_get_pci_tph`, `dma_buf_get_pci_tph` - These functions are essential to understand the integration with DMA-BUF, which is going to be my primary area of focus.
*   `pcie_tph_get_cpu_st_ext`, `pcie_tph_set_st_entries`, `pcie_enable_tph_ext` - PCIe side control and configuration. These are important for the overall architecture.
*   `vfio_tph_config_write`, `vfio_tph_config_read` - These represent the crucial interface functions for managing TPH configuration. Let's start digging into `vfio_tph_config_write` first. This is where the magic happens.



14/2 2026/07/14 16:06 tool grepper
0m Args:
{
  "Expression": "vfio_tph_config_write"
}

Results:
{
  "Output": "drivers/vfio/pci/vfio_pci_config.c=1156=static int vfio_tph_config_read(struct vfio_pci_core_device *vdev, int pos,\n--\ndrivers/vfio/pci/vfio_pci_config.c-1172-\ndrivers/vfio/pci/vfio_pci_config.c:1173:static int vfio_tph_config_write(struct vfio_pci_core_device *vdev, int pos,\ndrivers/vfio/pci/vfio_pci_config.c-1174-\t\t\t\t int count, struct perm_bits *perm,\n--\ndrivers/vfio/pci/vfio_pci_config.c=1247=int __init vfio_pci_init_perm_bits(void)\n--\ndrivers/vfio/pci/vfio_pci_config.c-1266-\tecap_perms[PCI_EXT_CAP_ID_TPH].readfn = vfio_tph_config_read;\ndrivers/vfio/pci/vfio_pci_config.c:1267:\tecap_perms[PCI_EXT_CAP_ID_TPH].writefn = vfio_tph_config_write;\ndrivers/vfio/pci/vfio_pci_config.c-1268-\tecap_perms[PCI_EXT_CAP_ID_VNDR].writefn = vfio_raw_config_write;\n"
}

15/2 2026/07/14 16:06 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 30993
							output: 25
							thoughts: 46
						

Thoughts:
Okay, here's my summarized thought process, presented as if I'm having it:

**TPH Configuration Examination**

Right, so I've confirmed that `vfio_tph_config_write` and `vfio_tph_config_read` are indeed present. Good, good. They are the fundamental building blocks, so this is where I'd expect to begin. Now, the natural next step is to examine the function that ultimately leverages or utilizes those functions. The key area of interest at this point is going to be where these functions are actually invoked in relation to the PCIe Translation Process. Let's see what `pcie_tph_set_st_entries` has to say. That's the logical next function to investigate in order to find where the actual PCIe transactions are set up.



16/2 2026/07/14 16:06 tool grepper
0m Args:
{
  "Expression": "pcie_tph_set_st_entries"
}

Results:
{
  "Output": "drivers/pci/tph.c=354=EXPORT_SYMBOL(pcie_tph_get_cpu_st);\n--\ndrivers/pci/tph.c-356-/**\ndrivers/pci/tph.c:357: * pcie_tph_set_st_entries - Batch set Steering Tag entries in the ST table\ndrivers/pci/tph.c-358- * @pdev: PCI device\n--\ndrivers/pci/tph.c-364- */\ndrivers/pci/tph.c:365:int pcie_tph_set_st_entries(struct pci_dev *pdev, unsigned int start,\ndrivers/pci/tph.c-366-\t\t\t    unsigned int count, const u16 *tags)\n--\ndrivers/pci/tph.c-414-}\ndrivers/pci/tph.c:415:EXPORT_SYMBOL(pcie_tph_set_st_entries);\ndrivers/pci/tph.c-416-\n--\ndrivers/pci/tph.c=425=int pcie_tph_set_st_entry(struct pci_dev *pdev, unsigned int index, u16 tag)\ndrivers/pci/tph.c-426-{\ndrivers/pci/tph.c:427:\tint ret = pcie_tph_set_st_entries(pdev, index, 1, \u0026tag);\ndrivers/pci/tph.c-428-\treturn ret == 1 ? 0 : ret;\n--\ndrivers/vfio/pci/vfio_pci_core.c=1854=static int vfio_pci_core_feature_tph_st(struct vfio_pci_core_device *vdev,\n--\ndrivers/vfio/pci/vfio_pci_core.c-1920-\t\tcmd = vfio_pci_memory_lock_and_enable(vdev);\ndrivers/vfio/pci/vfio_pci_core.c:1921:\t\tret = pcie_tph_set_st_entries(pdev, tph_st.start, i, tags);\ndrivers/vfio/pci/vfio_pci_core.c-1922-\t\tvfio_pci_memory_unlock_and_restore(vdev, cmd);\n--\ninclude/linux/pci-tph.h=38=int pcie_tph_set_st_entry(struct pci_dev *pdev,\ninclude/linux/pci-tph.h-39-\t\t\t  unsigned int index, u16 tag);\ninclude/linux/pci-tph.h:40:int pcie_tph_set_st_entries(struct pci_dev *pdev, unsigned int start,\ninclude/linux/pci-tph.h-41-\t\t\t    unsigned int count, const u16 *tags);\n--\ninclude/linux/pci-tph.h=57=static inline int pcie_tph_set_st_entry(struct pci_dev *pdev,\n--\ninclude/linux/pci-tph.h-59-{ return -EINVAL; }\ninclude/linux/pci-tph.h:60:static inline int pcie_tph_set_st_entries(struct pci_dev *pdev,\ninclude/linux/pci-tph.h-61-\t\t\t\t\t  unsigned int start,\n"
}

17/2 2026/07/14 16:06 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 32106
							output: 23
							thoughts: 18
						

Thoughts:
Okay, here's my take on those thoughts, tailored for an expert audience:

**Initial Assessment and Targeted Focus**

Awesome! That's the feeling when you're about to dive into a specific area. Now, my mind immediately jumps to `pcie_enable_tph_ext`. We need to dissect this, and that's where I'm going to focus my energy. Given my expertise, I will immediately be looking at what's going on with the TPH (Transaction Processing Hint) extensions in relation to PCIe enablement. It's time to dig in.



18/2 2026/07/14 16:06 tool grepper
0m Args:
{
  "Expression": "pcie_enable_tph_ext"
}

Results:
{
  "Output": "drivers/pci/tph.c=452=EXPORT_SYMBOL(pcie_disable_tph);\n--\ndrivers/pci/tph.c-454-/**\ndrivers/pci/tph.c:455: * pcie_enable_tph_ext - Enable TPH support with specific ST mode and\ndrivers/pci/tph.c-456- * requester type\n--\ndrivers/pci/tph.c-465- */\ndrivers/pci/tph.c:466:int pcie_enable_tph_ext(struct pci_dev *pdev, u8 mode, u8 req_type)\ndrivers/pci/tph.c-467-{\n--\ndrivers/pci/tph.c-510-}\ndrivers/pci/tph.c:511:EXPORT_SYMBOL(pcie_enable_tph_ext);\ndrivers/pci/tph.c-512-\n--\ndrivers/pci/tph.c-517- *\ndrivers/pci/tph.c:518: * Wrapper for pcie_enable_tph_ext(), automatically uses the pre-cached\ndrivers/pci/tph.c-519- * tph_max_type value (negotiated maximum requester type between device and\n--\ndrivers/pci/tph.c=524=int pcie_enable_tph(struct pci_dev *pdev, u8 mode)\ndrivers/pci/tph.c-525-{\ndrivers/pci/tph.c:526:\treturn pcie_enable_tph_ext(pdev, mode, pdev-\u003etph_max_type);\ndrivers/pci/tph.c-527-}\n--\ndrivers/vfio/pci/vfio_pci_config.c=1173=static int vfio_tph_config_write(struct vfio_pci_core_device *vdev, int pos,\n--\ndrivers/vfio/pci/vfio_pci_config.c-1212-\tif (req == PCI_TPH_REQ_TPH_ONLY || req == PCI_TPH_REQ_EXT_TPH) {\ndrivers/vfio/pci/vfio_pci_config.c:1213:\t\tret = pcie_enable_tph_ext(pdev, mode, req);\ndrivers/vfio/pci/vfio_pci_config.c-1214-\t\tif (ret)\n--\ninclude/linux/pci-tph.h=46=void pcie_disable_tph(struct pci_dev *pdev);\ninclude/linux/pci-tph.h:47:int pcie_enable_tph_ext(struct pci_dev *pdev, u8 mode, u8 req_type);\ninclude/linux/pci-tph.h-48-int pcie_enable_tph(struct pci_dev *pdev, u8 mode);\n--\ninclude/linux/pci-tph.h=73=static inline void pcie_disable_tph(struct pci_dev *pdev) { }\ninclude/linux/pci-tph.h:74:static inline int pcie_enable_tph_ext(struct pci_dev *pdev, u8 mode,\ninclude/linux/pci-tph.h-75-\t\t\t\t      u8 req_type)\n"
}

19/2 2026/07/14 16:07 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 33126
							output: 20
							thoughts: 382
						

Thoughts:
**My Assessment of the TPH Symbols and Dependencies**

Okay, so I'm putting together the dependencies for this patch related to Transaction Processing Hints (TPH). My focus symbols are definitely:

*   `vfio_pci_core_feature_tph`
*   `vfio_pci_core_feature_tph_resolve`
*   `vfio_pci_core_feature_tph_st`
*   `vfio_pci_core_feature_dma_buf_tph`
*   `vfio_pci_dma_buf_get_pci_tph`
*   `dma_buf_get_pci_tph`
*   `pcie_tph_get_cpu_st_ext`
*   `pcie_tph_set_st_entries`
*   `pcie_enable_tph_ext`
*   `vfio_tph_config_write`
*   `vfio_tph_config_read`

These seem pretty tightly coupled to the patch itself, not part of the core hot-path code, which is good.

I need to make sure I haven't missed any other configuration symbols that are relevant. Let's see... looking at the code, `CONFIG_ACPI` comes up in a couple of places: `pcie_tph_get_cpu_st_ext` and `pcie_tph_dsm_supported`. However, `ACPI` is basically a given on x86, isn't it? So, I don't think I really need to explicitly call that out unless it's *specific* to this TPH feature. The feature itself, though, that's `PCIE_TPH`.

Alright, I need to revisit the `VFIO_PCI_DMABUF` setting. I remember that's a bit more involved. It's configured as `def_bool y if VFIO_PCI_CORE && PCI_P2PDMA && DMA_SHARED_BUFFER`. Okay, so, in addition to the base `VFIO_PCI`, I definitely need `PCI_P2PDMA` and `DMA_SHARED_BUFFER`. I should double-check and make sure to list them.

Let's quickly check the definition for `DMA_SHARED_BUFFER` to be sure.



20/2 2026/07/14 16:07 tool grepper
0m Args:
{
  "Expression": "config DMA_SHARED_BUFFER"
}

Results:
{
  "Output": "drivers/base/Kconfig=213=source \"drivers/base/regmap/Kconfig\"\ndrivers/base/Kconfig-214-\ndrivers/base/Kconfig:215:config DMA_SHARED_BUFFER\ndrivers/base/Kconfig-216-\tbool\n"
}

21/2 2026/07/14 16:07 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 34129
							output: 274
							thoughts: 539
						

Thoughts:
**My Analysis of VFIO-PCI Configuration and Associated Functions**

Okay, let's break this down. First, I see `DMA_SHARED_BUFFER` is a boolean, and usually, it's set via other configurations. Makes sense. Then there are some configurations to list - I'll jot them down: `PCIE_TPH`, `VFIO_PCI`, `PCI_P2PDMA`, and `DMA_SHARED_BUFFER`.

I need to confirm their types though, just to be sure. Alright, let me check. `VFIO_PCI` is a tristate, and that checks out, as is `VFIO_PCI_CORE`. `PCIE_TPH` is a bool. Good. `PCI_P2PDMA` is also a bool. Right. And finally, `DMA_SHARED_BUFFER`, also a boolean. So, all booleans and tristates, confirmed.

Alright, now let's make a note of those specific configurations: `PCIE_TPH`, `VFIO_PCI`, `PCI_P2PDMA`, and `DMA_SHARED_BUFFER`.

Now I'll check to see if there are any other supporting functions. It looks like we have a bunch of functions starting with `vfio_pci_...`. Those are static functions inside `vfio_pci_core.c`. Listing all of those might be too exhaustive, so I'll focus on the primary IOCTL handlers.

Okay, let's see, the focus symbols are: `vfio_pci_core_feature_tph`, `vfio_pci_core_feature_tph_resolve`, `vfio_pci_core_feature_tph_st`, `vfio_pci_core_feature_dma_buf_tph`, `vfio_pci_dma_buf_get_pci_tph`, `dma_buf_get_pci_tph`, `pcie_tph_get_cpu_st_ext`, `pcie_tph_set_st_entries`, `pcie_enable_tph_ext`, and `vfio_tph_config_write`.

This appears pretty comprehensive, covering the core configuration options and related functionalities. I think this is a solid review. I'll go ahead and execute `set-results`.



22/2 2026/07/14 16:07 tool set-results
0m Args:
{
  "EnableConfigs": [
    "PCIE_TPH",
    "VFIO_PCI",
    "PCI_P2PDMA",
    "DMA_SHARED_BUFFER"
  ],
  "FocusSymbols": [
    "vfio_pci_core_feature_tph",
    "vfio_pci_core_feature_tph_resolve",
    "vfio_pci_core_feature_tph_st",
    "vfio_pci_core_feature_dma_buf_tph",
    "vfio_pci_dma_buf_get_pci_tph",
    "dma_buf_get_pci_tph",
    "pcie_tph_get_cpu_st_ext",
    "pcie_tph_set_st_entries",
    "pcie_enable_tph_ext",
    "vfio_tph_config_write"
  ],
  "Reasoning": "The patch introduces functional changes to VFIO PCI and DMA-BUF to support PCIe TLP Processing Hints (TPH). It adds new VFIO ioctls/features for TPH configuration and resolution, which are reachable from userspace and should be fuzzed.",
  "WorthFuzzing": true
}

Results:
{
  "EnableConfigs": [
    "PCIE_TPH",
    "VFIO_PCI",
    "PCI_P2PDMA",
    "DMA_SHARED_BUFFER"
  ],
  "FocusSymbols": [
    "vfio_pci_core_feature_tph",
    "vfio_pci_core_feature_tph_resolve",
    "vfio_pci_core_feature_tph_st",
    "vfio_pci_core_feature_dma_buf_tph",
    "vfio_pci_dma_buf_get_pci_tph",
    "dma_buf_get_pci_tph",
    "pcie_tph_get_cpu_st_ext",
    "pcie_tph_set_st_entries",
    "pcie_enable_tph_ext",
    "vfio_tph_config_write"
  ],
  "Reasoning": "The patch introduces functional changes to VFIO PCI and DMA-BUF to support PCIe TLP Processing Hints (TPH). It adds new VFIO ioctls/features for TPH configuration and resolution, which are reachable from userspace and should be fuzzed.",
  "WorthFuzzing": true
}

LLM Calls Summary:
Total Calls Total Tokens Avg Tokens Total Duration (Seconds) Avg Duration (Seconds)
Tool Calls Summary:
Total Calls Total Duration (Seconds) Avg Duration (Seconds)