DPDK-dev Archive on lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v1 0/4] Introduce generic cache stash API and PCIe TPH implementation
@ 2026-05-08  9:28 Chengwen Feng
  2026-05-08  9:28 ` [PATCH v1 1/4] bus/pci: introduce PCIe TPH support Chengwen Feng
                   ` (6 more replies)
  0 siblings, 7 replies; 13+ messages in thread
From: Chengwen Feng @ 2026-05-08  9:28 UTC (permalink / raw)
  To: thomas, stephen; +Cc: dev, wathsala.vithanage

This patch set introduces a generic ethdev cache stash framework,
provides PCIe TPH (Transaction Processing Hints) implementation
on PCI/VFIO bus, enables TPH cache stash support for e1000/igb PMD,
and adds testpmd command line configuration.

The patchset is organized as follows:
- Patch 1/4: bus/pci: introduce PCIe TPH support
  Add generic PCIe TPH APIs and VFIO backend implementation,
  with stub implementations for BSD and Windows.

- Patch 2/4: ethdev: introduce generic cache stash API
  Define unified cache stash capability/operation/config abstraction,
  support device-level and per-queue stash management.

- Patch 3/4: net/e1000: add cache stash support via TPH
  Implement ethdev cache stash ops for igb PMD based on PCIe TPH.

- Patch 4/4: app/testpmd: add TPH stash objects configuration
  Add testpmd CLI option to configure stash objects and
  automatically apply stash enable/disable on forwarding start/stop.

Note:
1. This implementation references the Linux kernel VFIO TPH uapi
   series (v8 version), which is not yet merged upstream:
   https://lore.kernel.org/all/20260508064053.37529-1-fengchengwen@huawei.com/

2. The API design and cache stash model learn from the prior  work
   by Wathsala Vithanage: "An API for Cache Stashing with TPH".

3. The e1000/igb PMD is enabled as the first user of this API
   because it is the only PCIe TPH capable hardware available
   for testing at this time. The generic API design is extensible
   to other NICs and future cache stash mechanisms.

Chengwen Feng (4):
  bus/pci: introduce PCIe TPH support
  ethdev: introduce generic cache stash API
  net/e1000: add cache stash support via TPH
  app/testpmd: add TPH stash objects configuration

 app/test-pmd/parameters.c               |  20 ++
 app/test-pmd/testpmd.c                  |  81 +++++++
 app/test-pmd/testpmd.h                  |   1 +
 drivers/bus/pci/bsd/pci.c               |  50 +++++
 drivers/bus/pci/linux/pci.c             |  48 +++++
 drivers/bus/pci/linux/pci_init.h        |   9 +
 drivers/bus/pci/linux/pci_vfio.c        | 272 ++++++++++++++++++++++++
 drivers/bus/pci/rte_bus_pci.h           | 112 ++++++++++
 drivers/bus/pci/windows/pci.c           |  50 +++++
 drivers/net/intel/e1000/base/e1000_hw.h |   2 +
 drivers/net/intel/e1000/base/e1000_vf.h |   2 +
 drivers/net/intel/e1000/igb_ethdev.c    | 221 +++++++++++++++++++
 lib/ethdev/ethdev_driver.h              |  37 ++++
 lib/ethdev/rte_ethdev.c                 |  40 ++++
 lib/ethdev/rte_ethdev.h                 |  95 +++++++++
 15 files changed, 1040 insertions(+)

-- 
2.17.1


^ permalink raw reply	[flat|nested] 13+ messages in thread

* [PATCH v1 1/4] bus/pci: introduce PCIe TPH support
  2026-05-08  9:28 [PATCH v1 0/4] Introduce generic cache stash API and PCIe TPH implementation Chengwen Feng
@ 2026-05-08  9:28 ` Chengwen Feng
  2026-05-08 15:00   ` Stephen Hemminger
  2026-05-08  9:28 ` [PATCH v1 2/4] ethdev: introduce generic cache stash API Chengwen Feng
                   ` (5 subsequent siblings)
  6 siblings, 1 reply; 13+ messages in thread
From: Chengwen Feng @ 2026-05-08  9:28 UTC (permalink / raw)
  To: thomas, stephen; +Cc: dev, wathsala.vithanage

Add experimental API and Linux VFIO implementation for PCIe TLP
Processing Hints (TPH). Support device capability query, TPH
enable/disable, and steering tag get/set operations.

Stub functions are added for BSD and Windows.

The API includes:
- rte_pci_tph_query:   Query device TPH capabilities
- rte_pci_tph_enable:  Enable TPH with specified mode
- rte_pci_tph_disable: Disable TPH on device
- rte_pci_tph_st_get:  Get steering tags for CPUs
- rte_pci_tph_st_set:  Program steering tags into device's ST table

Signed-off-by: Chengwen Feng <fengchengwen@huawei.com>
---
 drivers/bus/pci/bsd/pci.c        |  50 ++++++
 drivers/bus/pci/linux/pci.c      |  48 ++++++
 drivers/bus/pci/linux/pci_init.h |   9 +
 drivers/bus/pci/linux/pci_vfio.c | 272 +++++++++++++++++++++++++++++++
 drivers/bus/pci/rte_bus_pci.h    | 112 +++++++++++++
 drivers/bus/pci/windows/pci.c    |  50 ++++++
 6 files changed, 541 insertions(+)

diff --git a/drivers/bus/pci/bsd/pci.c b/drivers/bus/pci/bsd/pci.c
index aba44492e0..0ba1c9d898 100644
--- a/drivers/bus/pci/bsd/pci.c
+++ b/drivers/bus/pci/bsd/pci.c
@@ -667,3 +667,53 @@ rte_pci_ioport_unmap(struct rte_pci_ioport *p)
 
 	return ret;
 }
+
+RTE_EXPORT_EXPERIMENTAL_SYMBOL(rte_pci_tph_query, 26.07)
+int
+rte_pci_tph_query(const struct rte_pci_device *dev, uint32_t *supported_modes,
+		  uint32_t *st_table_sz)
+{
+	RTE_SET_USED(dev);
+	RTE_SET_USED(supported_modes);
+	RTE_SET_USED(st_table_sz);
+	return -ENOTSUP;
+}
+
+RTE_EXPORT_EXPERIMENTAL_SYMBOL(rte_pci_tph_enable, 26.07)
+int
+rte_pci_tph_enable(const struct rte_pci_device *dev, uint32_t mode)
+{
+	RTE_SET_USED(dev);
+	RTE_SET_USED(mode);
+	return -ENOTSUP;
+}
+
+RTE_EXPORT_EXPERIMENTAL_SYMBOL(rte_pci_tph_disable, 26.07)
+int
+rte_pci_tph_disable(const struct rte_pci_device *dev)
+{
+	RTE_SET_USED(dev);
+	return -ENOTSUP;
+}
+
+RTE_EXPORT_EXPERIMENTAL_SYMBOL(rte_pci_tph_st_get, 26.07)
+int
+rte_pci_tph_st_get(const struct rte_pci_device *dev,
+		   struct rte_pci_tph_entry *ents, uint32_t count)
+{
+	RTE_SET_USED(dev);
+	RTE_SET_USED(ents);
+	RTE_SET_USED(count);
+	return -ENOTSUP;
+}
+
+RTE_EXPORT_EXPERIMENTAL_SYMBOL(rte_pci_tph_st_set, 26.07)
+int
+rte_pci_tph_st_set(const struct rte_pci_device *dev,
+		   struct rte_pci_tph_entry *ents, uint32_t count)
+{
+	RTE_SET_USED(dev);
+	RTE_SET_USED(ents);
+	RTE_SET_USED(count);
+	return -ENOTSUP;
+}
diff --git a/drivers/bus/pci/linux/pci.c b/drivers/bus/pci/linux/pci.c
index 5f263f8b28..d2eade14a5 100644
--- a/drivers/bus/pci/linux/pci.c
+++ b/drivers/bus/pci/linux/pci.c
@@ -791,3 +791,51 @@ rte_pci_ioport_unmap(struct rte_pci_ioport *p)
 
 	return ret;
 }
+
+RTE_EXPORT_EXPERIMENTAL_SYMBOL(rte_pci_tph_query, 26.07)
+int
+rte_pci_tph_query(const struct rte_pci_device *dev, uint32_t *supported_modes,
+		  uint32_t *st_table_sz)
+{
+	if (dev->kdrv == RTE_PCI_KDRV_VFIO && pci_vfio_is_enabled())
+		return pci_vfio_tph_query(dev, supported_modes, st_table_sz);
+	return -ENOTSUP;
+}
+
+RTE_EXPORT_EXPERIMENTAL_SYMBOL(rte_pci_tph_enable, 26.07)
+int
+rte_pci_tph_enable(const struct rte_pci_device *dev, uint32_t mode)
+{
+	if (dev->kdrv == RTE_PCI_KDRV_VFIO && pci_vfio_is_enabled())
+		return pci_vfio_tph_enable(dev, mode);
+	return -ENOTSUP;
+}
+
+RTE_EXPORT_EXPERIMENTAL_SYMBOL(rte_pci_tph_disable, 26.07)
+int
+rte_pci_tph_disable(const struct rte_pci_device *dev)
+{
+	if (dev->kdrv == RTE_PCI_KDRV_VFIO && pci_vfio_is_enabled())
+		return pci_vfio_tph_disable(dev);
+	return -ENOTSUP;
+}
+
+RTE_EXPORT_EXPERIMENTAL_SYMBOL(rte_pci_tph_st_get, 26.07)
+int
+rte_pci_tph_st_get(const struct rte_pci_device *dev,
+		   struct rte_pci_tph_entry *ents, uint32_t count)
+{
+	if (dev->kdrv == RTE_PCI_KDRV_VFIO && pci_vfio_is_enabled())
+		return pci_vfio_tph_st_get(dev, ents, count);
+	return -ENOTSUP;
+}
+
+RTE_EXPORT_EXPERIMENTAL_SYMBOL(rte_pci_tph_st_set, 26.07)
+int
+rte_pci_tph_st_set(const struct rte_pci_device *dev,
+		   struct rte_pci_tph_entry *ents, uint32_t count)
+{
+	if (dev->kdrv == RTE_PCI_KDRV_VFIO && pci_vfio_is_enabled())
+		return pci_vfio_tph_st_set(dev, ents, count);
+	return -ENOTSUP;
+}
diff --git a/drivers/bus/pci/linux/pci_init.h b/drivers/bus/pci/linux/pci_init.h
index 6949dd57d9..7cbdc7e807 100644
--- a/drivers/bus/pci/linux/pci_init.h
+++ b/drivers/bus/pci/linux/pci_init.h
@@ -73,4 +73,13 @@ int pci_vfio_unmap_resource(struct rte_pci_device *dev);
 
 int pci_vfio_is_enabled(void);
 
+int pci_vfio_tph_query(const struct rte_pci_device *dev, uint32_t *supported_modes,
+		   uint32_t *st_table_sz);
+int pci_vfio_tph_enable(const struct rte_pci_device *dev, uint32_t mode);
+int pci_vfio_tph_disable(const struct rte_pci_device *dev);
+int pci_vfio_tph_st_get(const struct rte_pci_device *dev,
+		    struct rte_pci_tph_entry *ents, uint32_t count);
+int pci_vfio_tph_st_set(const struct rte_pci_device *dev,
+		    struct rte_pci_tph_entry *ents, uint32_t count);
+
 #endif /* EAL_PCI_INIT_H_ */
diff --git a/drivers/bus/pci/linux/pci_vfio.c b/drivers/bus/pci/linux/pci_vfio.c
index bc5c5c2499..d109f44d45 100644
--- a/drivers/bus/pci/linux/pci_vfio.c
+++ b/drivers/bus/pci/linux/pci_vfio.c
@@ -1308,3 +1308,275 @@ pci_vfio_is_enabled(void)
 	}
 	return status;
 }
+
+/**
+ * struct vfio_pci_tph_cap - PCIe TPH capability information
+ * @supported_modes: Supported TPH operating modes
+ * @st_table_sz: Number of entries in ST table; 0 means no ST table
+ * @reserved: Must be zero
+ *
+ * Used with VFIO_PCI_TPH_GET_CAP operation to return device
+ * TLP Processing Hints (TPH) capabilities to userspace.
+ */
+struct vfio_pci_tph_cap {
+	__u8  supported_modes;
+#define VFIO_PCI_TPH_MODE_IV	(1u << 0) /* Interrupt vector */
+#define VFIO_PCI_TPH_MODE_DS	(1u << 1) /* Device specific */
+	__u8  reserved0;
+	__u16 st_table_sz;
+	__u32 reserved;
+};
+
+/**
+ * struct vfio_pci_tph_ctrl - TPH enable control structure
+ * @mode: Selected TPH operating mode (VFIO_PCI_TPH_MODE_*)
+ * @reserved: Must be zero
+ *
+ * Used with VFIO_PCI_TPH_ENABLE operation to specify the
+ * operating mode when enabling TPH on the device.
+ */
+struct vfio_pci_tph_ctrl {
+	__u8 mode;
+	__u8 reserved[7];
+};
+
+/**
+ * struct vfio_pci_tph_entry - Single TPH steering tag entry
+ * @cpu: CPU identifier for steering tag calculation
+ * @mem_type: Memory type (VFIO_PCI_TPH_MEM_TYPE_*)
+ * @reserved0: Must be zero
+ * @index: ST table index for programming
+ * @st: Unused for SET_ST
+ * @reserved1: Must be zero
+ *
+ * For VFIO_PCI_TPH_GET_ST:
+ *   Userspace sets @cpu and @mem_type; kernel returns @st.
+ *
+ * For VFIO_PCI_TPH_SET_ST:
+ *   Userspace sets @index, @cpu, and @mem_type.
+ *   Kernel internally computes the steering tag and programs
+ *   it into the specified @index.
+ *
+ *   If @cpu == U32_MAX, kernel clears the steering tag at
+ *   the specified @index.
+ */
+struct vfio_pci_tph_entry {
+	__u32 cpu;
+	__u8  mem_type;
+#define VFIO_PCI_TPH_MEM_TYPE_VM	0
+#define VFIO_PCI_TPH_MEM_TYPE_PM	1
+	__u8  reserved0;
+	__u16 index;
+	__u16 st;
+	__u16 reserved1;
+};
+
+/**
+ * struct vfio_pci_tph_st - Batch steering tag request
+ * @count: Number of entries in the array
+ * @reserved: Must be zero
+ * @ents: Flexible array of steering tag entries
+ *
+ * Container structure for batch get/set operations.
+ * Used with both VFIO_PCI_TPH_GET_ST and VFIO_PCI_TPH_SET_ST.
+ */
+struct vfio_pci_tph_st {
+	__u32 count;
+	__u32 reserved;
+	struct vfio_pci_tph_entry ents[];
+#define VFIO_PCI_TPH_MAX_ENTRIES    2048
+};
+
+/**
+ * struct vfio_device_pci_tph_op - Argument for VFIO_DEVICE_PCI_TPH
+ * @argsz: User allocated size of this structure
+ * @op: TPH operation (VFIO_PCI_TPH_*)
+ * @cap: Capability data for GET_CAP
+ * @ctrl: Control data for ENABLE
+ * @st: Batch entry data for GET_ST/SET_ST
+ *
+ * @argsz must be set by the user to the size of the structure
+ * being executed. Kernel validates input and returns data
+ * only within the specified size.
+ *
+ * Operations:
+ * - VFIO_PCI_TPH_GET_CAP: Query device TPH capabilities.
+ * - VFIO_PCI_TPH_ENABLE:  Enable TPH using mode from &ctrl.
+ * - VFIO_PCI_TPH_DISABLE: Disable TPH on the device.
+ * - VFIO_PCI_TPH_GET_ST:  Retrieve CPU steering tags for Device-Specific (DS)
+ *                         mode. Used when device requires SW to obtain ST
+ *                         values for programming.
+ * - VFIO_PCI_TPH_SET_ST:  Program steering tag entries into device ST table.
+ *                         Valid when ST table resides in TPH Requester
+ *                         Capability or MSI-X Table.
+ *                         If any entry fails, all programmed entries are rolled
+ *                         back to 0 before returning error.
+ */
+struct vfio_device_pci_tph_op {
+	__u32 argsz;
+	__u32 op;
+#define VFIO_PCI_TPH_GET_CAP	0
+#define VFIO_PCI_TPH_ENABLE	1
+#define VFIO_PCI_TPH_DISABLE	2
+#define VFIO_PCI_TPH_GET_ST	3
+#define VFIO_PCI_TPH_SET_ST	4
+	union {
+		struct vfio_pci_tph_cap cap;
+		struct vfio_pci_tph_ctrl ctrl;
+		struct vfio_pci_tph_st st;
+	};
+};
+
+/**
+ * VFIO_DEVICE_PCI_TPH - _IO(VFIO_TYPE, VFIO_BASE + 22)
+ *
+ * IOCTL for managing PCIe TLP Processing Hints (TPH) on
+ * a VFIO-assigned PCI device. Provides operations to query
+ * device capabilities, enable/disable TPH, retrieve CPU's
+ * steering tags, and program steering tag tables.
+ *
+ * Return: 0 on success, negative errno on failure.
+ *         -EOPNOTSUPP: Operation not supported
+ *         -ENODEV: Device or required functionality not present
+ *         -EINVAL: Invalid argument or TPH not supported
+ */
+#define VFIO_DEVICE_PCI_TPH	_IO(VFIO_TYPE, VFIO_BASE + 22)
+
+static int
+pci_vfio_tph_ioctl(const struct rte_pci_device *dev, struct vfio_device_pci_tph_op *op)
+{
+	const struct rte_intr_handle *intr_handle = dev->intr_handle;
+	int vfio_dev_fd;
+
+	vfio_dev_fd = rte_intr_dev_fd_get(intr_handle);
+	if (vfio_dev_fd < 0)
+		return -EIO;
+
+	return ioctl(vfio_dev_fd, VFIO_DEVICE_PCI_TPH, op);
+}
+
+int
+pci_vfio_tph_query(const struct rte_pci_device *dev, uint32_t *supported_modes,
+		   uint32_t *st_table_sz)
+{
+	struct vfio_device_pci_tph_op op = {
+		.argsz = sizeof(struct vfio_device_pci_tph_op),
+		.op = VFIO_PCI_TPH_GET_CAP,
+	};
+	int ret;
+
+	ret = pci_vfio_tph_ioctl(dev, &op);
+	if (ret != 0)
+		return ret;
+
+	*supported_modes = 0;
+	if (op.cap.supported_modes & VFIO_PCI_TPH_MODE_IV)
+		*supported_modes |= RTE_PCI_TPH_MODE_IV;
+	if (op.cap.supported_modes & VFIO_PCI_TPH_MODE_DS)
+		*supported_modes |= RTE_PCI_TPH_MODE_DS;
+	*st_table_sz = op.cap.st_table_sz;
+
+	return 0;
+}
+
+int
+pci_vfio_tph_enable(const struct rte_pci_device *dev, uint32_t mode)
+{
+	struct vfio_device_pci_tph_op op = {
+		.argsz = sizeof(struct vfio_device_pci_tph_op),
+		.op = VFIO_PCI_TPH_ENABLE,
+	};
+
+	if (mode == RTE_PCI_TPH_MODE_IV)
+		op.ctrl.mode = VFIO_PCI_TPH_MODE_IV;
+	else if (mode == RTE_PCI_TPH_MODE_DS)
+		op.ctrl.mode = VFIO_PCI_TPH_MODE_DS;
+	else
+		return -EINVAL;
+
+	return pci_vfio_tph_ioctl(dev, &op);
+}
+
+int
+pci_vfio_tph_disable(const struct rte_pci_device *dev)
+{
+	struct vfio_device_pci_tph_op op = {
+		.argsz = sizeof(struct vfio_device_pci_tph_op),
+		.op = VFIO_PCI_TPH_DISABLE,
+	};
+	return pci_vfio_tph_ioctl(dev, &op);
+}
+
+static struct vfio_device_pci_tph_op *
+pci_vfio_tph_alloc_st_op(uint32_t count)
+{
+	struct vfio_device_pci_tph_op *op;
+	ssize_t sz = sizeof(struct vfio_device_pci_tph_op) +
+			count * sizeof(struct vfio_pci_tph_entry);
+	op = calloc(1, sz);
+	if (op == NULL)
+		return NULL;
+	op->argsz = sz;
+	op->st.count = count;
+	return op;
+}
+
+static void
+pci_vfio_tph_free_st_op(struct vfio_device_pci_tph_op *op)
+{
+	free(op);
+}
+
+int
+pci_vfio_tph_st_get(const struct rte_pci_device *dev,
+		    struct rte_pci_tph_entry *ents, uint32_t count)
+{
+	struct vfio_device_pci_tph_op *op;
+	uint32_t i;
+	int ret;
+
+	op = pci_vfio_tph_alloc_st_op(count);
+	if (op == NULL)
+		return -ENOMEM;
+
+	op->op = VFIO_PCI_TPH_GET_ST;
+	for (i = 0; i < count; i++) {
+		op->st.ents[i].cpu = ents[i].cpu;
+		op->st.ents[i].mem_type = VFIO_PCI_TPH_MEM_TYPE_VM;
+	}
+
+	ret = pci_vfio_tph_ioctl(dev, op);
+	if (ret != 0) {
+		pci_vfio_tph_free_st_op(op);
+		return ret;
+	}
+	for (i = 0; i < count; i++)
+		ents[i].st = op->st.ents[i].st;
+
+	pci_vfio_tph_free_st_op(op);
+	return 0;
+}
+
+int
+pci_vfio_tph_st_set(const struct rte_pci_device *dev,
+		    struct rte_pci_tph_entry *ents, uint32_t count)
+{
+	struct vfio_device_pci_tph_op *op;
+	uint32_t i;
+	int ret;
+
+	op = pci_vfio_tph_alloc_st_op(count);
+	if (op == NULL)
+		return -ENOMEM;
+
+	op->op = VFIO_PCI_TPH_SET_ST;
+	for (i = 0; i < count; i++) {
+		op->st.ents[i].cpu = ents[i].cpu;
+		op->st.ents[i].mem_type = VFIO_PCI_TPH_MEM_TYPE_VM;
+		op->st.ents[i].index = ents[i].index;
+	}
+
+	ret = pci_vfio_tph_ioctl(dev, op);
+	pci_vfio_tph_free_st_op(op);
+	return ret;
+}
diff --git a/drivers/bus/pci/rte_bus_pci.h b/drivers/bus/pci/rte_bus_pci.h
index 19a7b15b99..58dd4620a3 100644
--- a/drivers/bus/pci/rte_bus_pci.h
+++ b/drivers/bus/pci/rte_bus_pci.h
@@ -312,6 +312,118 @@ void rte_pci_ioport_read(struct rte_pci_ioport *p,
 void rte_pci_ioport_write(struct rte_pci_ioport *p,
 		const void *data, size_t len, off_t offset);
 
+#define RTE_PCI_TPH_MODE_IV	(1u << 0) /* Interrupt vector */
+#define RTE_PCI_TPH_MODE_DS	(1u << 1) /* Device specific */
+
+/**
+ * @struct rte_pci_tph_entry
+ * @warning
+ * @b EXPERIMENTAL: this structure may change without prior notice.
+ *
+ * An entry used for TPH Steering Tag (ST) get/set operations.
+ */
+struct rte_pci_tph_entry {
+	/**
+	 * CPU ID used for both get and set operations.
+	 * For set operation: if set to U32_MAX, clear the ST entry at
+	 * specified index.
+	 */
+	uint32_t cpu;
+	/** ST table index, only used for set operation */
+	uint16_t index;
+	/** Steering tag value, only used for get operation to return result */
+	uint16_t st;
+};
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Query PCIe TLP Processing Hints (TPH) capabilities of a device.
+ *
+ * @param dev
+ *   A pointer to a rte_pci_device structure describing the device to query.
+ * @param supported_modes
+ *   Output: supported TPH modes (RTE_PCI_TPH_MODE_*).
+ * @param st_table_sz
+ *   Output: number of entries in the ST table; 0 means no table present.
+ * @return
+ *   0 on success, negative value on error.
+ */
+__rte_experimental
+int rte_pci_tph_query(const struct rte_pci_device *dev, uint32_t *supported_modes,
+		  uint32_t *st_table_sz);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Enable PCIe TLP Processing Hints (TPH) on a device with specified mode.
+ *
+ * @param dev
+ *   A pointer to a rte_pci_device structure describing the device to enable.
+ * @param mode
+ *   TPH operating mode (RTE_PCI_TPH_MODE_*).
+ * @return
+ *   0 on success, negative value on error.
+ */
+__rte_experimental
+int rte_pci_tph_enable(const struct rte_pci_device *dev, uint32_t mode);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Disable PCIe TLP Processing Hints (TPH) on a device.
+ *
+ * @param dev
+ *   A pointer to a rte_pci_device structure describing the device to disable.
+ * @return
+ *   0 on success, negative value on error.
+ */
+__rte_experimental
+int rte_pci_tph_disable(const struct rte_pci_device *dev);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Get steering tags for given CPU IDs from the device.
+ * Only valid when TPH is enabled in Device-Specific (DS) mode.
+ *
+ * @param dev
+ *   A pointer to a rte_pci_device structure describing the device.
+ * @param ents
+ *   Array of entries with CPU IDs as input; steering tags are returned
+ *   as output.
+ * @param count
+ *   Number of entries in the array.
+ * @return
+ *   0 on success, negative value on error.
+ */
+__rte_experimental
+int rte_pci_tph_st_get(const struct rte_pci_device *dev,
+		   struct rte_pci_tph_entry *ents, uint32_t count);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Program steering tags into the device's ST table.
+ *
+ * @param dev
+ *   A pointer to a rte_pci_device structure describing the device.
+ * @param ents
+ *   Array of entries with CPU IDs and index indices to program.
+ * @param count
+ *   Number of entries in the array.
+ * @return
+ *   0 on success, negative errno value on error.
+ */
+__rte_experimental
+int rte_pci_tph_st_set(const struct rte_pci_device *dev,
+		   struct rte_pci_tph_entry *ents, uint32_t count);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/drivers/bus/pci/windows/pci.c b/drivers/bus/pci/windows/pci.c
index 549319ad5b..0a2f0408ca 100644
--- a/drivers/bus/pci/windows/pci.c
+++ b/drivers/bus/pci/windows/pci.c
@@ -207,6 +207,56 @@ pci_uio_remap_resource(struct rte_pci_device *dev __rte_unused)
 	return -1;
 }
 
+RTE_EXPORT_EXPERIMENTAL_SYMBOL(rte_pci_tph_query, 26.07)
+int
+rte_pci_tph_query(const struct rte_pci_device *dev, uint32_t *supported_modes,
+		  uint32_t *st_table_sz)
+{
+	RTE_SET_USED(dev);
+	RTE_SET_USED(supported_modes);
+	RTE_SET_USED(st_table_sz);
+	return -ENOTSUP;
+}
+
+RTE_EXPORT_EXPERIMENTAL_SYMBOL(rte_pci_tph_enable, 26.07)
+int
+rte_pci_tph_enable(const struct rte_pci_device *dev, uint32_t mode)
+{
+	RTE_SET_USED(dev);
+	RTE_SET_USED(mode);
+	return -ENOTSUP;
+}
+
+RTE_EXPORT_EXPERIMENTAL_SYMBOL(rte_pci_tph_disable, 26.07)
+int
+rte_pci_tph_disable(const struct rte_pci_device *dev)
+{
+	RTE_SET_USED(dev);
+	return -ENOTSUP;
+}
+
+RTE_EXPORT_EXPERIMENTAL_SYMBOL(rte_pci_tph_st_get, 26.07)
+int
+rte_pci_tph_st_get(const struct rte_pci_device *dev,
+		   struct rte_pci_tph_entry *ents, uint32_t count)
+{
+	RTE_SET_USED(dev);
+	RTE_SET_USED(ents);
+	RTE_SET_USED(count);
+	return -ENOTSUP;
+}
+
+RTE_EXPORT_EXPERIMENTAL_SYMBOL(rte_pci_tph_st_set, 26.07)
+int
+rte_pci_tph_st_set(const struct rte_pci_device *dev,
+		   struct rte_pci_tph_entry *ents, uint32_t count)
+{
+	RTE_SET_USED(dev);
+	RTE_SET_USED(ents);
+	RTE_SET_USED(count);
+	return -ENOTSUP;
+}
+
 static int
 get_device_pci_address(HDEVINFO dev_info,
 	PSP_DEVINFO_DATA device_info_data, struct rte_pci_addr *addr)
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [PATCH v1 2/4] ethdev: introduce generic cache stash API
  2026-05-08  9:28 [PATCH v1 0/4] Introduce generic cache stash API and PCIe TPH implementation Chengwen Feng
  2026-05-08  9:28 ` [PATCH v1 1/4] bus/pci: introduce PCIe TPH support Chengwen Feng
@ 2026-05-08  9:28 ` Chengwen Feng
  2026-05-08  9:28 ` [PATCH v1 3/4] net/e1000: add cache stash support via TPH Chengwen Feng
                   ` (4 subsequent siblings)
  6 siblings, 0 replies; 13+ messages in thread
From: Chengwen Feng @ 2026-05-08  9:28 UTC (permalink / raw)
  To: thomas, stephen; +Cc: dev, wathsala.vithanage

Introduce a generic Ethernet device API for cache stashing,
supporting hardware mechanisms such as PCIe TPH (Transaction
Processing Hints).

The API provides:
- Generic query for supported stashing types and objects
- Device-wide stashing enable/disable
- Per-queue stashing with target lcore and data objects
  (Rx/Tx descriptor, header, payload)

The design allows a device to support multiple stashing types
while only allowing one active type at runtime.

Signed-off-by: Chengwen Feng <fengchengwen@huawei.com>
---
 lib/ethdev/ethdev_driver.h | 37 +++++++++++++++
 lib/ethdev/rte_ethdev.c    | 40 ++++++++++++++++
 lib/ethdev/rte_ethdev.h    | 95 ++++++++++++++++++++++++++++++++++++++
 3 files changed, 172 insertions(+)

diff --git a/lib/ethdev/ethdev_driver.h b/lib/ethdev/ethdev_driver.h
index 1255cd6f2c..e7b4167939 100644
--- a/lib/ethdev/ethdev_driver.h
+++ b/lib/ethdev/ethdev_driver.h
@@ -1409,6 +1409,38 @@ enum rte_eth_dev_operation {
 typedef uint64_t (*eth_get_restore_flags_t)(struct rte_eth_dev *dev,
 					    enum rte_eth_dev_operation op);
 
+/**
+ * @internal
+ * Get cache stash capabilities of the Ethernet device.
+ *
+ * @param dev
+ *   Port (ethdev) handle.
+ * @param capa
+ *   Pointer to capability structure to store supported stash types and objects.
+ *
+ * @return
+ *   Negative on error, 0 on success.
+ */
+typedef int (*eth_cache_stash_get_t)(struct rte_eth_dev *dev,
+				     struct rte_eth_cache_stash_capability *capa);
+
+/**
+ * @internal
+ * Configure cache stash for the Ethernet device or queue.
+ *
+ * @param dev
+ *   Port (ethdev) handle.
+ * @param op
+ *   Cache stash operation type (enable/disable device or queue).
+ * @param config
+ *   Cache stash configuration (device or queue level).
+ *
+ * @return
+ *   Negative on error, 0 on success.
+ */
+typedef int (*eth_cache_stash_set_t)(struct rte_eth_dev *dev, enum rte_eth_cache_stash_op op,
+				     struct rte_eth_cache_stash_config *config);
+
 /**
  * @internal A structure containing the functions exported by an Ethernet driver.
  */
@@ -1661,6 +1693,11 @@ struct eth_dev_ops {
 
 	/** Get configuration which ethdev should restore */
 	eth_get_restore_flags_t get_restore_flags;
+
+	/** Cache stash get ops */
+	eth_cache_stash_get_t cache_stash_get;
+	/** Cache stash set ops */
+	eth_cache_stash_set_t cache_stash_set;
 };
 
 /**
diff --git a/lib/ethdev/rte_ethdev.c b/lib/ethdev/rte_ethdev.c
index 2edc7a362e..4398aca5e1 100644
--- a/lib/ethdev/rte_ethdev.c
+++ b/lib/ethdev/rte_ethdev.c
@@ -7460,5 +7460,45 @@ int rte_eth_dev_map_aggr_tx_affinity(uint16_t port_id, uint16_t tx_queue_id,
 	return ret;
 }
 
+RTE_EXPORT_EXPERIMENTAL_SYMBOL(rte_eth_cache_stash_get, 26.03)
+int
+rte_eth_cache_stash_get(uint16_t port_id,
+			struct rte_eth_cache_stash_capability *capa)
+{
+	struct rte_eth_dev *dev;
+
+	RTE_ETH_VALID_PORTID_OR_ERR_RET(port_id, -ENODEV);
+
+	if (capa == NULL)
+		return -EINVAL;
+
+	dev = &rte_eth_devices[port_id];
+
+	if (*dev->dev_ops->cache_stash_get == NULL)
+		return -ENOTSUP;
+
+	return eth_err(port_id, (*dev->dev_ops->cache_stash_get)(dev, capa));
+}
+
+RTE_EXPORT_EXPERIMENTAL_SYMBOL(rte_eth_cache_stash_set, 26.03)
+int
+rte_eth_cache_stash_set(uint16_t port_id, enum rte_eth_cache_stash_op op,
+		       struct rte_eth_cache_stash_config *config)
+{
+	struct rte_eth_dev *dev;
+
+	RTE_ETH_VALID_PORTID_OR_ERR_RET(port_id, -ENODEV);
+
+	if (op != RTE_ETH_CACHE_STASH_OP_DEV_DISABLE && config == NULL)
+		return -EINVAL;
+
+	dev = &rte_eth_devices[port_id];
+
+	if (*dev->dev_ops->cache_stash_set == NULL)
+		return -ENOTSUP;
+
+	return eth_err(port_id, (*dev->dev_ops->cache_stash_set)(dev, op, config));
+}
+
 RTE_EXPORT_SYMBOL(rte_eth_dev_logtype)
 RTE_LOG_REGISTER_DEFAULT(rte_eth_dev_logtype, INFO);
diff --git a/lib/ethdev/rte_ethdev.h b/lib/ethdev/rte_ethdev.h
index 0d8e2d0236..014f0c21b6 100644
--- a/lib/ethdev/rte_ethdev.h
+++ b/lib/ethdev/rte_ethdev.h
@@ -7178,6 +7178,101 @@ rte_eth_tx_queue_count(uint16_t port_id, uint16_t queue_id)
 	return rc;
 }
 
+/**
+ * Cache stash mechanisms (bitmask).
+ * A device may support multiple types but can only enable one at a time.
+ */
+enum rte_eth_cache_stash_type {
+	RTE_ETH_CACHE_STASH_TYPE_TPH = RTE_BIT32(0),
+};
+
+/**
+ * Cache stash objects (bitmask).
+ */
+#define RTE_ETH_CACHE_STASH_OBJ_RX_DESC		RTE_BIT64(0)
+#define RTE_ETH_CACHE_STASH_OBJ_TX_DESC         RTE_BIT64(1)
+#define RTE_ETH_CACHE_STASH_OBJ_RX_HEADER	RTE_BIT64(2)
+#define RTE_ETH_CACHE_STASH_OBJ_TX_HEADER       RTE_BIT64(3)
+#define RTE_ETH_CACHE_STASH_OBJ_RX_PAYLOAD	RTE_BIT64(4)
+#define RTE_ETH_CACHE_STASH_OBJ_TX_PAYLOAD	RTE_BIT64(5)
+
+/**
+ * Cache stash capability.
+ */
+struct rte_eth_cache_stash_capability {
+	uint32_t supported_types;  /**< Bitmask of rte_eth_cache_stash_type */
+	uint64_t supported_objects;/**< Bitmask of RTE_ETH_CACHE_STASH_OBJ_* */
+};
+
+/**
+ * Cache stash operations.
+ */
+enum rte_eth_cache_stash_op {
+	RTE_ETH_CACHE_STASH_OP_DEV_ENABLE,	/**< Enable device-wide cache stash */
+	RTE_ETH_CACHE_STASH_OP_DEV_DISABLE,	/**< Disable device-wide cache stash */
+	RTE_ETH_CACHE_STASH_OP_QUEUE_ENABLE,	/**< Enable cache stash for a queue */
+	RTE_ETH_CACHE_STASH_OP_QUEUE_DISABLE,	/**< Disable cache stash for a queue */
+	RTE_ETH_CACHE_STASH_OP_BUTT
+};
+
+/**
+ * Cache stash configuration.
+ * The used union member is determined by the operation.
+ */
+struct rte_eth_cache_stash_config {
+	union {
+		/**
+		 * Device-level configuration (used with DEV_ENABLE).
+		 */
+		struct {
+			uint32_t type;	/**< Selected stash mechanism type */
+		} dev;
+		/**
+		 * Queue-level configuration (used with QUEUE_ENABLE/QUEUE_DISABLE).
+		 */
+		struct {
+			uint32_t lcore_id;	/**< Target CPU core ID */
+			uint32_t queue_id;	/**< Queue ID */
+			uint64_t objects;	/**< Objects bitmask to stash */
+		} queue;
+	};
+};
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Get cache stash capabilities of a port.
+ *
+ * @param port_id
+ *   The port identifier.
+ * @param capa
+ *   Output: supported stash types and objects.
+ * @return
+ *   0 on success, negative on error.
+ */
+__rte_experimental
+int rte_eth_cache_stash_get(uint16_t port_id, struct rte_eth_cache_stash_capability *capa);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Configure cache stash.
+ *
+ * @param port_id
+ *   The port identifier.
+ * @param op
+ *   Configuration operation.
+ * @param config
+ *   Configuration structure.
+ * @return
+ *   0 on success, negative on error.
+ */
+__rte_experimental
+int rte_eth_cache_stash_set(uint16_t port_id, enum rte_eth_cache_stash_op op,
+			    struct rte_eth_cache_stash_config *config);
+
 #ifdef __cplusplus
 }
 #endif
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [PATCH v1 3/4] net/e1000: add cache stash support via TPH
  2026-05-08  9:28 [PATCH v1 0/4] Introduce generic cache stash API and PCIe TPH implementation Chengwen Feng
  2026-05-08  9:28 ` [PATCH v1 1/4] bus/pci: introduce PCIe TPH support Chengwen Feng
  2026-05-08  9:28 ` [PATCH v1 2/4] ethdev: introduce generic cache stash API Chengwen Feng
@ 2026-05-08  9:28 ` Chengwen Feng
  2026-05-08  9:28 ` [PATCH v1 4/4] app/testpmd: add TPH stash objects configuration Chengwen Feng
                   ` (3 subsequent siblings)
  6 siblings, 0 replies; 13+ messages in thread
From: Chengwen Feng @ 2026-05-08  9:28 UTC (permalink / raw)
  To: thomas, stephen; +Cc: dev, wathsala.vithanage

Implement ethdev generic cache stash operations for igb PMD
using PCIe TPH (Transaction Processing Hints) as the underlying
mechanism.

The implementation supports:
- Query TPH based stash capabilities
- Device-level global enable/disable of TPH
- Per-queue TPH stashing configuration with target lcore
- Configure stashing objects (Rx descriptor, payload, etc.)

Signed-off-by: Chengwen Feng <fengchengwen@huawei.com>
---
 drivers/net/intel/e1000/base/e1000_hw.h |   2 +
 drivers/net/intel/e1000/base/e1000_vf.h |   2 +
 drivers/net/intel/e1000/igb_ethdev.c    | 221 ++++++++++++++++++++++++
 3 files changed, 225 insertions(+)

diff --git a/drivers/net/intel/e1000/base/e1000_hw.h b/drivers/net/intel/e1000/base/e1000_hw.h
index 9b1fafd75c..3e8e21b194 100644
--- a/drivers/net/intel/e1000/base/e1000_hw.h
+++ b/drivers/net/intel/e1000/base/e1000_hw.h
@@ -1092,6 +1092,8 @@ struct e1000_hw {
 	u16 vendor_id;
 
 	u8  revision_id;
+
+	u8 tph_mode;
 };
 
 #include "e1000_82541.h"
diff --git a/drivers/net/intel/e1000/base/e1000_vf.h b/drivers/net/intel/e1000/base/e1000_vf.h
index 4bec21c935..dd3cef254e 100644
--- a/drivers/net/intel/e1000/base/e1000_vf.h
+++ b/drivers/net/intel/e1000/base/e1000_vf.h
@@ -248,6 +248,8 @@ struct e1000_hw {
 	u16 vendor_id;
 
 	u8  revision_id;
+
+	u8 tph_mode;
 };
 
 enum e1000_promisc_type {
diff --git a/drivers/net/intel/e1000/igb_ethdev.c b/drivers/net/intel/e1000/igb_ethdev.c
index ef1599ac38..7aea37d017 100644
--- a/drivers/net/intel/e1000/igb_ethdev.c
+++ b/drivers/net/intel/e1000/igb_ethdev.c
@@ -241,6 +241,10 @@ static int igb_tx_burst_mode_get(struct rte_eth_dev *dev,
 	__rte_unused uint16_t queue_id, struct rte_eth_burst_mode *mode);
 static int igb_rx_burst_mode_get(struct rte_eth_dev *dev,
 	__rte_unused uint16_t queue_id, struct rte_eth_burst_mode *mode);
+static int eth_igb_cache_stash_get(struct rte_eth_dev *dev,
+				   struct rte_eth_cache_stash_capability *capa);
+static int eth_igb_cache_stash_set(struct rte_eth_dev *dev, enum rte_eth_cache_stash_op op,
+				   struct rte_eth_cache_stash_config *config);
 
 /*
  * Define VF Stats MACRO for Non "cleared on read" register
@@ -402,6 +406,8 @@ static const struct eth_dev_ops eth_igb_ops = {
 	.timesync_read_time   = igb_timesync_read_time,
 	.timesync_write_time  = igb_timesync_write_time,
 	.read_clock		      = eth_igb_read_clock,
+	.cache_stash_get      = eth_igb_cache_stash_get,
+	.cache_stash_set      = eth_igb_cache_stash_set,
 };
 
 /*
@@ -5692,6 +5698,221 @@ igb_filter_restore(struct rte_eth_dev *dev)
 	return 0;
 }
 
+static int
+eth_igb_cache_stash_get(struct rte_eth_dev *dev, struct rte_eth_cache_stash_capability *capa)
+{
+	struct rte_pci_device *pci_dev = RTE_ETH_DEV_TO_PCI(dev);
+	uint32_t support_modes, st_table_sz;
+	int ret;
+
+	ret = rte_pci_tph_query(pci_dev, &support_modes, &st_table_sz);
+	if (ret != 0)
+		return -ENOTSUP;
+	capa->supported_types = RTE_ETH_CACHE_STASH_TYPE_TPH;
+	capa->supported_objects = RTE_ETH_CACHE_STASH_OBJ_RX_DESC |
+				  RTE_ETH_CACHE_STASH_OBJ_RX_HEADER |
+				  RTE_ETH_CACHE_STASH_OBJ_RX_PAYLOAD |
+				  RTE_ETH_CACHE_STASH_OBJ_TX_DESC |
+				  RTE_ETH_CACHE_STASH_OBJ_TX_HEADER |
+				  RTE_ETH_CACHE_STASH_OBJ_TX_PAYLOAD;
+	return 0;
+}
+
+static int
+eth_igb_cache_stash_dev_enable(struct rte_eth_dev *dev,
+			       struct rte_eth_cache_stash_config *config)
+{
+	struct e1000_hw *hw = E1000_DEV_PRIVATE_TO_HW(dev->data->dev_private);
+	struct rte_pci_device *pci_dev = RTE_ETH_DEV_TO_PCI(dev);
+	uint32_t support_modes, st_table_sz;
+	uint32_t mode;
+	int ret;
+
+	if (hw->tph_mode != 0) {
+		PMD_DRV_LOG(ERR, "Already enable tph_mode=%u", hw->tph_mode);
+		return -EINVAL;
+	}
+
+	if (config->dev.type != RTE_ETH_CACHE_STASH_TYPE_TPH) {
+		PMD_DRV_LOG(ERR, "Unsupported stash type=%u!", config->dev.type);
+		return -ENOTSUP;
+	}
+
+	ret = rte_pci_tph_query(pci_dev, &support_modes, &st_table_sz);
+	if (ret != 0) {
+		PMD_DRV_LOG(ERR, "Query TPH failed! ret=%d", ret);
+		return -ENOTSUP;
+	}
+
+	if (support_modes & RTE_PCI_TPH_MODE_IV)
+		mode = RTE_PCI_TPH_MODE_IV;
+	else if (support_modes & RTE_PCI_TPH_MODE_DS)
+		mode = RTE_PCI_TPH_MODE_DS;
+	else
+		return -ENOTSUP;
+	ret = rte_pci_tph_enable(pci_dev, mode);
+	if (ret == 0)
+		hw->tph_mode = mode;
+
+	return ret;
+}
+
+static void
+eth_igb_cache_stash_dev_clear(struct rte_eth_dev *dev)
+{
+	uint32_t nb_queues = RTE_MAX(dev->data->nb_rx_queues, dev->data->nb_tx_queues);
+	struct e1000_hw *hw = E1000_DEV_PRIVATE_TO_HW(dev->data->dev_private);
+	struct rte_pci_device *pci_dev = RTE_ETH_DEV_TO_PCI(dev);
+	struct rte_pci_tph_entry ent = {0};
+	uint32_t reg;
+	uint32_t i;
+
+	for (i = 0; i < nb_queues; i++) {
+		ent.cpu = UINT32_MAX;
+		ent.index = i;
+		rte_pci_tph_st_set(pci_dev, &ent, 1);
+		reg = E1000_READ_REG(hw, E1000_RXCTL(i));
+		reg &= ~RTE_BIT32(0);
+		reg &= ~RTE_BIT32(1);
+		reg &= ~RTE_BIT32(2);
+		reg &= ~RTE_BIT32(3);
+		reg &= ~RTE_SHIFT_VAL32(0xFF, 24);
+		E1000_WRITE_REG(hw, E1000_RXCTL(i), reg);
+	}
+}
+
+static int
+eth_igb_cache_stash_dev_disable(struct rte_eth_dev *dev)
+{
+	struct e1000_hw *hw = E1000_DEV_PRIVATE_TO_HW(dev->data->dev_private);
+	struct rte_pci_device *pci_dev = RTE_ETH_DEV_TO_PCI(dev);
+	int ret;
+
+	if (hw->tph_mode == 0) {
+		PMD_DRV_LOG(ERR, "Already disable TPH!");
+		return -EINVAL;
+	}
+
+	eth_igb_cache_stash_dev_clear(dev);
+	ret = rte_pci_tph_disable(pci_dev);
+	if (ret == 0)
+		hw->tph_mode = 0;
+
+	return ret;
+}
+
+static int
+eth_igb_cache_stash_queue_enable(struct rte_eth_dev *dev,
+				 struct rte_eth_cache_stash_config *config)
+{
+	struct e1000_hw *hw = E1000_DEV_PRIVATE_TO_HW(dev->data->dev_private);
+	struct rte_pci_device *pci_dev = RTE_ETH_DEV_TO_PCI(dev);
+	uint32_t queue_id = config->queue.queue_id;
+	struct rte_pci_tph_entry ent = {0};
+	uint32_t reg;
+	uint16_t st;
+	int ret;
+
+	if (hw->tph_mode == 0) {
+		PMD_DRV_LOG(ERR, "Device TPH was not enabled!");
+		return -EINVAL;
+	}
+
+	if (queue_id > RTE_MAX(dev->data->nb_rx_queues, dev->data->nb_tx_queues)) {
+		PMD_DRV_LOG(ERR, "Invalid queue_id when enable stash!");
+		return -EINVAL;
+	}
+
+	ent.cpu = config->queue.lcore_id;
+	ent.index = queue_id;
+	ret = rte_pci_tph_st_get(pci_dev, &ent, 1);
+	if (ret != 0) {
+		PMD_DRV_LOG(ERR, "Failed to get device queue=%u st of cpu=%u ret=%u",
+			    queue_id, config->queue.lcore_id, ret);
+		return ret;
+	}
+	st = ent.st;
+
+	reg = E1000_READ_REG(hw, E1000_RXCTL(queue_id));
+	PMD_DRV_LOG(DEBUG, "[Enable] Queue=%u rxctl register init val=0x%x", queue_id, reg);
+	if (config->queue.objects & RTE_ETH_CACHE_STASH_OBJ_RX_DESC) {
+		reg |= RTE_BIT32(0);
+		reg |= RTE_BIT32(1);
+	} else {
+		reg &= ~RTE_BIT32(0);
+		reg &= ~RTE_BIT32(1);
+	}
+	if (config->queue.objects & RTE_ETH_CACHE_STASH_OBJ_RX_HEADER)
+		reg |= RTE_BIT32(2);
+	else
+		reg &= ~RTE_BIT32(2);
+	if (config->queue.objects & RTE_ETH_CACHE_STASH_OBJ_RX_PAYLOAD)
+		reg |= RTE_BIT32(3);
+	else
+		reg &= ~RTE_BIT32(3);
+	/* I350 must encoding st in high 8bit, and ST in config-space is no needed! */
+	reg |= RTE_SHIFT_VAL32(st, 24);
+	E1000_WRITE_REG(hw, E1000_RXCTL(queue_id), reg);
+	PMD_DRV_LOG(DEBUG, "[Enable] Queue=%u rxctl register after val=0x%x", queue_id, reg);
+
+	PMD_DRV_LOG(DEBUG, "[Enable] Enable device queue=%u st of cpu=%u success!",
+			    queue_id, config->queue.lcore_id);
+
+	return 0;
+}
+
+static int
+eth_igb_cache_stash_queue_disable(struct rte_eth_dev *dev,
+				  struct rte_eth_cache_stash_config *config)
+{
+	struct e1000_hw *hw = E1000_DEV_PRIVATE_TO_HW(dev->data->dev_private);
+	uint32_t queue_id = config->queue.queue_id;
+	uint32_t reg;
+
+	if (hw->tph_mode == 0) {
+		PMD_DRV_LOG(ERR, "Device TPH was not enabled!");
+		return -EINVAL;
+	}
+
+	if (queue_id > RTE_MAX(dev->data->nb_rx_queues, dev->data->nb_tx_queues)) {
+		PMD_DRV_LOG(ERR, "Invalid queue_id when enable stash!");
+		return -EINVAL;
+	}
+
+	reg = E1000_READ_REG(hw, E1000_RXCTL(queue_id));
+	PMD_DRV_LOG(DEBUG, "[Disable] Queue=%u rxctl register init val=0x%x", queue_id, reg);
+	reg &= ~RTE_BIT32(0);
+	reg &= ~RTE_BIT32(1);
+	reg &= ~RTE_BIT32(2);
+	reg &= ~RTE_BIT32(3);
+	reg &= ~RTE_SHIFT_VAL32(0xFF, 24);
+	E1000_WRITE_REG(hw, E1000_RXCTL(queue_id), reg);
+	PMD_DRV_LOG(DEBUG, "[Disable] Queue=%u rxctl register after val=0x%x", queue_id, reg);
+
+	PMD_DRV_LOG(DEBUG, "[Disable] disable device queue=%u st of cpu=%u success!",
+			    queue_id, config->queue.lcore_id);
+
+	return 0;
+}
+
+static int
+eth_igb_cache_stash_set(struct rte_eth_dev *dev, enum rte_eth_cache_stash_op op,
+			struct rte_eth_cache_stash_config *config)
+{
+	switch (op) {
+	case RTE_ETH_CACHE_STASH_OP_DEV_ENABLE:
+		return eth_igb_cache_stash_dev_enable(dev, config);
+	case RTE_ETH_CACHE_STASH_OP_DEV_DISABLE:
+		return eth_igb_cache_stash_dev_disable(dev);
+	case RTE_ETH_CACHE_STASH_OP_QUEUE_ENABLE:
+		return eth_igb_cache_stash_queue_enable(dev, config);
+	case RTE_ETH_CACHE_STASH_OP_QUEUE_DISABLE:
+		return eth_igb_cache_stash_queue_disable(dev, config);
+	default:
+		return -ENOTSUP;
+	}
+}
+
 RTE_PMD_REGISTER_PCI(net_e1000_igb, rte_igb_pmd);
 RTE_PMD_REGISTER_PCI_TABLE(net_e1000_igb, pci_id_igb_map);
 RTE_PMD_REGISTER_KMOD_DEP(net_e1000_igb, "* igb_uio | uio_pci_generic | vfio-pci");
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [PATCH v1 4/4] app/testpmd: add TPH stash objects configuration
  2026-05-08  9:28 [PATCH v1 0/4] Introduce generic cache stash API and PCIe TPH implementation Chengwen Feng
                   ` (2 preceding siblings ...)
  2026-05-08  9:28 ` [PATCH v1 3/4] net/e1000: add cache stash support via TPH Chengwen Feng
@ 2026-05-08  9:28 ` Chengwen Feng
  2026-05-12  9:22 ` [PATCH v2 0/4] Introduce generic cache stash API and PCIe TPH implementation Chengwen Feng
                   ` (2 subsequent siblings)
  6 siblings, 0 replies; 13+ messages in thread
From: Chengwen Feng @ 2026-05-08  9:28 UTC (permalink / raw)
  To: thomas, stephen; +Cc: dev, wathsala.vithanage

Add --tph-stash-objects command line option to configure PCIe TPH cache
stash objects for Rx queues. Implement enable/disable logic during
packet forwarding start/stop with device capability checks.

Signed-off-by: Chengwen Feng <fengchengwen@huawei.com>
---
 app/test-pmd/parameters.c | 20 ++++++++++
 app/test-pmd/testpmd.c    | 81 +++++++++++++++++++++++++++++++++++++++
 app/test-pmd/testpmd.h    |  1 +
 3 files changed, 102 insertions(+)

diff --git a/app/test-pmd/parameters.c b/app/test-pmd/parameters.c
index ecbd618f00..ae31d9ba4c 100644
--- a/app/test-pmd/parameters.c
+++ b/app/test-pmd/parameters.c
@@ -79,6 +79,8 @@ enum {
 	TESTPMD_OPT_NO_NUMA_NUM,
 #define TESTPMD_OPT_MP_ANON "mp-anon"
 	TESTPMD_OPT_MP_ANON_NUM,
+#define TESTPMD_OPT_TPH_STASH_OBJECTS "tph-stash-objects"
+	TESTPMD_OPT_TPH_STASH_OBJECTS_NUM,
 #define TESTPMD_OPT_PORT_NUMA_CONFIG "port-numa-config"
 	TESTPMD_OPT_PORT_NUMA_CONFIG_NUM,
 #define TESTPMD_OPT_RING_NUMA_CONFIG "ring-numa-config"
@@ -289,6 +291,7 @@ static const struct option long_options[] = {
 	NO_ARG(TESTPMD_OPT_NUMA),
 	NO_ARG(TESTPMD_OPT_NO_NUMA),
 	NO_ARG(TESTPMD_OPT_MP_ANON), /* deprecated */
+	REQUIRED_ARG(TESTPMD_OPT_TPH_STASH_OBJECTS),
 	REQUIRED_ARG(TESTPMD_OPT_PORT_NUMA_CONFIG),
 	REQUIRED_ARG(TESTPMD_OPT_RING_NUMA_CONFIG),
 	REQUIRED_ARG(TESTPMD_OPT_SOCKET_NUM),
@@ -1140,6 +1143,23 @@ launch_args_parse(int argc, char** argv)
 					"native, anon, xmem or xmemhuge\n",
 					optarg);
 			break;
+		case TESTPMD_OPT_TPH_STASH_OBJECTS_NUM:
+			if (!strcmp(optarg, "rxdesc"))
+				tph_stash_objects = RTE_ETH_CACHE_STASH_OBJ_RX_DESC;
+			else if (!strcmp(optarg, "rxheader"))
+				tph_stash_objects = RTE_ETH_CACHE_STASH_OBJ_RX_HEADER;
+			else if (!strcmp(optarg, "rxpayload"))
+				tph_stash_objects = RTE_ETH_CACHE_STASH_OBJ_RX_PAYLOAD;
+			else if (!strcmp(optarg, "rxdata"))
+				tph_stash_objects = RTE_ETH_CACHE_STASH_OBJ_RX_HEADER |
+						    RTE_ETH_CACHE_STASH_OBJ_RX_PAYLOAD;
+			else if (!strcmp(optarg, "rxall"))
+				tph_stash_objects = RTE_ETH_CACHE_STASH_OBJ_RX_DESC |
+						    RTE_ETH_CACHE_STASH_OBJ_RX_HEADER |
+						    RTE_ETH_CACHE_STASH_OBJ_RX_PAYLOAD;
+			else
+				rte_exit(EXIT_FAILURE, "unknown tph stash mode %s\n", optarg);
+			break;
 		case TESTPMD_OPT_PORT_NUMA_CONFIG_NUM:
 			if (parse_portnuma_config(optarg))
 				rte_exit(EXIT_FAILURE,
diff --git a/app/test-pmd/testpmd.c b/app/test-pmd/testpmd.c
index e2569d9e30..10d9218a9c 100644
--- a/app/test-pmd/testpmd.c
+++ b/app/test-pmd/testpmd.c
@@ -136,6 +136,11 @@ uint8_t socket_num = UMA_NO_CONFIG;
  */
 uint8_t mp_alloc_type = MP_ALLOC_NATIVE;
 
+/*
+ * PCIE TPH stash objects.
+ */
+uint64_t tph_stash_objects;
+
 /*
  * Store specified sockets on which memory pool to be used by ports
  * is allocated.
@@ -2540,6 +2545,77 @@ update_queue_state(portid_t pid)
 	}
 }
 
+static void
+start_tph_stash(void)
+{
+	struct rte_eth_cache_stash_capability capa;
+	struct rte_eth_cache_stash_config config;
+	struct fwd_config *cfg = &cur_fwd_config;
+	struct fwd_stream *fs;
+	lcoreid_t  lc_id;
+	streamid_t sm_id;
+	portid_t pt_id;
+	int ret;
+	int i;
+
+	for (i = 0; i < cur_fwd_config.nb_fwd_ports; i++) {
+		pt_id = fwd_ports_ids[i];
+		ret = rte_eth_cache_stash_get(pt_id, &capa);
+		if (ret != 0 || (capa.supported_types & RTE_ETH_CACHE_STASH_TYPE_TPH) == 0) {
+			fprintf(stderr,	"%s: (port %u) don't support tph stash!\n",
+				__func__, pt_id);
+			return;
+		}
+		if ((capa.supported_objects & tph_stash_objects) != tph_stash_objects) {
+			fprintf(stderr, "%s: (port %u) don't support objects=0x%lx 0x%lx\n",
+				__func__, pt_id, tph_stash_objects, capa.supported_objects);
+			return;
+		}
+		memset(&config, 0, sizeof(config));
+		config.dev.type = RTE_ETH_CACHE_STASH_TYPE_TPH;
+		ret = rte_eth_cache_stash_set(pt_id, RTE_ETH_CACHE_STASH_OP_DEV_ENABLE, &config);
+		if (ret != 0) {
+			fprintf(stderr, "%s: (port %u) enable tph failed! ret=%d\n",
+				__func__, pt_id, ret);
+			return;
+		}
+	}
+
+	for (lc_id = 0; lc_id < cfg->nb_fwd_lcores; lc_id++) {
+		for (sm_id = 0; sm_id < fwd_lcores[lc_id]->stream_nb; sm_id++) {
+			fs = fwd_streams[fwd_lcores[lc_id]->stream_idx + sm_id];
+			memset(&config, 0, sizeof(config));
+			config.queue.lcore_id = fwd_lcores_cpuids[lc_id];
+			config.queue.queue_id = fs->rx_queue;
+			config.queue.objects = tph_stash_objects;
+			ret = rte_eth_cache_stash_set(fs->rx_port,
+				RTE_ETH_CACHE_STASH_OP_QUEUE_ENABLE, &config);
+			if (ret != 0)
+				fprintf(stderr, "%s: (port %u) enable rx-queue=%u cpu=%u stash ret=%d\n",
+					__func__, fs->rx_port, fs->rx_queue,
+					fwd_lcores_cpuids[lc_id], ret);
+		}
+	}
+}
+
+static void
+stop_tph_stash(void)
+{
+	struct rte_eth_cache_stash_config config;
+	portid_t pt_id;
+	int ret;
+	int i;
+
+	for (i = 0; i < cur_fwd_config.nb_fwd_ports; i++) {
+		pt_id = fwd_ports_ids[i];
+		memset(&config, 0, sizeof(config));
+		ret = rte_eth_cache_stash_set(pt_id, RTE_ETH_CACHE_STASH_OP_DEV_DISABLE, &config);
+		if (ret != 0)
+			fprintf(stderr, "%s: (port %u) disable tph stash ret=%d\n",
+				__func__, pt_id, ret);
+	}
+}
+
 /*
  * Launch packet forwarding configuration.
  */
@@ -2614,6 +2690,9 @@ start_packet_forwarding(int with_tx_first)
 	if(!no_flush_rx)
 		flush_fwd_rx_queues();
 
+	if (tph_stash_objects > 0)
+		start_tph_stash();
+
 	rxtx_config_display();
 
 	fwd_stats_reset();
@@ -2649,6 +2728,8 @@ stop_packet_forwarding(void)
 		fwd_lcores[lc_id]->stopped = 1;
 	printf("\nWaiting for lcores to finish...\n");
 	rte_eal_mp_wait_lcore();
+	if (tph_stash_objects > 0)
+		stop_tph_stash();
 	port_fwd_end = cur_fwd_config.fwd_eng->port_fwd_end;
 	if (port_fwd_end != NULL) {
 		for (i = 0; i < cur_fwd_config.nb_fwd_ports; i++) {
diff --git a/app/test-pmd/testpmd.h b/app/test-pmd/testpmd.h
index 9b60ebd7fc..4124d40fda 100644
--- a/app/test-pmd/testpmd.h
+++ b/app/test-pmd/testpmd.h
@@ -535,6 +535,7 @@ extern uint8_t flow_isolate_all; /**< set by "--flow-isolate-all */
 extern uint8_t no_flow_flush; /**< set by "--disable-flow-flush" parameter */
 extern uint8_t  mp_alloc_type;
 /**< set by "--mp-anon" or "--mp-alloc" parameter */
+extern uint64_t tph_stash_objects; /**< set by "--tph-stash-objects" parameter */
 extern uint32_t eth_link_speed;
 extern uint8_t no_link_check; /**<set by "--disable-link-check" parameter */
 extern uint8_t no_device_start; /**<set by "--disable-device-start" parameter */
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 13+ messages in thread

* Re: [PATCH v1 1/4] bus/pci: introduce PCIe TPH support
  2026-05-08  9:28 ` [PATCH v1 1/4] bus/pci: introduce PCIe TPH support Chengwen Feng
@ 2026-05-08 15:00   ` Stephen Hemminger
  0 siblings, 0 replies; 13+ messages in thread
From: Stephen Hemminger @ 2026-05-08 15:00 UTC (permalink / raw)
  To: Chengwen Feng; +Cc: thomas, dev, wathsala.vithanage

On Fri, 8 May 2026 17:28:52 +0800
Chengwen Feng <fengchengwen@huawei.com> wrote:

> + * Used with VFIO_PCI_TPH_GET_CAP operation to return device
> + * TLP Processing Hints (TPH) capabilities to userspace.
> + */
> +struct vfio_pci_tph_cap {
> +	__u8  supported_modes;
> +#define VFIO_PCI_TPH_MODE_IV	(1u << 0) /* Interrupt vector */
> +#define VFIO_PCI_TPH_MODE_DS	(1u << 1) /* Device specific */

Use enum n

> +	__u8  reserved0;
> +	__u16 st_table_sz;
> +	__u32 reserved;
> +};

__u8, __u32 are kernel style types don't use in DPDK API's

I hate reserved fields. They don't do what you think and
create more problems. You end up having to enforce that reserved == 0
and 

^ permalink raw reply	[flat|nested] 13+ messages in thread

* [PATCH v2 0/4] Introduce generic cache stash API and PCIe TPH implementation
  2026-05-08  9:28 [PATCH v1 0/4] Introduce generic cache stash API and PCIe TPH implementation Chengwen Feng
                   ` (3 preceding siblings ...)
  2026-05-08  9:28 ` [PATCH v1 4/4] app/testpmd: add TPH stash objects configuration Chengwen Feng
@ 2026-05-12  9:22 ` Chengwen Feng
  2026-05-12  9:22   ` [PATCH v2 1/4] bus/pci: introduce PCIe TPH support Chengwen Feng
                     ` (3 more replies)
  2026-05-12 14:36 ` [PATCH v1 0/4] Introduce generic cache stash API and PCIe TPH implementation Stephen Hemminger
  2026-05-12 14:38 ` Stephen Hemminger
  6 siblings, 4 replies; 13+ messages in thread
From: Chengwen Feng @ 2026-05-12  9:22 UTC (permalink / raw)
  To: thomas, stephen; +Cc: dev, wathsala.vithanage, liuyonglong

This patch set introduces a generic ethdev cache stash framework,
provides PCIe TPH (Transaction Processing Hints) implementation
on PCI/VFIO bus, enables TPH cache stash support for e1000/igb PMD,
and adds testpmd command line configuration.

The patchset is organized as follows:
- Patch 1/4: bus/pci: introduce PCIe TPH support
  Add generic PCIe TPH APIs and VFIO backend implementation,
  with stub implementations for BSD and Windows.

- Patch 2/4: ethdev: introduce generic cache stash API
  Define unified cache stash capability/operation/config abstraction,
  support device-level and per-queue stash management.

- Patch 3/4: net/e1000: add cache stash support via TPH
  Implement ethdev cache stash ops for igb PMD based on PCIe TPH.

- Patch 4/4: app/testpmd: add TPH stash objects configuration
  Add testpmd CLI option to configure stash objects and
  automatically apply stash enable/disable on forwarding start/stop.

Note:
1. This implementation references the Linux kernel VFIO TPH uapi
   series (v9 version), which is not yet merged upstream:
   https://lore.kernel.org/all/20260512080329.42593-1-fengchengwen@huawei.com/

2. The API design and cache stash model learn from the prior  work
   by Wathsala Vithanage: "An API for Cache Stashing with TPH".

3. The e1000/igb PMD is enabled as the first user of this API
   because it is the only PCIe TPH capable hardware available
   for testing at this time. The generic API design is extensible
   to other NICs and future cache stash mechanisms.

--
v2: Sync kernel side v9
    Move vfio interface to kernel/linux/uapi/linux/vfio.h
    Fix CI compile error

Chengwen Feng (4):
  bus/pci: introduce PCIe TPH support
  ethdev: introduce generic cache stash API
  net/e1000: add cache stash support via TPH
  app/testpmd: add TPH stash objects configuration

 app/test-pmd/parameters.c               |  20 +++
 app/test-pmd/testpmd.c                  |  81 ++++++++++
 app/test-pmd/testpmd.h                  |   1 +
 drivers/bus/pci/bsd/pci.c               |  51 ++++++
 drivers/bus/pci/linux/pci.c             |  48 ++++++
 drivers/bus/pci/linux/pci_init.h        |   9 ++
 drivers/bus/pci/linux/pci_vfio.c        | 164 +++++++++++++++++++
 drivers/bus/pci/rte_bus_pci.h           | 114 +++++++++++++
 drivers/bus/pci/windows/pci.c           |  51 ++++++
 drivers/net/intel/e1000/base/e1000_hw.h |   2 +
 drivers/net/intel/e1000/base/e1000_vf.h |   2 +
 drivers/net/intel/e1000/igb_ethdev.c    | 205 ++++++++++++++++++++++++
 kernel/linux/uapi/linux/vfio.h          |  41 +++++
 lib/ethdev/ethdev_driver.h              |  37 +++++
 lib/ethdev/rte_ethdev.c                 |  40 +++++
 lib/ethdev/rte_ethdev.h                 |  95 +++++++++++
 lib/pci/rte_pci.h                       |   1 +
 17 files changed, 962 insertions(+)

-- 
2.17.1


^ permalink raw reply	[flat|nested] 13+ messages in thread

* [PATCH v2 1/4] bus/pci: introduce PCIe TPH support
  2026-05-12  9:22 ` [PATCH v2 0/4] Introduce generic cache stash API and PCIe TPH implementation Chengwen Feng
@ 2026-05-12  9:22   ` Chengwen Feng
  2026-05-12  9:23   ` [PATCH v2 2/4] ethdev: introduce generic cache stash API Chengwen Feng
                     ` (2 subsequent siblings)
  3 siblings, 0 replies; 13+ messages in thread
From: Chengwen Feng @ 2026-05-12  9:22 UTC (permalink / raw)
  To: thomas, stephen; +Cc: dev, wathsala.vithanage, liuyonglong

Add experimental API and Linux VFIO implementation for PCIe TLP
Processing Hints (TPH). Support device capability query, TPH
enable/disable, and steering tag get/set operations.

Stub functions are added for BSD and Windows.

The API includes:
- rte_pci_tph_query:   Query device TPH capabilities
- rte_pci_tph_enable:  Enable TPH with specified mode
- rte_pci_tph_disable: Disable TPH on device
- rte_pci_tph_st_get:  Get steering tags for CPUs
- rte_pci_tph_st_set:  Program steering tags into device's ST table

Signed-off-by: Chengwen Feng <fengchengwen@huawei.com>
---
 drivers/bus/pci/bsd/pci.c        |  51 ++++++++++
 drivers/bus/pci/linux/pci.c      |  48 +++++++++
 drivers/bus/pci/linux/pci_init.h |   9 ++
 drivers/bus/pci/linux/pci_vfio.c | 164 +++++++++++++++++++++++++++++++
 drivers/bus/pci/rte_bus_pci.h    | 114 +++++++++++++++++++++
 drivers/bus/pci/windows/pci.c    |  51 ++++++++++
 kernel/linux/uapi/linux/vfio.h   |  41 ++++++++
 lib/pci/rte_pci.h                |   1 +
 8 files changed, 479 insertions(+)

diff --git a/drivers/bus/pci/bsd/pci.c b/drivers/bus/pci/bsd/pci.c
index aba44492e0..15b65091b3 100644
--- a/drivers/bus/pci/bsd/pci.c
+++ b/drivers/bus/pci/bsd/pci.c
@@ -667,3 +667,54 @@ rte_pci_ioport_unmap(struct rte_pci_ioport *p)
 
 	return ret;
 }
+
+RTE_EXPORT_EXPERIMENTAL_SYMBOL(rte_pci_tph_query, 26.07)
+int
+rte_pci_tph_query(const struct rte_pci_device *dev, uint32_t *supported_modes,
+		  uint32_t *st_table_sz)
+{
+	RTE_SET_USED(dev);
+	RTE_SET_USED(supported_modes);
+	RTE_SET_USED(st_table_sz);
+	return -ENOTSUP;
+}
+
+RTE_EXPORT_EXPERIMENTAL_SYMBOL(rte_pci_tph_enable, 26.07)
+int
+rte_pci_tph_enable(const struct rte_pci_device *dev, uint32_t mode)
+{
+	RTE_SET_USED(dev);
+	RTE_SET_USED(mode);
+	return -ENOTSUP;
+}
+
+RTE_EXPORT_EXPERIMENTAL_SYMBOL(rte_pci_tph_disable, 26.07)
+int
+rte_pci_tph_disable(const struct rte_pci_device *dev)
+{
+	RTE_SET_USED(dev);
+	return -ENOTSUP;
+}
+
+RTE_EXPORT_EXPERIMENTAL_SYMBOL(rte_pci_tph_st_get, 26.07)
+int
+rte_pci_tph_st_get(const struct rte_pci_device *dev,
+		   struct rte_pci_tph_entry *ents, uint32_t count)
+{
+	RTE_SET_USED(dev);
+	RTE_SET_USED(ents);
+	RTE_SET_USED(count);
+	return -ENOTSUP;
+}
+
+RTE_EXPORT_EXPERIMENTAL_SYMBOL(rte_pci_tph_st_set, 26.07)
+int
+rte_pci_tph_st_set(const struct rte_pci_device *dev, uint16_t index,
+		   struct rte_pci_tph_entry *ents, uint32_t count)
+{
+	RTE_SET_USED(dev);
+	RTE_SET_USED(index);
+	RTE_SET_USED(ents);
+	RTE_SET_USED(count);
+	return -ENOTSUP;
+}
diff --git a/drivers/bus/pci/linux/pci.c b/drivers/bus/pci/linux/pci.c
index 5f263f8b28..824be47b36 100644
--- a/drivers/bus/pci/linux/pci.c
+++ b/drivers/bus/pci/linux/pci.c
@@ -791,3 +791,51 @@ rte_pci_ioport_unmap(struct rte_pci_ioport *p)
 
 	return ret;
 }
+
+RTE_EXPORT_EXPERIMENTAL_SYMBOL(rte_pci_tph_query, 26.07)
+int
+rte_pci_tph_query(const struct rte_pci_device *dev, uint32_t *supported_modes,
+		  uint32_t *st_table_sz)
+{
+	if (dev->kdrv == RTE_PCI_KDRV_VFIO && pci_vfio_is_enabled())
+		return pci_vfio_tph_query(dev, supported_modes, st_table_sz);
+	return -ENOTSUP;
+}
+
+RTE_EXPORT_EXPERIMENTAL_SYMBOL(rte_pci_tph_enable, 26.07)
+int
+rte_pci_tph_enable(const struct rte_pci_device *dev, uint32_t mode)
+{
+	if (dev->kdrv == RTE_PCI_KDRV_VFIO && pci_vfio_is_enabled())
+		return pci_vfio_tph_enable(dev, mode);
+	return -ENOTSUP;
+}
+
+RTE_EXPORT_EXPERIMENTAL_SYMBOL(rte_pci_tph_disable, 26.07)
+int
+rte_pci_tph_disable(const struct rte_pci_device *dev)
+{
+	if (dev->kdrv == RTE_PCI_KDRV_VFIO && pci_vfio_is_enabled())
+		return pci_vfio_tph_disable(dev);
+	return -ENOTSUP;
+}
+
+RTE_EXPORT_EXPERIMENTAL_SYMBOL(rte_pci_tph_st_get, 26.07)
+int
+rte_pci_tph_st_get(const struct rte_pci_device *dev,
+		   struct rte_pci_tph_entry *ents, uint32_t count)
+{
+	if (dev->kdrv == RTE_PCI_KDRV_VFIO && pci_vfio_is_enabled())
+		return pci_vfio_tph_st_get(dev, ents, count);
+	return -ENOTSUP;
+}
+
+RTE_EXPORT_EXPERIMENTAL_SYMBOL(rte_pci_tph_st_set, 26.07)
+int
+rte_pci_tph_st_set(const struct rte_pci_device *dev, uint16_t index,
+		   struct rte_pci_tph_entry *ents, uint32_t count)
+{
+	if (dev->kdrv == RTE_PCI_KDRV_VFIO && pci_vfio_is_enabled())
+		return pci_vfio_tph_st_set(dev, index, ents, count);
+	return -ENOTSUP;
+}
diff --git a/drivers/bus/pci/linux/pci_init.h b/drivers/bus/pci/linux/pci_init.h
index 6949dd57d9..c475a4cfbf 100644
--- a/drivers/bus/pci/linux/pci_init.h
+++ b/drivers/bus/pci/linux/pci_init.h
@@ -73,4 +73,13 @@ int pci_vfio_unmap_resource(struct rte_pci_device *dev);
 
 int pci_vfio_is_enabled(void);
 
+int pci_vfio_tph_query(const struct rte_pci_device *dev, uint32_t *supported_modes,
+		   uint32_t *st_table_sz);
+int pci_vfio_tph_enable(const struct rte_pci_device *dev, uint32_t mode);
+int pci_vfio_tph_disable(const struct rte_pci_device *dev);
+int pci_vfio_tph_st_get(const struct rte_pci_device *dev,
+		    struct rte_pci_tph_entry *ents, uint32_t count);
+int pci_vfio_tph_st_set(const struct rte_pci_device *dev, uint16_t index,
+		    struct rte_pci_tph_entry *ents, uint32_t count);
+
 #endif /* EAL_PCI_INIT_H_ */
diff --git a/drivers/bus/pci/linux/pci_vfio.c b/drivers/bus/pci/linux/pci_vfio.c
index bc5c5c2499..dac745cd74 100644
--- a/drivers/bus/pci/linux/pci_vfio.c
+++ b/drivers/bus/pci/linux/pci_vfio.c
@@ -1308,3 +1308,167 @@ pci_vfio_is_enabled(void)
 	}
 	return status;
 }
+
+#define  PCI_TPH_CAP_OFF	0x4
+#define  PCI_TPH_CAP_ST_NS	0x00000001 /* No ST Mode Supported */
+#define  PCI_TPH_CAP_ST_IV	0x00000002 /* Interrupt Vector Mode Supported */
+#define  PCI_TPH_CAP_ST_DS	0x00000004 /* Device Specific Mode Supported */
+#define  PCI_TPH_CAP_LOC_MASK	0x00000600 /* ST Table Location */
+#define  PCI_TPH_LOC_NONE	0x00000000 /* Not present */
+#define  PCI_TPH_LOC_CAP	0x00000200 /* In capability */
+#define  PCI_TPH_LOC_MSIX	0x00000400 /* In MSI-X */
+#define  PCI_TPH_CTRL_OFF	0x8
+#define  PCI_TPH_ST_NS_MODE	0x0 /* No ST Mode */
+#define  PCI_TPH_ST_IV_MODE	0x1 /* Interrupt Vector Mode */
+#define  PCI_TPH_ST_DS_MODE	0x2 /* Device Specific Mode */
+
+int
+pci_vfio_tph_query(const struct rte_pci_device *dev, uint32_t *supported_modes,
+		   uint32_t *st_table_sz)
+{
+	off_t off = rte_pci_find_ext_capability(dev, RTE_PCI_EXT_CAP_ID_TPH);
+	uint32_t cap, loc;
+	int ret;
+
+	if (off <= 0)
+		return -ENOTSUP;
+
+	ret = rte_pci_read_config(dev, &cap, 4, off + PCI_TPH_CAP_OFF);
+	if (ret != 4)
+		return -EIO;
+
+	*supported_modes = 0;
+	if (cap & PCI_TPH_CAP_ST_IV)
+		*supported_modes |= RTE_PCI_TPH_MODE_IV;
+	if (cap & PCI_TPH_CAP_ST_DS)
+		*supported_modes |= RTE_PCI_TPH_MODE_DS;
+	loc = cap & PCI_TPH_CAP_LOC_MASK;
+	if (loc == PCI_TPH_LOC_CAP || loc == PCI_TPH_LOC_MSIX)
+		*st_table_sz = RTE_FIELD_GET32(PCI_TPH_LOC_MSIX, cap) + 1;
+	else
+		*st_table_sz = 0;
+
+	return 0;
+}
+
+static int
+pci_vfio_tph_ctrl(const struct rte_pci_device *dev, uint32_t mode)
+{
+	off_t off = rte_pci_find_ext_capability(dev, RTE_PCI_EXT_CAP_ID_TPH);
+	int ret;
+
+	if (off <= 0)
+		return -ENOTSUP;
+
+	ret = rte_pci_write_config(dev, &mode, 4, off + PCI_TPH_CTRL_OFF);
+	if (ret != 4)
+		return -EIO;
+
+	return 0;
+}
+
+int
+pci_vfio_tph_enable(const struct rte_pci_device *dev, uint32_t mode)
+{
+	uint32_t st_mode;
+
+	if (mode == RTE_PCI_TPH_MODE_IV)
+		st_mode = PCI_TPH_ST_IV_MODE;
+	else if (mode == RTE_PCI_TPH_MODE_DS)
+		st_mode = PCI_TPH_ST_DS_MODE;
+	else
+		return -EINVAL;
+
+	return pci_vfio_tph_ctrl(dev, st_mode);
+}
+
+int
+pci_vfio_tph_disable(const struct rte_pci_device *dev)
+{
+	return pci_vfio_tph_ctrl(dev, PCI_TPH_ST_NS_MODE);
+}
+
+int
+pci_vfio_tph_st_get(const struct rte_pci_device *dev,
+		    struct rte_pci_tph_entry *ents, uint32_t count)
+{
+	struct vfio_device_feature_tph_st *tph_st;
+	struct vfio_device_feature *feature;
+	int vfio_dev_fd, ret;
+	size_t argsz;
+	uint32_t i;
+
+	if (count > VFIO_TPH_ST_MAX_COUNT)
+		return -EINVAL;
+
+	vfio_dev_fd = rte_intr_dev_fd_get(dev->intr_handle);
+	if (vfio_dev_fd < 0)
+		return -1;
+
+	argsz = sizeof(struct vfio_device_feature) +
+		sizeof(struct vfio_device_feature_tph_st) +
+		count * sizeof(uint32_t);
+	feature = (struct vfio_device_feature *)calloc(1, argsz);
+	if (feature == NULL)
+		return -ENOMEM;
+	tph_st = (struct vfio_device_feature_tph_st *)feature->data;
+
+	feature->argsz = argsz;
+	feature->flags = VFIO_DEVICE_FEATURE_TPH_ST;
+	feature->flags |= VFIO_DEVICE_FEATURE_GET;
+	for (i = 0; i < count; i++)
+		tph_st->data[i] = ents[i].cpu;
+	tph_st->flags = VFIO_TPH_ST_MEM_TYPE_VM;
+	tph_st->index = 0;
+	tph_st->count = count;
+	ret = ioctl(vfio_dev_fd, VFIO_DEVICE_FEATURE, feature);
+	if (ret) {
+		free(feature);
+		return ret;
+	}
+
+	for (i = 0; i < count; i++)
+		ents[i].st = tph_st->data[i];
+	free(feature);
+
+	return 0;
+}
+
+int
+pci_vfio_tph_st_set(const struct rte_pci_device *dev, uint16_t index,
+		    struct rte_pci_tph_entry *ents, uint32_t count)
+{
+	struct vfio_device_feature_tph_st *tph_st;
+	struct vfio_device_feature *feature;
+	int vfio_dev_fd, ret;
+	size_t argsz;
+	uint32_t i;
+
+	if (count > VFIO_TPH_ST_MAX_COUNT)
+		return -EINVAL;
+
+	vfio_dev_fd = rte_intr_dev_fd_get(dev->intr_handle);
+	if (vfio_dev_fd < 0)
+		return -1;
+
+	argsz = sizeof(struct vfio_device_feature) +
+		sizeof(struct vfio_device_feature_tph_st) +
+		count * sizeof(uint32_t);
+	feature = (struct vfio_device_feature *)calloc(1, argsz);
+	if (feature == NULL)
+		return -ENOMEM;
+	tph_st = (struct vfio_device_feature_tph_st *)feature->data;
+
+	feature->argsz = argsz;
+	feature->flags = VFIO_DEVICE_FEATURE_TPH_ST;
+	feature->flags |= VFIO_DEVICE_FEATURE_SET;
+	tph_st->flags = VFIO_TPH_ST_MEM_TYPE_VM;
+	tph_st->index = index;
+	tph_st->count = count;
+	for (i = 0; i < count; i++)
+		tph_st->data[i] = ents[i].cpu;
+	ret = ioctl(vfio_dev_fd, VFIO_DEVICE_FEATURE, feature);
+	free(feature);
+
+	return ret;
+}
diff --git a/drivers/bus/pci/rte_bus_pci.h b/drivers/bus/pci/rte_bus_pci.h
index 19a7b15b99..623c80e855 100644
--- a/drivers/bus/pci/rte_bus_pci.h
+++ b/drivers/bus/pci/rte_bus_pci.h
@@ -312,6 +312,120 @@ void rte_pci_ioport_read(struct rte_pci_ioport *p,
 void rte_pci_ioport_write(struct rte_pci_ioport *p,
 		const void *data, size_t len, off_t offset);
 
+#define RTE_PCI_TPH_MODE_IV	(1u << 0) /* Interrupt vector */
+#define RTE_PCI_TPH_MODE_DS	(1u << 1) /* Device specific */
+
+/**
+ * @struct rte_pci_tph_entry
+ * @warning
+ * @b EXPERIMENTAL: this structure may change without prior notice.
+ *
+ * An entry used for TPH Steering Tag (ST) get/set operations.
+ */
+struct rte_pci_tph_entry {
+	/**
+	 * CPU ID used for both get and set operations.
+	 * For set operation: if set to U32_MAX, clear the ST entry at
+	 * specified index.
+	 */
+	uint32_t cpu;
+	/**
+	 * Steering tag value, only used for get operation to return result.
+	 */
+	uint16_t st;
+};
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Query PCIe TLP Processing Hints (TPH) capabilities of a device.
+ *
+ * @param dev
+ *   A pointer to a rte_pci_device structure describing the device to query.
+ * @param supported_modes
+ *   Output: supported TPH modes (RTE_PCI_TPH_MODE_*).
+ * @param st_table_sz
+ *   Output: number of entries in the ST table; 0 means no table present.
+ * @return
+ *   0 on success, negative value on error.
+ */
+__rte_experimental
+int rte_pci_tph_query(const struct rte_pci_device *dev, uint32_t *supported_modes,
+		  uint32_t *st_table_sz);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Enable PCIe TLP Processing Hints (TPH) on a device with specified mode.
+ *
+ * @param dev
+ *   A pointer to a rte_pci_device structure describing the device to enable.
+ * @param mode
+ *   TPH operating mode (RTE_PCI_TPH_MODE_*).
+ * @return
+ *   0 on success, negative value on error.
+ */
+__rte_experimental
+int rte_pci_tph_enable(const struct rte_pci_device *dev, uint32_t mode);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Disable PCIe TLP Processing Hints (TPH) on a device.
+ *
+ * @param dev
+ *   A pointer to a rte_pci_device structure describing the device to disable.
+ * @return
+ *   0 on success, negative value on error.
+ */
+__rte_experimental
+int rte_pci_tph_disable(const struct rte_pci_device *dev);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Get steering tags for given CPU IDs from the device.
+ * Only valid when TPH is enabled in Device-Specific (DS) mode.
+ *
+ * @param dev
+ *   A pointer to a rte_pci_device structure describing the device.
+ * @param ents
+ *   Array of entries with CPU IDs as input; steering tags are returned
+ *   as output.
+ * @param count
+ *   Number of entries in the array.
+ * @return
+ *   0 on success, negative value on error.
+ */
+__rte_experimental
+int rte_pci_tph_st_get(const struct rte_pci_device *dev,
+		   struct rte_pci_tph_entry *ents, uint32_t count);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Program steering tags into the device's ST table.
+ *
+ * @param dev
+ *   A pointer to a rte_pci_device structure describing the device.
+ * @index
+ *   Start ST table index to program.
+ * @param ents
+ *   Array of entries with CPU IDs and index indices to program.
+ * @param count
+ *   Number of entries in the array.
+ * @return
+ *   0 on success, negative errno value on error.
+ */
+__rte_experimental
+int rte_pci_tph_st_set(const struct rte_pci_device *dev, uint16_t index,
+		   struct rte_pci_tph_entry *ents, uint32_t count);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/drivers/bus/pci/windows/pci.c b/drivers/bus/pci/windows/pci.c
index 549319ad5b..53d7bebde9 100644
--- a/drivers/bus/pci/windows/pci.c
+++ b/drivers/bus/pci/windows/pci.c
@@ -207,6 +207,57 @@ pci_uio_remap_resource(struct rte_pci_device *dev __rte_unused)
 	return -1;
 }
 
+RTE_EXPORT_EXPERIMENTAL_SYMBOL(rte_pci_tph_query, 26.07)
+int
+rte_pci_tph_query(const struct rte_pci_device *dev, uint32_t *supported_modes,
+		  uint32_t *st_table_sz)
+{
+	RTE_SET_USED(dev);
+	RTE_SET_USED(supported_modes);
+	RTE_SET_USED(st_table_sz);
+	return -ENOTSUP;
+}
+
+RTE_EXPORT_EXPERIMENTAL_SYMBOL(rte_pci_tph_enable, 26.07)
+int
+rte_pci_tph_enable(const struct rte_pci_device *dev, uint32_t mode)
+{
+	RTE_SET_USED(dev);
+	RTE_SET_USED(mode);
+	return -ENOTSUP;
+}
+
+RTE_EXPORT_EXPERIMENTAL_SYMBOL(rte_pci_tph_disable, 26.07)
+int
+rte_pci_tph_disable(const struct rte_pci_device *dev)
+{
+	RTE_SET_USED(dev);
+	return -ENOTSUP;
+}
+
+RTE_EXPORT_EXPERIMENTAL_SYMBOL(rte_pci_tph_st_get, 26.07)
+int
+rte_pci_tph_st_get(const struct rte_pci_device *dev,
+		   struct rte_pci_tph_entry *ents, uint32_t count)
+{
+	RTE_SET_USED(dev);
+	RTE_SET_USED(ents);
+	RTE_SET_USED(count);
+	return -ENOTSUP;
+}
+
+RTE_EXPORT_EXPERIMENTAL_SYMBOL(rte_pci_tph_st_set, 26.07)
+int
+rte_pci_tph_st_set(const struct rte_pci_device *dev, uint16_t index,
+		   struct rte_pci_tph_entry *ents, uint32_t count)
+{
+	RTE_SET_USED(dev);
+	RTE_SET_USED(index);
+	RTE_SET_USED(ents);
+	RTE_SET_USED(count);
+	return -ENOTSUP;
+}
+
 static int
 get_device_pci_address(HDEVINFO dev_info,
 	PSP_DEVINFO_DATA device_info_data, struct rte_pci_addr *addr)
diff --git a/kernel/linux/uapi/linux/vfio.h b/kernel/linux/uapi/linux/vfio.h
index 79bf8c0cc5..f0676310bf 100644
--- a/kernel/linux/uapi/linux/vfio.h
+++ b/kernel/linux/uapi/linux/vfio.h
@@ -1468,6 +1468,47 @@ struct vfio_device_feature_bus_master {
 };
 #define VFIO_DEVICE_FEATURE_BUS_MASTER 10
 
+/**
+ * VFIO_DEVICE_FEATURE_TPH_ST - Get/Set PCIe TPH Steering Tag (ST) entries
+ *
+ * Provides userspace interface to manage PCIe TPH ST table entries.
+ * This feature is only available when device TPH is enabled.
+ *
+ * Upon VFIO_DEVICE_FEATURE_SET:
+ *   Program contiguous ST table entries from the starting @index.
+ *   Valid only for hardware with ST table located in TPH Capability
+ *   space or MSI-X table. If an entry CPU ID is specified as U32_MAX,
+ *   the corresponding ST entry will be cleared. @index and @count define
+ *   the contiguous entry range to be programmed.
+ *   If any entry programming fails, the operation will roll back and
+ *   clear all entries that were successfully programmed before the error.
+ *
+ * Upon VFIO_DEVICE_FEATURE_GET:
+ *   Retrieve the ST value mapped to each given CPU ID in the @data array.
+ *   Userspace fills @data with CPU ID array, the interface returns each
+ *   CPU's corresponding ST value back in place.
+ *   Valid only when TPH DS mode is enabled.
+ *
+ * @flags: Operation flags (VFIO_TPH_ST_MEM_TYPE_*).
+ * @index: Starting ST entry index, only valid for FEATURE_SET.
+ * @count: Number of contiguous entries to access.
+ * @data: Array of CPU IDs for both SET and GET. On SET it programs ST for
+ *        each CPU; on GET it returns the mapped ST value of each CPU.
+ *
+ * This feature is gated by enable_unsafe_tph module parameter.
+ */
+#define VFIO_DEVICE_FEATURE_TPH_ST     13
+
+struct vfio_device_feature_tph_st {
+	__u32 flags;
+#define VFIO_TPH_ST_MEM_TYPE_VM                (0U << 0)
+#define VFIO_TPH_ST_MEM_TYPE_PM                (1U << 0)
+	__u16 index;
+	__u16 count;
+#define VFIO_TPH_ST_MAX_COUNT          2048
+	__u32 data[];
+};
+
 /* -------- API for Type1 VFIO IOMMU -------- */
 
 /**
diff --git a/lib/pci/rte_pci.h b/lib/pci/rte_pci.h
index 9d04978a0f..f9129723eb 100644
--- a/lib/pci/rte_pci.h
+++ b/lib/pci/rte_pci.h
@@ -111,6 +111,7 @@ extern "C" {
 #define RTE_PCI_EXT_CAP_ID_ACS		0x0d	/* Access Control Services */
 #define RTE_PCI_EXT_CAP_ID_SRIOV	0x10	/* SR-IOV */
 #define RTE_PCI_EXT_CAP_ID_PRI		0x13	/* Page Request Interface */
+#define RTE_PCI_EXT_CAP_ID_TPH		0x17	/* TLP Processing Hints */
 #define RTE_PCI_EXT_CAP_ID_PASID	0x1b    /* Process Address Space ID */
 
 /* Advanced Error Reporting (RTE_PCI_EXT_CAP_ID_ERR) */
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [PATCH v2 2/4] ethdev: introduce generic cache stash API
  2026-05-12  9:22 ` [PATCH v2 0/4] Introduce generic cache stash API and PCIe TPH implementation Chengwen Feng
  2026-05-12  9:22   ` [PATCH v2 1/4] bus/pci: introduce PCIe TPH support Chengwen Feng
@ 2026-05-12  9:23   ` Chengwen Feng
  2026-05-12  9:23   ` [PATCH v2 3/4] net/e1000: add cache stash support via TPH Chengwen Feng
  2026-05-12  9:23   ` [PATCH v2 4/4] app/testpmd: add TPH stash objects configuration Chengwen Feng
  3 siblings, 0 replies; 13+ messages in thread
From: Chengwen Feng @ 2026-05-12  9:23 UTC (permalink / raw)
  To: thomas, stephen; +Cc: dev, wathsala.vithanage, liuyonglong

Introduce a generic Ethernet device API for cache stashing,
supporting hardware mechanisms such as PCIe TPH (Transaction
Processing Hints).

The API provides:
- Generic query for supported stashing types and objects
- Device-wide stashing enable/disable
- Per-queue stashing with target lcore and data objects
  (Rx/Tx descriptor, header, payload)

The design allows a device to support multiple stashing types
while only allowing one active type at runtime.

Signed-off-by: Chengwen Feng <fengchengwen@huawei.com>
---
 lib/ethdev/ethdev_driver.h | 37 +++++++++++++++
 lib/ethdev/rte_ethdev.c    | 40 ++++++++++++++++
 lib/ethdev/rte_ethdev.h    | 95 ++++++++++++++++++++++++++++++++++++++
 3 files changed, 172 insertions(+)

diff --git a/lib/ethdev/ethdev_driver.h b/lib/ethdev/ethdev_driver.h
index 1255cd6f2c..e7b4167939 100644
--- a/lib/ethdev/ethdev_driver.h
+++ b/lib/ethdev/ethdev_driver.h
@@ -1409,6 +1409,38 @@ enum rte_eth_dev_operation {
 typedef uint64_t (*eth_get_restore_flags_t)(struct rte_eth_dev *dev,
 					    enum rte_eth_dev_operation op);
 
+/**
+ * @internal
+ * Get cache stash capabilities of the Ethernet device.
+ *
+ * @param dev
+ *   Port (ethdev) handle.
+ * @param capa
+ *   Pointer to capability structure to store supported stash types and objects.
+ *
+ * @return
+ *   Negative on error, 0 on success.
+ */
+typedef int (*eth_cache_stash_get_t)(struct rte_eth_dev *dev,
+				     struct rte_eth_cache_stash_capability *capa);
+
+/**
+ * @internal
+ * Configure cache stash for the Ethernet device or queue.
+ *
+ * @param dev
+ *   Port (ethdev) handle.
+ * @param op
+ *   Cache stash operation type (enable/disable device or queue).
+ * @param config
+ *   Cache stash configuration (device or queue level).
+ *
+ * @return
+ *   Negative on error, 0 on success.
+ */
+typedef int (*eth_cache_stash_set_t)(struct rte_eth_dev *dev, enum rte_eth_cache_stash_op op,
+				     struct rte_eth_cache_stash_config *config);
+
 /**
  * @internal A structure containing the functions exported by an Ethernet driver.
  */
@@ -1661,6 +1693,11 @@ struct eth_dev_ops {
 
 	/** Get configuration which ethdev should restore */
 	eth_get_restore_flags_t get_restore_flags;
+
+	/** Cache stash get ops */
+	eth_cache_stash_get_t cache_stash_get;
+	/** Cache stash set ops */
+	eth_cache_stash_set_t cache_stash_set;
 };
 
 /**
diff --git a/lib/ethdev/rte_ethdev.c b/lib/ethdev/rte_ethdev.c
index 2edc7a362e..4398aca5e1 100644
--- a/lib/ethdev/rte_ethdev.c
+++ b/lib/ethdev/rte_ethdev.c
@@ -7460,5 +7460,45 @@ int rte_eth_dev_map_aggr_tx_affinity(uint16_t port_id, uint16_t tx_queue_id,
 	return ret;
 }
 
+RTE_EXPORT_EXPERIMENTAL_SYMBOL(rte_eth_cache_stash_get, 26.03)
+int
+rte_eth_cache_stash_get(uint16_t port_id,
+			struct rte_eth_cache_stash_capability *capa)
+{
+	struct rte_eth_dev *dev;
+
+	RTE_ETH_VALID_PORTID_OR_ERR_RET(port_id, -ENODEV);
+
+	if (capa == NULL)
+		return -EINVAL;
+
+	dev = &rte_eth_devices[port_id];
+
+	if (*dev->dev_ops->cache_stash_get == NULL)
+		return -ENOTSUP;
+
+	return eth_err(port_id, (*dev->dev_ops->cache_stash_get)(dev, capa));
+}
+
+RTE_EXPORT_EXPERIMENTAL_SYMBOL(rte_eth_cache_stash_set, 26.03)
+int
+rte_eth_cache_stash_set(uint16_t port_id, enum rte_eth_cache_stash_op op,
+		       struct rte_eth_cache_stash_config *config)
+{
+	struct rte_eth_dev *dev;
+
+	RTE_ETH_VALID_PORTID_OR_ERR_RET(port_id, -ENODEV);
+
+	if (op != RTE_ETH_CACHE_STASH_OP_DEV_DISABLE && config == NULL)
+		return -EINVAL;
+
+	dev = &rte_eth_devices[port_id];
+
+	if (*dev->dev_ops->cache_stash_set == NULL)
+		return -ENOTSUP;
+
+	return eth_err(port_id, (*dev->dev_ops->cache_stash_set)(dev, op, config));
+}
+
 RTE_EXPORT_SYMBOL(rte_eth_dev_logtype)
 RTE_LOG_REGISTER_DEFAULT(rte_eth_dev_logtype, INFO);
diff --git a/lib/ethdev/rte_ethdev.h b/lib/ethdev/rte_ethdev.h
index 0d8e2d0236..014f0c21b6 100644
--- a/lib/ethdev/rte_ethdev.h
+++ b/lib/ethdev/rte_ethdev.h
@@ -7178,6 +7178,101 @@ rte_eth_tx_queue_count(uint16_t port_id, uint16_t queue_id)
 	return rc;
 }
 
+/**
+ * Cache stash mechanisms (bitmask).
+ * A device may support multiple types but can only enable one at a time.
+ */
+enum rte_eth_cache_stash_type {
+	RTE_ETH_CACHE_STASH_TYPE_TPH = RTE_BIT32(0),
+};
+
+/**
+ * Cache stash objects (bitmask).
+ */
+#define RTE_ETH_CACHE_STASH_OBJ_RX_DESC		RTE_BIT64(0)
+#define RTE_ETH_CACHE_STASH_OBJ_TX_DESC         RTE_BIT64(1)
+#define RTE_ETH_CACHE_STASH_OBJ_RX_HEADER	RTE_BIT64(2)
+#define RTE_ETH_CACHE_STASH_OBJ_TX_HEADER       RTE_BIT64(3)
+#define RTE_ETH_CACHE_STASH_OBJ_RX_PAYLOAD	RTE_BIT64(4)
+#define RTE_ETH_CACHE_STASH_OBJ_TX_PAYLOAD	RTE_BIT64(5)
+
+/**
+ * Cache stash capability.
+ */
+struct rte_eth_cache_stash_capability {
+	uint32_t supported_types;  /**< Bitmask of rte_eth_cache_stash_type */
+	uint64_t supported_objects;/**< Bitmask of RTE_ETH_CACHE_STASH_OBJ_* */
+};
+
+/**
+ * Cache stash operations.
+ */
+enum rte_eth_cache_stash_op {
+	RTE_ETH_CACHE_STASH_OP_DEV_ENABLE,	/**< Enable device-wide cache stash */
+	RTE_ETH_CACHE_STASH_OP_DEV_DISABLE,	/**< Disable device-wide cache stash */
+	RTE_ETH_CACHE_STASH_OP_QUEUE_ENABLE,	/**< Enable cache stash for a queue */
+	RTE_ETH_CACHE_STASH_OP_QUEUE_DISABLE,	/**< Disable cache stash for a queue */
+	RTE_ETH_CACHE_STASH_OP_BUTT
+};
+
+/**
+ * Cache stash configuration.
+ * The used union member is determined by the operation.
+ */
+struct rte_eth_cache_stash_config {
+	union {
+		/**
+		 * Device-level configuration (used with DEV_ENABLE).
+		 */
+		struct {
+			uint32_t type;	/**< Selected stash mechanism type */
+		} dev;
+		/**
+		 * Queue-level configuration (used with QUEUE_ENABLE/QUEUE_DISABLE).
+		 */
+		struct {
+			uint32_t lcore_id;	/**< Target CPU core ID */
+			uint32_t queue_id;	/**< Queue ID */
+			uint64_t objects;	/**< Objects bitmask to stash */
+		} queue;
+	};
+};
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Get cache stash capabilities of a port.
+ *
+ * @param port_id
+ *   The port identifier.
+ * @param capa
+ *   Output: supported stash types and objects.
+ * @return
+ *   0 on success, negative on error.
+ */
+__rte_experimental
+int rte_eth_cache_stash_get(uint16_t port_id, struct rte_eth_cache_stash_capability *capa);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Configure cache stash.
+ *
+ * @param port_id
+ *   The port identifier.
+ * @param op
+ *   Configuration operation.
+ * @param config
+ *   Configuration structure.
+ * @return
+ *   0 on success, negative on error.
+ */
+__rte_experimental
+int rte_eth_cache_stash_set(uint16_t port_id, enum rte_eth_cache_stash_op op,
+			    struct rte_eth_cache_stash_config *config);
+
 #ifdef __cplusplus
 }
 #endif
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [PATCH v2 3/4] net/e1000: add cache stash support via TPH
  2026-05-12  9:22 ` [PATCH v2 0/4] Introduce generic cache stash API and PCIe TPH implementation Chengwen Feng
  2026-05-12  9:22   ` [PATCH v2 1/4] bus/pci: introduce PCIe TPH support Chengwen Feng
  2026-05-12  9:23   ` [PATCH v2 2/4] ethdev: introduce generic cache stash API Chengwen Feng
@ 2026-05-12  9:23   ` Chengwen Feng
  2026-05-12  9:23   ` [PATCH v2 4/4] app/testpmd: add TPH stash objects configuration Chengwen Feng
  3 siblings, 0 replies; 13+ messages in thread
From: Chengwen Feng @ 2026-05-12  9:23 UTC (permalink / raw)
  To: thomas, stephen; +Cc: dev, wathsala.vithanage, liuyonglong

Implement ethdev generic cache stash operations for igb PMD
using PCIe TPH (Transaction Processing Hints) as the underlying
mechanism.

The implementation supports:
- Query TPH based stash capabilities
- Device-level global enable/disable of TPH
- Per-queue TPH stashing configuration with target lcore
- Configure stashing objects (Rx descriptor, payload, etc.)

Signed-off-by: Chengwen Feng <fengchengwen@huawei.com>
---
 drivers/net/intel/e1000/base/e1000_hw.h |   2 +
 drivers/net/intel/e1000/base/e1000_vf.h |   2 +
 drivers/net/intel/e1000/igb_ethdev.c    | 205 ++++++++++++++++++++++++
 3 files changed, 209 insertions(+)

diff --git a/drivers/net/intel/e1000/base/e1000_hw.h b/drivers/net/intel/e1000/base/e1000_hw.h
index 9b1fafd75c..3e8e21b194 100644
--- a/drivers/net/intel/e1000/base/e1000_hw.h
+++ b/drivers/net/intel/e1000/base/e1000_hw.h
@@ -1092,6 +1092,8 @@ struct e1000_hw {
 	u16 vendor_id;
 
 	u8  revision_id;
+
+	u8 tph_mode;
 };
 
 #include "e1000_82541.h"
diff --git a/drivers/net/intel/e1000/base/e1000_vf.h b/drivers/net/intel/e1000/base/e1000_vf.h
index 4bec21c935..dd3cef254e 100644
--- a/drivers/net/intel/e1000/base/e1000_vf.h
+++ b/drivers/net/intel/e1000/base/e1000_vf.h
@@ -248,6 +248,8 @@ struct e1000_hw {
 	u16 vendor_id;
 
 	u8  revision_id;
+
+	u8 tph_mode;
 };
 
 enum e1000_promisc_type {
diff --git a/drivers/net/intel/e1000/igb_ethdev.c b/drivers/net/intel/e1000/igb_ethdev.c
index ef1599ac38..0f06139786 100644
--- a/drivers/net/intel/e1000/igb_ethdev.c
+++ b/drivers/net/intel/e1000/igb_ethdev.c
@@ -241,6 +241,10 @@ static int igb_tx_burst_mode_get(struct rte_eth_dev *dev,
 	__rte_unused uint16_t queue_id, struct rte_eth_burst_mode *mode);
 static int igb_rx_burst_mode_get(struct rte_eth_dev *dev,
 	__rte_unused uint16_t queue_id, struct rte_eth_burst_mode *mode);
+static int eth_igb_cache_stash_get(struct rte_eth_dev *dev,
+				   struct rte_eth_cache_stash_capability *capa);
+static int eth_igb_cache_stash_set(struct rte_eth_dev *dev, enum rte_eth_cache_stash_op op,
+				   struct rte_eth_cache_stash_config *config);
 
 /*
  * Define VF Stats MACRO for Non "cleared on read" register
@@ -402,6 +406,8 @@ static const struct eth_dev_ops eth_igb_ops = {
 	.timesync_read_time   = igb_timesync_read_time,
 	.timesync_write_time  = igb_timesync_write_time,
 	.read_clock		      = eth_igb_read_clock,
+	.cache_stash_get      = eth_igb_cache_stash_get,
+	.cache_stash_set      = eth_igb_cache_stash_set,
 };
 
 /*
@@ -5692,6 +5698,205 @@ igb_filter_restore(struct rte_eth_dev *dev)
 	return 0;
 }
 
+static int
+eth_igb_cache_stash_get(struct rte_eth_dev *dev, struct rte_eth_cache_stash_capability *capa)
+{
+	struct rte_pci_device *pci_dev = RTE_ETH_DEV_TO_PCI(dev);
+	uint32_t support_modes, st_table_sz;
+	int ret;
+
+	ret = rte_pci_tph_query(pci_dev, &support_modes, &st_table_sz);
+	if (ret != 0)
+		return -ENOTSUP;
+	capa->supported_types = RTE_ETH_CACHE_STASH_TYPE_TPH;
+	capa->supported_objects = RTE_ETH_CACHE_STASH_OBJ_RX_DESC |
+				  RTE_ETH_CACHE_STASH_OBJ_RX_HEADER |
+				  RTE_ETH_CACHE_STASH_OBJ_RX_PAYLOAD |
+				  RTE_ETH_CACHE_STASH_OBJ_TX_DESC |
+				  RTE_ETH_CACHE_STASH_OBJ_TX_HEADER |
+				  RTE_ETH_CACHE_STASH_OBJ_TX_PAYLOAD;
+	return 0;
+}
+
+static int
+eth_igb_cache_stash_dev_enable(struct rte_eth_dev *dev,
+			       struct rte_eth_cache_stash_config *config)
+{
+	struct e1000_hw *hw = E1000_DEV_PRIVATE_TO_HW(dev->data->dev_private);
+	struct rte_pci_device *pci_dev = RTE_ETH_DEV_TO_PCI(dev);
+	uint32_t support_modes, st_table_sz;
+	uint32_t mode;
+	int ret;
+
+	if (hw->tph_mode != 0) {
+		PMD_DRV_LOG(ERR, "Already enable tph_mode=%u", hw->tph_mode);
+		return -EINVAL;
+	}
+
+	if (config->dev.type != RTE_ETH_CACHE_STASH_TYPE_TPH) {
+		PMD_DRV_LOG(ERR, "Unsupported stash type=%u!", config->dev.type);
+		return -ENOTSUP;
+	}
+
+	ret = rte_pci_tph_query(pci_dev, &support_modes, &st_table_sz);
+	if (ret != 0) {
+		PMD_DRV_LOG(ERR, "Query TPH failed! ret=%d", ret);
+		return -ENOTSUP;
+	}
+
+	if (support_modes & RTE_PCI_TPH_MODE_IV)
+		mode = RTE_PCI_TPH_MODE_IV;
+	else if (support_modes & RTE_PCI_TPH_MODE_DS)
+		mode = RTE_PCI_TPH_MODE_DS;
+	else
+		return -ENOTSUP;
+	ret = rte_pci_tph_enable(pci_dev, mode);
+	if (ret == 0)
+		hw->tph_mode = mode;
+
+	return ret;
+}
+
+static void
+eth_igb_cache_stash_dev_clear(struct rte_eth_dev *dev)
+{
+	uint32_t nb_queues = RTE_MAX(dev->data->nb_rx_queues, dev->data->nb_tx_queues);
+	struct e1000_hw *hw = E1000_DEV_PRIVATE_TO_HW(dev->data->dev_private);
+	struct rte_pci_device *pci_dev = RTE_ETH_DEV_TO_PCI(dev);
+	struct rte_pci_tph_entry ent = {0};
+	uint32_t reg;
+	uint32_t i;
+
+	for (i = 0; i < nb_queues; i++) {
+		ent.cpu = UINT32_MAX;
+		rte_pci_tph_st_set(pci_dev, i, &ent, 1);
+		reg = E1000_READ_REG(hw, E1000_RXCTL(i));
+		reg &= ~RTE_BIT32(0);
+		reg &= ~RTE_BIT32(2);
+		reg &= ~RTE_BIT32(3);
+		reg &= ~RTE_SHIFT_VAL32(0xFF, 24);
+		E1000_WRITE_REG(hw, E1000_RXCTL(i), reg);
+	}
+}
+
+static int
+eth_igb_cache_stash_dev_disable(struct rte_eth_dev *dev)
+{
+	struct e1000_hw *hw = E1000_DEV_PRIVATE_TO_HW(dev->data->dev_private);
+	struct rte_pci_device *pci_dev = RTE_ETH_DEV_TO_PCI(dev);
+	int ret;
+
+	if (hw->tph_mode == 0) {
+		PMD_DRV_LOG(ERR, "Already disable TPH!");
+		return -EINVAL;
+	}
+
+	eth_igb_cache_stash_dev_clear(dev);
+	ret = rte_pci_tph_disable(pci_dev);
+	if (ret == 0)
+		hw->tph_mode = 0;
+
+	return ret;
+}
+
+static int
+eth_igb_cache_stash_queue_enable(struct rte_eth_dev *dev,
+				 struct rte_eth_cache_stash_config *config)
+{
+	struct e1000_hw *hw = E1000_DEV_PRIVATE_TO_HW(dev->data->dev_private);
+	struct rte_pci_device *pci_dev = RTE_ETH_DEV_TO_PCI(dev);
+	uint32_t queue_id = config->queue.queue_id;
+	struct rte_pci_tph_entry ent = {0};
+	uint32_t reg;
+	uint16_t st;
+	int ret;
+
+	if (hw->tph_mode == 0) {
+		PMD_DRV_LOG(ERR, "Device TPH was not enabled!");
+		return -EINVAL;
+	}
+
+	ent.cpu = config->queue.lcore_id;
+	ret = rte_pci_tph_st_get(pci_dev, &ent, 1);
+	if (ret != 0) {
+		PMD_DRV_LOG(ERR, "Failed to get device queue=%u st of cpu=%u ret=%u",
+			    queue_id, config->queue.lcore_id, ret);
+		return ret;
+	}
+	st = ent.st;
+
+	reg = E1000_READ_REG(hw, E1000_RXCTL(queue_id));
+	PMD_DRV_LOG(DEBUG, "[Enable] Queue=%u rxctl register init val=0x%x", queue_id, reg);
+	if (config->queue.objects & RTE_ETH_CACHE_STASH_OBJ_RX_DESC)
+		reg |= RTE_BIT32(0);
+	else
+		reg &= ~RTE_BIT32(0);
+	if (config->queue.objects & RTE_ETH_CACHE_STASH_OBJ_RX_HEADER)
+		reg |= RTE_BIT32(2);
+	else
+		reg &= ~RTE_BIT32(2);
+	if (config->queue.objects & RTE_ETH_CACHE_STASH_OBJ_RX_PAYLOAD)
+		reg |= RTE_BIT32(3);
+	else
+		reg &= ~RTE_BIT32(3);
+	/* I350 must encoding st in high 8bit, and ST in config-space is no needed! */
+	reg |= RTE_SHIFT_VAL32(st, 24);
+	E1000_WRITE_REG(hw, E1000_RXCTL(queue_id), reg);
+	PMD_DRV_LOG(DEBUG, "[Enable] Queue=%u rxctl register after val=0x%x", queue_id, reg);
+
+	PMD_DRV_LOG(DEBUG, "[Enable] Enable device queue=%u st of cpu=%u success!",
+			    queue_id, config->queue.lcore_id);
+
+	return 0;
+}
+
+static int
+eth_igb_cache_stash_queue_disable(struct rte_eth_dev *dev,
+				  struct rte_eth_cache_stash_config *config)
+{
+	struct e1000_hw *hw = E1000_DEV_PRIVATE_TO_HW(dev->data->dev_private);
+	uint32_t queue_id = config->queue.queue_id;
+	uint32_t reg;
+
+	if (hw->tph_mode == 0) {
+		PMD_DRV_LOG(ERR, "Device TPH was not enabled!");
+		return -EINVAL;
+	}
+
+	reg = E1000_READ_REG(hw, E1000_RXCTL(queue_id));
+	PMD_DRV_LOG(DEBUG, "[Disable] Queue=%u rxctl register init val=0x%x", queue_id, reg);
+	reg &= ~RTE_BIT32(0);
+	reg &= ~RTE_BIT32(1);
+	reg &= ~RTE_BIT32(2);
+	reg &= ~RTE_BIT32(3);
+	reg &= ~RTE_SHIFT_VAL32(0xFF, 24);
+	E1000_WRITE_REG(hw, E1000_RXCTL(queue_id), reg);
+	PMD_DRV_LOG(DEBUG, "[Disable] Queue=%u rxctl register after val=0x%x", queue_id, reg);
+
+	PMD_DRV_LOG(DEBUG, "[Disable] disable device queue=%u st of cpu=%u success!",
+			    queue_id, config->queue.lcore_id);
+
+	return 0;
+}
+
+static int
+eth_igb_cache_stash_set(struct rte_eth_dev *dev, enum rte_eth_cache_stash_op op,
+			struct rte_eth_cache_stash_config *config)
+{
+	switch (op) {
+	case RTE_ETH_CACHE_STASH_OP_DEV_ENABLE:
+		return eth_igb_cache_stash_dev_enable(dev, config);
+	case RTE_ETH_CACHE_STASH_OP_DEV_DISABLE:
+		return eth_igb_cache_stash_dev_disable(dev);
+	case RTE_ETH_CACHE_STASH_OP_QUEUE_ENABLE:
+		return eth_igb_cache_stash_queue_enable(dev, config);
+	case RTE_ETH_CACHE_STASH_OP_QUEUE_DISABLE:
+		return eth_igb_cache_stash_queue_disable(dev, config);
+	default:
+		return -ENOTSUP;
+	}
+}
+
 RTE_PMD_REGISTER_PCI(net_e1000_igb, rte_igb_pmd);
 RTE_PMD_REGISTER_PCI_TABLE(net_e1000_igb, pci_id_igb_map);
 RTE_PMD_REGISTER_KMOD_DEP(net_e1000_igb, "* igb_uio | uio_pci_generic | vfio-pci");
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [PATCH v2 4/4] app/testpmd: add TPH stash objects configuration
  2026-05-12  9:22 ` [PATCH v2 0/4] Introduce generic cache stash API and PCIe TPH implementation Chengwen Feng
                     ` (2 preceding siblings ...)
  2026-05-12  9:23   ` [PATCH v2 3/4] net/e1000: add cache stash support via TPH Chengwen Feng
@ 2026-05-12  9:23   ` Chengwen Feng
  3 siblings, 0 replies; 13+ messages in thread
From: Chengwen Feng @ 2026-05-12  9:23 UTC (permalink / raw)
  To: thomas, stephen; +Cc: dev, wathsala.vithanage, liuyonglong

Add --tph-stash-objects command line option to configure PCIe TPH cache
stash objects for Rx queues. Implement enable/disable logic during
packet forwarding start/stop with device capability checks.

Signed-off-by: Chengwen Feng <fengchengwen@huawei.com>
---
 app/test-pmd/parameters.c | 20 ++++++++++
 app/test-pmd/testpmd.c    | 81 +++++++++++++++++++++++++++++++++++++++
 app/test-pmd/testpmd.h    |  1 +
 3 files changed, 102 insertions(+)

diff --git a/app/test-pmd/parameters.c b/app/test-pmd/parameters.c
index ecbd618f00..ae31d9ba4c 100644
--- a/app/test-pmd/parameters.c
+++ b/app/test-pmd/parameters.c
@@ -79,6 +79,8 @@ enum {
 	TESTPMD_OPT_NO_NUMA_NUM,
 #define TESTPMD_OPT_MP_ANON "mp-anon"
 	TESTPMD_OPT_MP_ANON_NUM,
+#define TESTPMD_OPT_TPH_STASH_OBJECTS "tph-stash-objects"
+	TESTPMD_OPT_TPH_STASH_OBJECTS_NUM,
 #define TESTPMD_OPT_PORT_NUMA_CONFIG "port-numa-config"
 	TESTPMD_OPT_PORT_NUMA_CONFIG_NUM,
 #define TESTPMD_OPT_RING_NUMA_CONFIG "ring-numa-config"
@@ -289,6 +291,7 @@ static const struct option long_options[] = {
 	NO_ARG(TESTPMD_OPT_NUMA),
 	NO_ARG(TESTPMD_OPT_NO_NUMA),
 	NO_ARG(TESTPMD_OPT_MP_ANON), /* deprecated */
+	REQUIRED_ARG(TESTPMD_OPT_TPH_STASH_OBJECTS),
 	REQUIRED_ARG(TESTPMD_OPT_PORT_NUMA_CONFIG),
 	REQUIRED_ARG(TESTPMD_OPT_RING_NUMA_CONFIG),
 	REQUIRED_ARG(TESTPMD_OPT_SOCKET_NUM),
@@ -1140,6 +1143,23 @@ launch_args_parse(int argc, char** argv)
 					"native, anon, xmem or xmemhuge\n",
 					optarg);
 			break;
+		case TESTPMD_OPT_TPH_STASH_OBJECTS_NUM:
+			if (!strcmp(optarg, "rxdesc"))
+				tph_stash_objects = RTE_ETH_CACHE_STASH_OBJ_RX_DESC;
+			else if (!strcmp(optarg, "rxheader"))
+				tph_stash_objects = RTE_ETH_CACHE_STASH_OBJ_RX_HEADER;
+			else if (!strcmp(optarg, "rxpayload"))
+				tph_stash_objects = RTE_ETH_CACHE_STASH_OBJ_RX_PAYLOAD;
+			else if (!strcmp(optarg, "rxdata"))
+				tph_stash_objects = RTE_ETH_CACHE_STASH_OBJ_RX_HEADER |
+						    RTE_ETH_CACHE_STASH_OBJ_RX_PAYLOAD;
+			else if (!strcmp(optarg, "rxall"))
+				tph_stash_objects = RTE_ETH_CACHE_STASH_OBJ_RX_DESC |
+						    RTE_ETH_CACHE_STASH_OBJ_RX_HEADER |
+						    RTE_ETH_CACHE_STASH_OBJ_RX_PAYLOAD;
+			else
+				rte_exit(EXIT_FAILURE, "unknown tph stash mode %s\n", optarg);
+			break;
 		case TESTPMD_OPT_PORT_NUMA_CONFIG_NUM:
 			if (parse_portnuma_config(optarg))
 				rte_exit(EXIT_FAILURE,
diff --git a/app/test-pmd/testpmd.c b/app/test-pmd/testpmd.c
index e2569d9e30..6f94dd3b76 100644
--- a/app/test-pmd/testpmd.c
+++ b/app/test-pmd/testpmd.c
@@ -136,6 +136,11 @@ uint8_t socket_num = UMA_NO_CONFIG;
  */
 uint8_t mp_alloc_type = MP_ALLOC_NATIVE;
 
+/*
+ * PCIE TPH stash objects.
+ */
+uint64_t tph_stash_objects;
+
 /*
  * Store specified sockets on which memory pool to be used by ports
  * is allocated.
@@ -2540,6 +2545,77 @@ update_queue_state(portid_t pid)
 	}
 }
 
+static void
+start_tph_stash(void)
+{
+	struct rte_eth_cache_stash_capability capa;
+	struct rte_eth_cache_stash_config config;
+	struct fwd_config *cfg = &cur_fwd_config;
+	struct fwd_stream *fs;
+	lcoreid_t  lc_id;
+	streamid_t sm_id;
+	portid_t pt_id;
+	int ret;
+	int i;
+
+	for (i = 0; i < cur_fwd_config.nb_fwd_ports; i++) {
+		pt_id = fwd_ports_ids[i];
+		ret = rte_eth_cache_stash_get(pt_id, &capa);
+		if (ret != 0 || (capa.supported_types & RTE_ETH_CACHE_STASH_TYPE_TPH) == 0) {
+			fprintf(stderr,	"%s: (port %u) don't support tph stash!\n",
+				__func__, pt_id);
+			return;
+		}
+		if ((capa.supported_objects & tph_stash_objects) != tph_stash_objects) {
+			fprintf(stderr, "%s: (port %u) don't support objects=0x%"PRIx64" 0x%"PRIx64"\n",
+				__func__, pt_id, tph_stash_objects, capa.supported_objects);
+			return;
+		}
+		memset(&config, 0, sizeof(config));
+		config.dev.type = RTE_ETH_CACHE_STASH_TYPE_TPH;
+		ret = rte_eth_cache_stash_set(pt_id, RTE_ETH_CACHE_STASH_OP_DEV_ENABLE, &config);
+		if (ret != 0) {
+			fprintf(stderr, "%s: (port %u) enable tph failed! ret=%d\n",
+				__func__, pt_id, ret);
+			return;
+		}
+	}
+
+	for (lc_id = 0; lc_id < cfg->nb_fwd_lcores; lc_id++) {
+		for (sm_id = 0; sm_id < fwd_lcores[lc_id]->stream_nb; sm_id++) {
+			fs = fwd_streams[fwd_lcores[lc_id]->stream_idx + sm_id];
+			memset(&config, 0, sizeof(config));
+			config.queue.lcore_id = fwd_lcores_cpuids[lc_id];
+			config.queue.queue_id = fs->rx_queue;
+			config.queue.objects = tph_stash_objects;
+			ret = rte_eth_cache_stash_set(fs->rx_port,
+				RTE_ETH_CACHE_STASH_OP_QUEUE_ENABLE, &config);
+			if (ret != 0)
+				fprintf(stderr, "%s: (port %u) enable rx-queue=%u cpu=%u stash ret=%d\n",
+					__func__, fs->rx_port, fs->rx_queue,
+					fwd_lcores_cpuids[lc_id], ret);
+		}
+	}
+}
+
+static void
+stop_tph_stash(void)
+{
+	struct rte_eth_cache_stash_config config;
+	portid_t pt_id;
+	int ret;
+	int i;
+
+	for (i = 0; i < cur_fwd_config.nb_fwd_ports; i++) {
+		pt_id = fwd_ports_ids[i];
+		memset(&config, 0, sizeof(config));
+		ret = rte_eth_cache_stash_set(pt_id, RTE_ETH_CACHE_STASH_OP_DEV_DISABLE, &config);
+		if (ret != 0)
+			fprintf(stderr, "%s: (port %u) disable tph stash ret=%d\n",
+				__func__, pt_id, ret);
+	}
+}
+
 /*
  * Launch packet forwarding configuration.
  */
@@ -2614,6 +2690,9 @@ start_packet_forwarding(int with_tx_first)
 	if(!no_flush_rx)
 		flush_fwd_rx_queues();
 
+	if (tph_stash_objects > 0)
+		start_tph_stash();
+
 	rxtx_config_display();
 
 	fwd_stats_reset();
@@ -2649,6 +2728,8 @@ stop_packet_forwarding(void)
 		fwd_lcores[lc_id]->stopped = 1;
 	printf("\nWaiting for lcores to finish...\n");
 	rte_eal_mp_wait_lcore();
+	if (tph_stash_objects > 0)
+		stop_tph_stash();
 	port_fwd_end = cur_fwd_config.fwd_eng->port_fwd_end;
 	if (port_fwd_end != NULL) {
 		for (i = 0; i < cur_fwd_config.nb_fwd_ports; i++) {
diff --git a/app/test-pmd/testpmd.h b/app/test-pmd/testpmd.h
index 9b60ebd7fc..4124d40fda 100644
--- a/app/test-pmd/testpmd.h
+++ b/app/test-pmd/testpmd.h
@@ -535,6 +535,7 @@ extern uint8_t flow_isolate_all; /**< set by "--flow-isolate-all */
 extern uint8_t no_flow_flush; /**< set by "--disable-flow-flush" parameter */
 extern uint8_t  mp_alloc_type;
 /**< set by "--mp-anon" or "--mp-alloc" parameter */
+extern uint64_t tph_stash_objects; /**< set by "--tph-stash-objects" parameter */
 extern uint32_t eth_link_speed;
 extern uint8_t no_link_check; /**<set by "--disable-link-check" parameter */
 extern uint8_t no_device_start; /**<set by "--disable-device-start" parameter */
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 13+ messages in thread

* Re: [PATCH v1 0/4] Introduce generic cache stash API and PCIe TPH implementation
  2026-05-08  9:28 [PATCH v1 0/4] Introduce generic cache stash API and PCIe TPH implementation Chengwen Feng
                   ` (4 preceding siblings ...)
  2026-05-12  9:22 ` [PATCH v2 0/4] Introduce generic cache stash API and PCIe TPH implementation Chengwen Feng
@ 2026-05-12 14:36 ` Stephen Hemminger
  2026-05-12 14:38 ` Stephen Hemminger
  6 siblings, 0 replies; 13+ messages in thread
From: Stephen Hemminger @ 2026-05-12 14:36 UTC (permalink / raw)
  To: Chengwen Feng; +Cc: thomas, dev, wathsala.vithanage

On Fri, 8 May 2026 17:28:51 +0800
Chengwen Feng <fengchengwen@huawei.com> wrote:

> Note:
> 1. This implementation references the Linux kernel VFIO TPH uapi
>    series (v8 version), which is not yet merged upstream:
>    https://lore.kernel.org/all/20260508064053.37529-1-fengchengwen@huawei.com/

DPDK changes will not be accepted until it is merged upstream.
No speculative patches.

Also, the first patch should be a single patch that updates the sanitized
kernel headers in DPDK.


^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH v1 0/4] Introduce generic cache stash API and PCIe TPH implementation
  2026-05-08  9:28 [PATCH v1 0/4] Introduce generic cache stash API and PCIe TPH implementation Chengwen Feng
                   ` (5 preceding siblings ...)
  2026-05-12 14:36 ` [PATCH v1 0/4] Introduce generic cache stash API and PCIe TPH implementation Stephen Hemminger
@ 2026-05-12 14:38 ` Stephen Hemminger
  6 siblings, 0 replies; 13+ messages in thread
From: Stephen Hemminger @ 2026-05-12 14:38 UTC (permalink / raw)
  To: Chengwen Feng; +Cc: thomas, dev, wathsala.vithanage

On Fri, 8 May 2026 17:28:51 +0800
Chengwen Feng <fengchengwen@huawei.com> wrote:

> This patch set introduces a generic ethdev cache stash framework,
> provides PCIe TPH (Transaction Processing Hints) implementation
> on PCI/VFIO bus, enables TPH cache stash support for e1000/igb PMD,
> and adds testpmd command line configuration.
> 
> The patchset is organized as follows:
> - Patch 1/4: bus/pci: introduce PCIe TPH support
>   Add generic PCIe TPH APIs and VFIO backend implementation,
>   with stub implementations for BSD and Windows.
> 
> - Patch 2/4: ethdev: introduce generic cache stash API
>   Define unified cache stash capability/operation/config abstraction,
>   support device-level and per-queue stash management.
> 
> - Patch 3/4: net/e1000: add cache stash support via TPH
>   Implement ethdev cache stash ops for igb PMD based on PCIe TPH.
> 
> - Patch 4/4: app/testpmd: add TPH stash objects configuration
>   Add testpmd CLI option to configure stash objects and
>   automatically apply stash enable/disable on forwarding start/stop.
> 
> Note:
> 1. This implementation references the Linux kernel VFIO TPH uapi
>    series (v8 version), which is not yet merged upstream:
>    https://lore.kernel.org/all/20260508064053.37529-1-fengchengwen@huawei.com/
> 
> 2. The API design and cache stash model learn from the prior  work
>    by Wathsala Vithanage: "An API for Cache Stashing with TPH".
> 
> 3. The e1000/igb PMD is enabled as the first user of this API
>    because it is the only PCIe TPH capable hardware available
>    for testing at this time. The generic API design is extensible
>    to other NICs and future cache stash mechanisms.
> 
> Chengwen Feng (4):
>   bus/pci: introduce PCIe TPH support
>   ethdev: introduce generic cache stash API
>   net/e1000: add cache stash support via TPH
>   app/testpmd: add TPH stash objects configuration
> 
>  app/test-pmd/parameters.c               |  20 ++
>  app/test-pmd/testpmd.c                  |  81 +++++++
>  app/test-pmd/testpmd.h                  |   1 +
>  drivers/bus/pci/bsd/pci.c               |  50 +++++
>  drivers/bus/pci/linux/pci.c             |  48 +++++
>  drivers/bus/pci/linux/pci_init.h        |   9 +
>  drivers/bus/pci/linux/pci_vfio.c        | 272 ++++++++++++++++++++++++
>  drivers/bus/pci/rte_bus_pci.h           | 112 ++++++++++
>  drivers/bus/pci/windows/pci.c           |  50 +++++
>  drivers/net/intel/e1000/base/e1000_hw.h |   2 +
>  drivers/net/intel/e1000/base/e1000_vf.h |   2 +
>  drivers/net/intel/e1000/igb_ethdev.c    | 221 +++++++++++++++++++
>  lib/ethdev/ethdev_driver.h              |  37 ++++
>  lib/ethdev/rte_ethdev.c                 |  40 ++++
>  lib/ethdev/rte_ethdev.h                 |  95 +++++++++
>  15 files changed, 1040 insertions(+)
> 

The longer AI generated review


Subject: [v2 0/4] PCIe TPH and ethdev cache stash - review

Review of "PCIe TPH support" / "generic cache stash" series v2.


Patch 1 - bus/pci: introduce PCIe TPH support
=============================================

Error: pci_vfio_tph_query() extracts the ST Table Size with the wrong
field mask:

	*st_table_sz = RTE_FIELD_GET32(PCI_TPH_LOC_MSIX, cap) + 1;

PCI_TPH_LOC_MSIX is 0x00000400 (bit 10, an ST Table Location bit). Per
PCIe Base Spec, ST Table Size is bits [26:16] of the TPH Requester
Capability Register; Linux defines this as PCI_TPH_CAP_ST_MASK
(0x07FF0000) with shift 16. The current code returns 1 or 2 instead of
the real table size and the result is meaningless. Add the proper
mask/shift, e.g.:

	#define PCI_TPH_CAP_ST_MASK	0x07FF0000
	#define PCI_TPH_CAP_ST_SHIFT	16
	*st_table_sz = ((cap & PCI_TPH_CAP_ST_MASK) >> PCI_TPH_CAP_ST_SHIFT) + 1;

Additionally, per PCIe spec / Linux pci_get_tph_st_num_entries(), the
Size field is only meaningful when the table is located in capability
space. When the ST table is in the MSI-X table, the entry count comes
from the MSI-X table size, not from this field. The branch that returns
the same value for LOC_CAP and LOC_MSIX is incorrect for the MSI-X
case.

Error: pci_vfio_tph_st_get() and pci_vfio_tph_st_set() return raw -1 on
the vfio_dev_fd lookup failure and return the raw ioctl(2) return value
on ioctl error. ioctl() returns -1 with errno set; the function loses
errno and reports -1 to its callers. Return -errno (or a meaningful
-EXXX in the fd case):

	if (vfio_dev_fd < 0)
		return -ENODEV;
	...
	if (ioctl(vfio_dev_fd, VFIO_DEVICE_FEATURE, feature) < 0) {
		ret = -errno;
		free(feature);
		return ret;
	}

Error: kernel/linux/uapi/linux/vfio.h adds VFIO_DEVICE_FEATURE_TPH_ST
= 13 and the surrounding vfio_device_feature_tph_st structure. The
in-tree uapi is synced from a specific upstream kernel
(kernel/linux/uapi/version says v6.16, where the last
VFIO_DEVICE_FEATURE_* is BUS_MASTER = 10). Picking 13 ahead of an
accepted kernel change is risky - upstream may take 11/12/13 for other
features, creating a permanent ABI clash. DPDK uapi headers should only
mirror accepted kernel UAPI; please rebase once the kernel change is
upstream.

Warning: rte_pci_tph_st_set() doxygen uses "@index" rather than
"@param index", and the description "Array of entries with CPU IDs and
index indices to program" reads as a typo. Also rte_pci_tph_disable()
says "negative value on error" while the rest of the new API says
"negative errno value on error" - please make these consistent.


Patch 2 - ethdev: introduce generic cache stash API
===================================================

Error: RTE_EXPORT_EXPERIMENTAL_SYMBOL(rte_eth_cache_stash_get, 26.03)
and ..._set use ABI version 26.03. The tree is currently 26.07.0-rc0
(and patch 1 correctly uses 26.07). 26.03 is a past release; new
symbols added in this cycle must use 26.07.

Warning: rte_eth_cache_stash_set() does not validate op against
RTE_ETH_CACHE_STASH_OP_BUTT. An out-of-range op is passed straight to
the driver, where the switch default just returns -ENOTSUP - validate
in the API:

	if (op >= RTE_ETH_CACHE_STASH_OP_BUTT)
		return -EINVAL;

Warning: the API uses the legacy dereference style for dev_ops
function pointers:

	if (*dev->dev_ops->cache_stash_get == NULL)
		return -ENOTSUP;
	return eth_err(port_id, (*dev->dev_ops->cache_stash_get)(dev, capa));

Recent ethdev code uses dev->dev_ops->X == NULL / dev->dev_ops->X(...)
directly; please match.

Warning: RTE_ETH_CACHE_STASH_OP_BUTT is not a typical DPDK
sentinel name; existing code uses _MAX, _LAST or _NB. Same for the
enum: rte_eth_cache_stash_type uses RTE_BIT32(0) values (a bitmask)
while the cap field that consumes it is plain uint32_t supported_types
- either keep the type as a flag enum and store flags in a plain
uint32_t (and rename the enum so it does not read as a value type),
or use #define constants like the RTE_ETH_CACHE_STASH_OBJ_* set.

Warning: the queue-level config carries a single queue_id with a
bitmask of Rx and Tx objects in the same call. There is no way for a
caller to say "stash Rx queue 3" versus "stash Tx queue 3" - they share
the queue_id field. Either split into separate enable ops or document
that OBJ_RX_* targets queue.queue_id as an Rx queue and OBJ_TX_* as a
Tx queue (with the caller required to make two calls for both).

Warning: missing release notes entry for the new public API.


Patch 3 - net/e1000: add cache stash support via TPH
====================================================

Error: supported_objects advertises TX_DESC, TX_HEADER and TX_PAYLOAD,
but eth_igb_cache_stash_queue_enable() only reads/writes
E1000_RXCTL(queue_id) and never touches E1000_TXCTL. If a caller
requests Tx stashing the call silently succeeds and does nothing.
Either implement the TXCTL programming or drop the TX_* bits from
supported_objects.

Error: eth_igb_cache_stash_dev_clear() loops over
max(nb_rx_queues, nb_tx_queues) and writes E1000_RXCTL(i):

	nb_queues = RTE_MAX(dev->data->nb_rx_queues, dev->data->nb_tx_queues);
	for (i = 0; i < nb_queues; i++) {
		...
		reg = E1000_READ_REG(hw, E1000_RXCTL(i));
		...
		E1000_WRITE_REG(hw, E1000_RXCTL(i), reg);
	}

If nb_tx_queues > nb_rx_queues this writes RXCTL entries for indices
beyond the configured Rx queues. Use nb_rx_queues for the RXCTL loop
(and a separate TXCTL loop with nb_tx_queues when Tx is implemented).

Error: eth_igb_cache_stash_queue_enable() and _queue_disable() do not
bounds-check config->queue.queue_id. E1000_RXCTL(queue_id) is just an
arithmetic register offset; a bogus queue_id writes to an arbitrary MMIO
address. Reject queue_id >= dev->data->nb_rx_queues.

Error: config->queue.lcore_id is passed directly to rte_pci_tph_st_get()
without validation. Use rte_lcore_is_enabled() (or at least
config->queue.lcore_id < RTE_MAX_LCORE) before calling.

Error: drivers/net/intel/e1000/base/e1000_hw.h and base/e1000_vf.h are
modified to add "u8 tph_mode". The base/ directory is a snapshot of
Intel's vendor base (see base/README) and DPDK-private state must not
be added there - it will be lost on the next resync. Put the field in
the DPDK-side adapter struct in drivers/net/intel/e1000/e1000_ethdev.h
instead. Also, the patch touches the VF copy of struct e1000_hw but the
implementation only lives in igb_ethdev.c (PF) - the VF addition is
dead state.

Warning: log uses %u for an int errno value:

	PMD_DRV_LOG(ERR, "Failed to get device queue=%u st of cpu=%u ret=%u",
		    queue_id, config->queue.lcore_id, ret);

ret is int and is negative on failure; print with %d.

Warning: magic bit literals in RXCTL programming - RTE_BIT32(0/2/3),
RTE_SHIFT_VAL32(0xFF, 24) - should be named per the I350 register layout
(e.g. E1000_RXCTL_RX_DESC_STASH_EN, _HDR_STASH_EN, _PLD_STASH_EN,
_CPU_STAG_MASK/SHIFT).

Warning: comment "I350 must encoding st in high 8bit, and ST in
config-space is no needed!" - grammar aside, the intent is not clear;
please reword.

Warning: doc/guides/nics/features/igb.ini is not updated for the new
cache-stash capability.

Warning: missing release notes for the new e1000 capability.


Patch 4 - app/testpmd: add TPH stash objects configuration
==========================================================

Warning: start_tph_stash() returns early on the first port that fails
the capability check or DEV_ENABLE, but ports with index < i have
already had DEV_ENABLE applied. Those ports stay enabled with no queue
configuration and will not be disabled on stop until a full
stop_tph_stash() runs. Either roll back already-enabled ports on error,
or skip just the failing port and continue.

Warning: queue-level enable failures in the inner loop only print to
stderr and forwarding proceeds with a partial configuration. Worth at
least returning failure from start_tph_stash() and propagating so
start_packet_forwarding() can refuse to run.

Warning: no entry added to usage() in app/test-pmd/parameters.c for
--tph-stash-objects. Users have no way to discover the option or its
accepted values (rxdesc, rxheader, rxpayload, rxdata, rxall).

Warning: no documentation update (doc/guides/testpmd_app_ug/) covering
the new option.

Warning: user-visible message "don't support tph stash" -> "doesn't
support tph stash" (also in the second fprintf). Minor.

Warning: "lcoreid_t  lc_id;" has a double space; "int i" should be
unsigned to match the portid_t/nb_fwd_ports loop bound type.


General
=======

The kernel-side dependency is the central blocker: until the matching
VFIO_DEVICE_FEATURE_TPH_ST is in an accepted Linux UAPI, the uapi
header import in patch 1 (and by extension all of patch 1's VFIO
ioctl plumbing) is speculative. Please post or reference the upstream
kernel patch so the feature number and structure layout are stable
before this lands in DPDK.

^ permalink raw reply	[flat|nested] 13+ messages in thread

end of thread, other threads:[~2026-05-12 14:38 UTC | newest]

Thread overview: 13+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-05-08  9:28 [PATCH v1 0/4] Introduce generic cache stash API and PCIe TPH implementation Chengwen Feng
2026-05-08  9:28 ` [PATCH v1 1/4] bus/pci: introduce PCIe TPH support Chengwen Feng
2026-05-08 15:00   ` Stephen Hemminger
2026-05-08  9:28 ` [PATCH v1 2/4] ethdev: introduce generic cache stash API Chengwen Feng
2026-05-08  9:28 ` [PATCH v1 3/4] net/e1000: add cache stash support via TPH Chengwen Feng
2026-05-08  9:28 ` [PATCH v1 4/4] app/testpmd: add TPH stash objects configuration Chengwen Feng
2026-05-12  9:22 ` [PATCH v2 0/4] Introduce generic cache stash API and PCIe TPH implementation Chengwen Feng
2026-05-12  9:22   ` [PATCH v2 1/4] bus/pci: introduce PCIe TPH support Chengwen Feng
2026-05-12  9:23   ` [PATCH v2 2/4] ethdev: introduce generic cache stash API Chengwen Feng
2026-05-12  9:23   ` [PATCH v2 3/4] net/e1000: add cache stash support via TPH Chengwen Feng
2026-05-12  9:23   ` [PATCH v2 4/4] app/testpmd: add TPH stash objects configuration Chengwen Feng
2026-05-12 14:36 ` [PATCH v1 0/4] Introduce generic cache stash API and PCIe TPH implementation Stephen Hemminger
2026-05-12 14:38 ` Stephen Hemminger

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox