* [PATCH v1 4/4] app/testpmd: add TPH stash objects configuration
From: Chengwen Feng @ 2026-05-08 9:28 UTC (permalink / raw)
To: thomas, stephen; +Cc: dev, wathsala.vithanage
In-Reply-To: <20260508092855.51987-1-fengchengwen@huawei.com>
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
* [PATCH v1 1/4] bus/pci: introduce PCIe TPH support
From: Chengwen Feng @ 2026-05-08 9:28 UTC (permalink / raw)
To: thomas, stephen; +Cc: dev, wathsala.vithanage
In-Reply-To: <20260508092855.51987-1-fengchengwen@huawei.com>
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
* [PATCH v1 2/4] ethdev: introduce generic cache stash API
From: Chengwen Feng @ 2026-05-08 9:28 UTC (permalink / raw)
To: thomas, stephen; +Cc: dev, wathsala.vithanage
In-Reply-To: <20260508092855.51987-1-fengchengwen@huawei.com>
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
* [PATCH v1 0/4] Introduce generic cache stash API and PCIe TPH implementation
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
* RE: [PATCH v1 1/1] net/iavf: fix large VF IRQ mapping
From: Morten Brørup @ 2026-05-08 9:16 UTC (permalink / raw)
To: Burakov, Anatoly, Vladimir Medvedkin, Bruce Richardson
Cc: dev, David Marchand
In-Reply-To: <3b9ba191-f086-4264-8382-653c06772e17@intel.com>
> From: Burakov, Anatoly [mailto:anatoly.burakov@intel.com]
> Sent: Thursday, 7 May 2026 10.09
>
> On 5/6/2026 5:58 PM, David Marchand wrote:
> > On Wed, 6 May 2026 at 16:07, Anatoly Burakov
> <anatoly.burakov@intel.com> wrote:
> >>
> >> The PF will check buffer size for being too big, and the chunk
> sizing code
> >> correctly calls that out. However, the size was actually still too
> big
> >> because `struct virtchnl_queue_vector_maps` already had one queue
> vector
> >> as part of its definition, so `chunk_sz` was too big by 1.
> >>
> >> Fixes: 292d3b781ac4 ("net/iavf: replace unnecessary hugepage memory
> allocations")
> >>
> >> Signed-off-by: Anatoly Burakov <anatoly.burakov@intel.com>
> >> ---
> >> drivers/net/intel/iavf/iavf_vchnl.c | 2 +-
> >> 1 file changed, 1 insertion(+), 1 deletion(-)
> >>
> >> diff --git a/drivers/net/intel/iavf/iavf_vchnl.c
> b/drivers/net/intel/iavf/iavf_vchnl.c
> >> index c2f340db81..dd09b0fa61 100644
> >> --- a/drivers/net/intel/iavf/iavf_vchnl.c
> >> +++ b/drivers/net/intel/iavf/iavf_vchnl.c
> >> @@ -1528,7 +1528,7 @@ iavf_config_irq_map_lv_chunk(struct
> iavf_adapter *adapter,
> >>
> >> /* for some reason PF side checks for buffer being too big,
> so adjust it down */
> >
> > The comment above can be removed?
>
> No, it's still relevant, because it refers to the fact that we're
> adjusting the total length downwards as opposed to leaving it at max
> size.
>
> >
> >> buf_len = sizeof(struct virtchnl_queue_vector_maps) +
> >> - sizeof(struct virtchnl_queue_vector) * chunk_sz;
> >> + sizeof(struct virtchnl_queue_vector) * (chunk_sz -
> 1);
> >
> > - did you make sure you did not break compat with previous version of
> > Intel out of tree PF driver (since this concerns configuring "Large
> > VF")?
>
> The commit in question *did* break things with previous out of tree PF
> driver. This commit fixes the breakage introduced in that commit. The
> commit being fixed was a refactor, which specified size as N-1.
>
> >
> > - all those virtchnl list struct have the same elems[1] issue.
> > Kernel side did some cleanups some time ago, maybe time for DPDK to
> do
> > the same...?
> >
>
> Yes, it is indeed time to do the same, but not as part of this
> patchset,
> and not before the base driver code is updated to do the same. There is
> some background work happening on that front already, but there are a
> lot of dependencies and moving parts, so we can't just change this
> willy
> nilly.
Is there a timeline for this fix?
With the performance improved rte_memcpy() patch [1], one of the CI compilers complains about buffer overflows when writing beyond these undersize arrays [2].
And I'd like to see the performance improved rte_memcpy() merged in 26.07.
[1]: https://patchwork.dpdk.org/project/dpdk/patch/20260429103548.220354-1-mb@smartsharesystems.com/
[2]: https://github.com/ovsrobot/dpdk/actions/runs/25104438552/job/74968218420
-Morten
^ permalink raw reply
* Re: [PATCH 6/6] cmdline: add null checks for invalid input
From: Bruce Richardson @ 2026-05-08 7:41 UTC (permalink / raw)
To: Alex Michael; +Cc: dev
In-Reply-To: <71F56436-5745-4CE1-93CD-8136788E6546@icloud.com>
On Thu, May 07, 2026 at 06:25:25PM -0400, Alex Michael wrote:
> pmd_buffer_scatter test suite failed on Ubuntu 24.04 LTS with an Intel XL710 40GbE NIC. Conversely it didn’t fail on Ubuntu 22.04 with an Intel E810 NIC, so the issue lies either with the OS (mbuf size or mempool configuration mismatch / buffer handling logic), the NIC (driver-level bug), the interaction between them or the test suite itself (as it should be unrelated to NULL conditionals).
Agree. The test failure seem unrelated to this patchset.
^ permalink raw reply
* RE: [PATCH v9] eal/x86: optimize memcpy of small sizes
From: Morten Brørup @ 2026-05-08 6:32 UTC (permalink / raw)
To: dev
In-Reply-To: <20260429103548.220354-1-mb@smartsharesystems.com>
Recheck-request: github-robot, iol-unit-arm64-testing, iol-sample-apps-testing
^ permalink raw reply
* [PATCH 2/2] eal: notify secondary on primary exit
From: Stephen Hemminger @ 2026-05-07 20:58 UTC (permalink / raw)
To: dev; +Cc: Stephen Hemminger, stable, Anatoly Burakov, Qi Zhang
In-Reply-To: <20260507210152.410419-1-stephen@networkplumber.org>
When primary process exits, it should notify the secondary
processes. This allows for clean shutdown and eliminates need
for MP handling in applications (such as test-pmd).
Bugzilla ID: 1942
Fixes: 85d6815fa6d0 ("eal: close multi-process socket during cleanup")
Cc: stable@dpdk.org
Signed-off-by: Stephen Hemminger <stephen@networkplumber.org>
---
lib/eal/common/eal_common_proc.c | 51 ++++++++++++++++++++++++++++++--
1 file changed, 49 insertions(+), 2 deletions(-)
diff --git a/lib/eal/common/eal_common_proc.c b/lib/eal/common/eal_common_proc.c
index 06f151818c..4369892e44 100644
--- a/lib/eal/common/eal_common_proc.c
+++ b/lib/eal/common/eal_common_proc.c
@@ -60,6 +60,15 @@ enum mp_type {
MP_IGN, /* Response telling requester to ignore this response */
};
+/* Message sent from primary EAL to secondary EAL */
+#define EAL_MP "mp_eal"
+struct eal_mp_req {
+ enum {
+ MP_REQ_NOP = 0,
+ MP_REQ_QUIT,
+ } type;
+};
+
struct mp_msg_internal {
int type;
struct rte_mp_msg msg;
@@ -614,6 +623,25 @@ close_socket_fd(int fd)
unlink(path);
}
+/* callback in secondary when primary has exited */
+static int
+mp_primary_exited(const struct rte_mp_msg *msg, const void *peer __rte_unused)
+{
+ const struct eal_mp_req *req;
+
+ if (msg->len_param != sizeof(*req)) {
+ EAL_LOG(ERR, "invalid request from primary");
+ return -EINVAL;
+ }
+
+ req = (const struct eal_mp_req *)msg->param;
+ if (req->type == MP_REQ_QUIT)
+ rte_mp_channel_cleanup();
+
+ /* no reply needed */
+ return 0;
+}
+
int
rte_mp_channel_init(void)
{
@@ -661,6 +689,10 @@ rte_mp_channel_init(void)
return -1;
}
+ /* listen for quit message from primary */
+ if (rte_eal_process_type() == RTE_PROC_SECONDARY)
+ rte_mp_action_register(EAL_MP, mp_primary_exited);
+
if (rte_thread_create_internal_control(&mp_handle_tid, "mp-msg",
mp_handle, NULL) < 0) {
EAL_LOG(ERR, "failed to create mp thread: %s",
@@ -682,12 +714,27 @@ rte_mp_channel_cleanup(void)
{
int fd;
+ /* Primary exiting, tell all secondary processes */
+ if (rte_eal_process_type() == RTE_PROC_PRIMARY) {
+ struct rte_mp_msg msg = { .name = EAL_MP };
+ struct eal_mp_req req = { .type = MP_REQ_QUIT };
+
+ memcpy(&msg.param, &req, sizeof(req));
+ msg.len_param = sizeof(req);
+
+ rte_mp_sendmsg(&msg);
+ } else {
+ rte_mp_action_unregister(EAL_MP);
+ }
+
fd = rte_atomic_exchange_explicit(&mp_fd, -1, rte_memory_order_relaxed);
if (fd < 0)
return;
- pthread_cancel((pthread_t)mp_handle_tid.opaque_id);
- rte_thread_join(mp_handle_tid, NULL);
+ if (pthread_self() != (pthread_t)mp_handle_tid.opaque_id) {
+ pthread_cancel((pthread_t)mp_handle_tid.opaque_id);
+ rte_thread_join(mp_handle_tid, NULL);
+ }
close_socket_fd(fd);
}
--
2.53.0
^ permalink raw reply related
* [PATCH 1/2] Revert "app/testpmd: stop forwarding in secondary process"
From: Stephen Hemminger @ 2026-05-07 20:58 UTC (permalink / raw)
To: dev; +Cc: Stephen Hemminger, stable, Aman Singh, Anatoly Burakov
In-Reply-To: <20260507210152.410419-1-stephen@networkplumber.org>
Testpmd must not assume that the secondary process
is testpmd. The notification needs to be done by EAL.
This reverts commit f96273c8e9d39e472bb07acc05e493b1e712e51b.
Bugzilla ID: 1942
Cc: stable@dpdk.org
Signed-off-by: Stephen Hemminger <stephen@networkplumber.org>
---
app/test-pmd/testpmd.c | 103 ++---------------------------------------
1 file changed, 4 insertions(+), 99 deletions(-)
diff --git a/app/test-pmd/testpmd.c b/app/test-pmd/testpmd.c
index e2569d9e30..e54a3981e9 100644
--- a/app/test-pmd/testpmd.c
+++ b/app/test-pmd/testpmd.c
@@ -3716,83 +3716,6 @@ detach_devargs(char *identifier)
rte_devargs_reset(&da);
}
-#ifndef RTE_EXEC_ENV_WINDOWS
-
-enum testpmd_req_type {
- TESTPMD_REQ_TYPE_EXIT,
-};
-
-struct testpmd_mp_req {
- enum testpmd_req_type t;
-};
-
-struct testpmd_mp_resp {
- int result;
-};
-
-#define TESTPMD_MP "mp_testpmd"
-
-/* Send reply to this peer when testpmd exits */
-static RTE_ATOMIC(const char *) primary_name;
-
-static void
-reply_to_primary(const char *peer, int result)
-{
- struct rte_mp_msg reply = { };
- struct testpmd_mp_resp *resp = (struct testpmd_mp_resp *) &reply.param;
-
- strlcpy(reply.name, TESTPMD_MP, RTE_MP_MAX_NAME_LEN);
- reply.len_param = sizeof(*resp);
- resp->result = result;
-
- printf("Replying %d to primary\n", result);
- fflush(stdout);
-
- if (rte_mp_reply(&reply, peer) < 0)
- printf("Failed to send response to primary:%s", strerror(rte_errno));
-}
-
-/* Primary process is exiting, stop secondary process */
-static void
-pmd_notify_secondary(void)
-{
- struct testpmd_mp_req request = {
- .t = TESTPMD_REQ_TYPE_EXIT,
- };
- struct rte_mp_msg mp_req = {
- .name = TESTPMD_MP,
- .len_param = sizeof(request),
- };
- struct rte_mp_reply reply;
- struct timespec ts = {.tv_sec = 5, .tv_nsec = 0};
-
- printf("\nPrimary: Sending 'stop_req' request to secondary...\n");
- fflush(stdout);
-
- memcpy(mp_req.param, &request, sizeof(request));
- rte_mp_request_sync(&mp_req, &reply, &ts);
-}
-
-static int
-handle_testpmd_request(const struct rte_mp_msg *request, const void *peer)
-{
- const struct testpmd_mp_req *req = (const struct testpmd_mp_req *)request->param;
-
- if (req->t == TESTPMD_REQ_TYPE_EXIT) {
- printf("\nReceived notification of primary exiting\n");
- fflush(stdout);
-
- /* Response is sent after forwarding loop exits */
- rte_atomic_store_explicit(&primary_name, peer, rte_memory_order_relaxed);
-
- kill(getpid(), SIGINT);
- } else {
- reply_to_primary(peer, -EINVAL);
- }
- return 0;
-}
-#endif
-
void
pmd_test_exit(void)
{
@@ -3804,10 +3727,6 @@ pmd_test_exit(void)
stop_packet_forwarding();
#ifndef RTE_EXEC_ENV_WINDOWS
- /* Tell secondary to exit */
- if (rte_eal_process_type() == RTE_PROC_PRIMARY)
- pmd_notify_secondary();
-
for (i = 0 ; i < RTE_DIM(mempools) ; i++) {
if (mempools[i]) {
if (mp_alloc_type == MP_ALLOC_ANON)
@@ -4623,12 +4542,9 @@ main(int argc, char** argv)
rte_strerror(rte_errno));
#ifndef RTE_EXEC_ENV_WINDOWS
- if (rte_eal_process_type() == RTE_PROC_SECONDARY) {
- if (enable_primary_monitor() < 0)
- rte_exit(EXIT_FAILURE, "Cannot setup primary monitor");
- if (rte_mp_action_register(TESTPMD_MP, handle_testpmd_request) < 0)
- rte_exit(EXIT_FAILURE, "Failed to register message action\n");
- }
+ if (rte_eal_process_type() == RTE_PROC_SECONDARY &&
+ enable_primary_monitor() < 0)
+ rte_exit(EXIT_FAILURE, "Cannot setup primary monitor");
#endif
/* allocate port structures, and init them */
@@ -4830,23 +4746,12 @@ main(int argc, char** argv)
}
#ifndef RTE_EXEC_ENV_WINDOWS
- if (rte_eal_process_type() == RTE_PROC_SECONDARY) {
+ if (rte_eal_process_type() == RTE_PROC_SECONDARY)
disable_primary_monitor();
- rte_mp_action_unregister(TESTPMD_MP);
- }
#endif
pmd_test_exit();
-#ifndef RTE_EXEC_ENV_WINDOWS
- if (rte_eal_process_type() == RTE_PROC_SECONDARY) {
- const char *peer = rte_atomic_exchange_explicit(&primary_name, NULL,
- rte_memory_order_relaxed);
- if (peer)
- reply_to_primary(peer, 0);
- }
-#endif
-
#ifdef RTE_LIB_PDUMP
/* uninitialize packet capture framework */
rte_pdump_uninit();
--
2.53.0
^ permalink raw reply related
* [PATCH 0/2] eal: notify secondaries on primary exit
From: Stephen Hemminger @ 2026-05-07 20:58 UTC (permalink / raw)
To: dev; +Cc: Stephen Hemminger
Bugzilla 1942: when a primary process exits cleanly, secondary
processes other than testpmd do not get notified. The notification
mechanism added in 25.11 was placed in testpmd and used
rte_mp_request_sync() with a testpmd-specific action name, so any
non-testpmd secondary (dpdk-dumpcap, dpdk-pdump, dpdk-procinfo, or
out-of-tree consumers) would log "Cannot find action: mp_testpmd"
and the primary would block on the 5 second request timeout.
Putting application-specific IPC actions on a broadcast request path
is the wrong layer. Notification of primary exit is something every
secondary needs and should come from EAL, not from each application.
Patch 1 reverts the testpmd-side mechanism (commit f96273c8e9d3).
The secondary-side primary alive monitor (enable_primary_monitor)
is preserved and continues to handle detection of primary exit via
the existing alarm-based polling of rte_eal_primary_proc_alive().
Patch 2 adds a generic EAL-level notification. On primary cleanup,
rte_mp_channel_cleanup() broadcasts an MP_REQ_QUIT message to all
known secondaries via rte_mp_sendmsg(). Secondaries register an
internal action handler that tears down their own MP channel on
receipt. No new public API; no application changes required for
any secondary, in-tree or out.
This is the minimum fix suitable for backport to 25.11 stable.
It addresses the clean exit case. The crash case (primary killed
or signaled) continues to be handled by the existing
rte_eal_primary_proc_alive() polling on the secondary side, which
detects the primary's release of the config file lock.
A more complete solution using a connected socket type (SOCK_SEQPACKET)
is planned since that can handle both planned and forced exiting
of the primary.
Tested with testpmd (primary) and dpdk-dumpcap, dpdk-pdump,
dpdk-procinfo (secondaries).
Stephen Hemminger (2):
Revert "app/testpmd: stop forwarding in secondary process"
eal: notify secondary on primary exit
app/test-pmd/testpmd.c | 103 ++-----------------------------
lib/eal/common/eal_common_proc.c | 51 ++++++++++++++-
2 files changed, 53 insertions(+), 101 deletions(-)
--
2.53.0
^ permalink raw reply
* Re: [RFC 0/3] fib: tbl8 reservation drift reproducer and proposed fix
From: Stephen Hemminger @ 2026-05-07 19:02 UTC (permalink / raw)
To: Maxime Leroy; +Cc: Vladimir Medvedkin, dev
In-Reply-To: <cover.1778146229.git.maxime@leroys.fr>
On Thu, 7 May 2026 11:50:40 +0200
Maxime Leroy <maxime@leroys.fr> wrote:
> Asymmetric ADD/DEL sequences in FIB6 trie (a covering parent
> removed between the ADD and DEL of a longer prefix) eventually
> make ADD fail with -ENOSPC even when the tbl8 pool is empty.
>
> Patch 2/3 is a small reproducer.
>
> Root cause: rsvd_tbl8s is updated by depth_diff recomputed from
> the current RIB, so increments at ADD and decrements at DEL do
> not cancel when the RIB state changes in between. The counter
> drifts and wraps to UINT32_MAX.
>
> The simplest fix I could find (1/3, 3/3) is to drop rsvd_tbl8s
> and use the pool counters already maintained by alloc/free:
> tbl8_pool_pos in trie, cur_tbl8s in dir24_8. The DQ reclaim
> inside tbl8_alloc() is moved into the pre-check.
>
> I am not sure I understood the original intent of keeping
> rsvd_tbl8s separate from the pool counters. In dir24_8 the two
> mirror each other 1:1 and rsvd_tbl8s looks redundant; in trie,
> depth_diff gives it a worst-case-reservation flavor but the
> recomputation from the RIB is exactly what makes it drift. If
> there was a deliberate reason, please point it out.
>
> Patch 3/3 is a no-op cleanup that aligns dir24_8 with the trie
> pattern.
>
> Maxime Leroy (3):
> fib6: fix tbl8 reservation drift in trie
> test/fib6: add reproducer for tbl8 reservation drift
> fib: drop redundant tbl8 reservation counter
>
> app/test/test_fib6.c | 83 ++++++++++++++++++++++++++++++++++++++++++++
> lib/fib/dir24_8.c | 31 ++++++-----------
> lib/fib/dir24_8.h | 1 -
> lib/fib/trie.c | 23 ++++++------
> lib/fib/trie.h | 3 +-
> 5 files changed, 104 insertions(+), 37 deletions(-)
>
Used this a test case for multiple AI reviewers.
Most reported false positives in various places.
But the winner was Claude web (or desktop) with Opus 4.7 extended.
It saw:
Patch 3 - fib: drop redundant tbl8 reservation counter
Warning: The commit message frames this as removing a "redundant" counter and claims "Behavior is unchanged for non-DQ modes," but dir24_8 has the same rsvd_tbl8s drift bug that patch 1/3 fixes for trie6 — this patch silently fixes it as well.
Trace (no RCU, depth-1 mode):
ADD /28 A (no /24 cover):
- tmp == NULL, pre-check passes
- install_to_fib allocates 1 tbl8 -> cur_tbl8s = 1
- post-add hook: tmp == NULL -> rsvd_tbl8s++ = 1
ADD /24 B covering A:
- depth == 24, no rsvd_tbl8s update
- tbl8 entries rewritten, no alloc/free
- state: rsvd_tbl8s = 1, cur_tbl8s = 1
DEL /28 A (with /24 cover B):
- modify_fib installs B's nh over A's range
- tbl8 becomes uniform -> tbl8_recycle() -> cur_tbl8s = 0
- post-del check: tmp != NULL (B is the cover) -> rsvd_tbl8s NOT decremented
- state: rsvd_tbl8s = 1, cur_tbl8s = 0 (diverged)
DEL /24 B:
- depth == 24, no rsvd_tbl8s update
- final: rsvd_tbl8s = 1, cur_tbl8s = 0
Each repetition leaks +1 into rsvd_tbl8s with no actual tbl8 in use. Over enough cycles, rsvd_tbl8s reaches number_tbl8s and the pre-check rejects valid /25-/32 ADDs even though the pool is empty. This is the same class of bug as the IPv6 case, just with smaller per-cycle drift.
The patch should carry:
Fixes: <commit that introduced rsvd_tbl8s in dir24_8>
Cc: stable@dpdk.org
^ permalink raw reply
* Re: [RFC v2 0/4] flow_compile: textual flow rule compiler
From: Stephen Hemminger @ 2026-05-07 16:57 UTC (permalink / raw)
To: Bruce Richardson; +Cc: dev
In-Reply-To: <afy9RC1UUlGlqp2G@bricha3-mobl1.ger.corp.intel.com>
On Thu, 7 May 2026 17:26:44 +0100
Bruce Richardson <bruce.richardson@intel.com> wrote:
> Checking with Claude, it seems it's there already:
>
> Multi-string (TOKEN_STRING_MULTI) — reads until cmdline_isendofcommand(), which stops only at \n, \r, \0, or #. This captures the entire remainder of the line including spaces.
>
> > - AI proposed new syntax:
> > flow compile <port> "quote rule"
>
> I tend to prefer explicit pre-field names in the syntax as a general rule
> as it makes it clearer what the numeric values in the command are. So I
> suggest e.g.:
>
> "flow_compile port <N> rule: ...."
>
> In the absense of quoting, I think having a ":" at the end of rule helps to
> separate the testpmd syntax from the rule syntax.
Would be good to have flow_compile as temporary solution and replace the old parser later.
I can't see huge requirement for quoting with current syntax, good to have but no fields seem to require it
^ permalink raw reply
* [PATCH v1] dts: clean cryptodev environment after a test run
From: Andrew Bailey @ 2026-05-07 16:36 UTC (permalink / raw)
To: dev, luca.vizzarro, patrickrobb1997
Cc: lylavoie, ahassick, knimoji, Andrew Bailey
Prior to this patch, the virtual functions created by DTS for crypto
devices were not properly cleanedu up. This could lead to a system to be
left in a bad state after running the cryptodev throughput test suite.
This patch properly cleans up the environment after cryptodev testing.
Bugzilla ID: 1923
Fixes: 8ee2df9da125 ("dts: add cryptodev package")
Signed-off-by: Andrew Bailey <abailey@iol.unh.edu>
---
dts/framework/testbed_model/linux_session.py | 28 +++++++++++++++++++-
dts/framework/testbed_model/os_session.py | 8 ++++++
2 files changed, 35 insertions(+), 1 deletion(-)
diff --git a/dts/framework/testbed_model/linux_session.py b/dts/framework/testbed_model/linux_session.py
index ee943462c2..a5bb34b4e8 100644
--- a/dts/framework/testbed_model/linux_session.py
+++ b/dts/framework/testbed_model/linux_session.py
@@ -181,6 +181,21 @@ def get_port_info(self, pci_address: str) -> PortInfo:
return PortInfo(mac_address, logical_name, driver, is_link_up)
+ def unbind_ports(self, ports):
+ """Overrides :meth:`~.os_session.OSSession.unbind_ports`.
+
+ The :attr:`~.devbind_script_path` property must be setup in order to call this method.
+ """
+ ports_pci_addrs = " ".join(port.pci for port in ports)
+
+ self.send_command(
+ f"{self.devbind_script_path} -u --force {ports_pci_addrs}",
+ privileged=True,
+ verify=True,
+ )
+
+ del self._lshw_net_info
+
def bind_ports_to_driver(self, ports: list[Port], driver_name: str) -> None:
"""Overrides :meth:`~.os_session.OSSession.bind_ports_to_driver`.
@@ -289,7 +304,18 @@ def create_vfs(self, pf_port: Port) -> None:
self.refresh_lshw()
def delete_crypto_vfs(self, pf_port: Port) -> None:
- """Overrides :meth:`~.os_session.OSSession.delete_crypto_vfs`."""
+ """Overrides :meth:`~.os_session.OSSession.delete_crypto_vfs`.
+
+ The :attr:`~.devbind_script_path` property must be setup in order to call this method.
+ """
+ if vfs := self.get_pci_addr_of_crypto_vfs(pf_port):
+ vf_pci_addrs = " ".join(vf for vf in vfs)
+ self.send_command(
+ f"{self.devbind_script_path} -u --force {vf_pci_addrs}",
+ privileged=True,
+ verify=True,
+ )
+ self.unbind_ports([pf_port])
self.send_command(
f"echo 1 | sudo tee /sys/bus/pci/devices/{pf_port.pci}/remove".replace(":", "\\:"),
privileged=True,
diff --git a/dts/framework/testbed_model/os_session.py b/dts/framework/testbed_model/os_session.py
index 2c267afed1..f2dc9b20a9 100644
--- a/dts/framework/testbed_model/os_session.py
+++ b/dts/framework/testbed_model/os_session.py
@@ -573,6 +573,14 @@ def get_port_info(self, pci_address: str) -> PortInfo:
ConfigurationError: If the port could not be found.
"""
+ @abstractmethod
+ def unbind_ports(self, ports: list[Port]) -> None:
+ """Unbind `ports` from any driver.
+
+ Args:
+ ports: The list of ports to unbind.
+ """
+
@abstractmethod
def bind_ports_to_driver(self, ports: list[Port], driver_name: str) -> None:
"""Bind `ports` to the given `driver_name`.
--
2.50.1
^ permalink raw reply related
* Re: [RFC v2 0/4] flow_compile: textual flow rule compiler
From: Bruce Richardson @ 2026-05-07 16:26 UTC (permalink / raw)
To: Stephen Hemminger; +Cc: dev
In-Reply-To: <20260507090923.0f8a620f@phoenix.local>
On Thu, May 07, 2026 at 09:09:23AM -0700, Stephen Hemminger wrote:
> On Thu, 7 May 2026 09:10:48 +0100
> Bruce Richardson <bruce.richardson@intel.com> wrote:
>
> > > 1. API shape. pcap_compile-style (one string -> opaque object ->
> > > arrays) versus the three-call attr/pattern/actions form
> > > Sismis's v12 exposes. What does your application actually
> > > want?
> > >
> >
> > For this, I wonder if we also could do with a second API for the creation
> > which takes a list of tokens rather than just a single string. Thinking
> > about integration with testpmd, or with apps which already have some
> > commandline interface which produces a list of tokens, having to re-stitch
> > the tokens together into one string seems awkward.
> >
> > Also, have you already investigated how this might be integrated into
> > testpmd? Do we have the capability to pass multi-token strings via cmdline?
>
> Lex pass does tokenizing in a way that is different than simple string split.
> Could have a wrapper that takes list of tokens and quotes them back to
> a string.
>
> For testpmd integration.
> - the new compiler may intentionally diverge from existing adhoc
> parsing. The AI code generation already flagged a couple of these
> and put note in documentation.
>
> - testpmd (and probably cmdline) will need ability to not pass unparsed
> string, may need new cmdline type for "rest of line as string"
>
Checking with Claude, it seems it's there already:
Multi-string (TOKEN_STRING_MULTI) — reads until cmdline_isendofcommand(), which stops only at \n, \r, \0, or #. This captures the entire remainder of the line including spaces.
> - AI proposed new syntax:
> flow compile <port> "quote rule"
I tend to prefer explicit pre-field names in the syntax as a general rule
as it makes it clearer what the numeric values in the command are. So I
suggest e.g.:
"flow_compile port <N> rule: ...."
In the absense of quoting, I think having a ":" at the end of rule helps to
separate the testpmd syntax from the rule syntax.
/Bruce
^ permalink raw reply
* Re: [RFC v2 0/4] flow_compile: textual flow rule compiler
From: Stephen Hemminger @ 2026-05-07 16:09 UTC (permalink / raw)
To: Bruce Richardson; +Cc: dev
In-Reply-To: <afxJCBfkbui5clep@bricha3-mobl1.ger.corp.intel.com>
On Thu, 7 May 2026 09:10:48 +0100
Bruce Richardson <bruce.richardson@intel.com> wrote:
> > 1. API shape. pcap_compile-style (one string -> opaque object ->
> > arrays) versus the three-call attr/pattern/actions form
> > Sismis's v12 exposes. What does your application actually
> > want?
> >
>
> For this, I wonder if we also could do with a second API for the creation
> which takes a list of tokens rather than just a single string. Thinking
> about integration with testpmd, or with apps which already have some
> commandline interface which produces a list of tokens, having to re-stitch
> the tokens together into one string seems awkward.
>
> Also, have you already investigated how this might be integrated into
> testpmd? Do we have the capability to pass multi-token strings via cmdline?
Lex pass does tokenizing in a way that is different than simple string split.
Could have a wrapper that takes list of tokens and quotes them back to
a string.
For testpmd integration.
- the new compiler may intentionally diverge from existing adhoc
parsing. The AI code generation already flagged a couple of these
and put note in documentation.
- testpmd (and probably cmdline) will need ability to not pass unparsed
string, may need new cmdline type for "rest of line as string"
- AI proposed new syntax:
flow compile <port> "quote rule"
^ permalink raw reply
* RE: [PATCH v7 1/4] lib/net: add IEEE 1588 PTP v2 protocol header definitions
From: Morten Brørup @ 2026-05-07 15:27 UTC (permalink / raw)
To: Rajesh Kumar, dev; +Cc: bruce.richardson, aman.deep.singh, stephen
In-Reply-To: <20260507134529.2573300-2-rajesh3.kumar@intel.com>
> From: Rajesh Kumar [mailto:rajesh3.kumar@intel.com]
> Sent: Thursday, 7 May 2026 15.45
> To: dev@dpdk.org
>
> Add PTP (Precision Time Protocol) header structures and inline helper
> functions to lib/net following DPDK conventions for protocol libraries
> (similar to rte_tcp.h, rte_ip.h).
>
> Provides wire-format structures with endian-annotated types:
> - rte_ptp_port_id: 10-byte port identity with EUI-64 clock ID
> - rte_ptp_hdr: 34-byte common message header with correctionField
> - rte_ptp_timestamp: 10-byte nanosecond-precision timestamp
>
> PTP message type constants for all IEEE 1588-2019 message types
> (Sync, Delay_Req, Announce, Management, etc.) and flag field bits
> (two-step, unicast, leap indicator).
>
> Inline accessor and utility functions for performance:
> - rte_ptp_msg_type(), rte_ptp_version(), rte_ptp_domain()
> - rte_ptp_seq_id(), rte_ptp_is_event(), rte_ptp_is_two_step()
> - rte_ptp_correction_ns(), rte_ptp_add_correction()
> - rte_ptp_timestamp_to_ns() for timestamp conversion
>
> Supports all PTP encapsulations: L2 (EtherType 0x88F7), VLAN/QinQ,
> UDP/IPv4, and UDP/IPv6 (ports 319/320).
>
> All inline functions — zero function call overhead, suitable for
> real-time packet processing.
>
> Signed-off-by: Rajesh Kumar <rajesh3.kumar@intel.com>
Great progress.
My next wave of comments inline below. :-)
> ---
> MAINTAINERS | 6 +
> lib/net/meson.build | 1 +
> lib/net/rte_ptp.h | 264 ++++++++++++++++++++++++++++++++++++++++++++
> 3 files changed, 271 insertions(+)
> create mode 100644 lib/net/rte_ptp.h
>
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 0f5539f851..da31ada871 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -1665,6 +1665,12 @@ F: doc/guides/prog_guide/ipsec_lib.rst
> M: Vladimir Medvedkin <vladimir.medvedkin@intel.com>
> F: app/test-sad/
>
> +PTP - lib/net
Please remove lib/net from this headline.
Intead, suggest:
PTP (IEEE 1588 Precision Time Protocol)
> +M: Rajesh Kumar <rajesh3.kumar@intel.com>
> +F: lib/net/rte_ptp.h
> +F: examples/ptp_tap_relay_sw/
> +F: doc/guides/sample_app_ug/ptp_tap_relay_sw.rst
> +
> PDCP - EXPERIMENTAL
> M: Anoob Joseph <anoobj@marvell.com>
> M: Volodymyr Fialko <vfialko@marvell.com>
> diff --git a/lib/net/meson.build b/lib/net/meson.build
> index 3fad5edc5b..63d13719f3 100644
> --- a/lib/net/meson.build
> +++ b/lib/net/meson.build
> @@ -28,6 +28,7 @@ headers = files(
> 'rte_geneve.h',
> 'rte_l2tpv2.h',
> 'rte_ppp.h',
> + 'rte_ptp.h',
> 'rte_ib.h',
> )
>
> diff --git a/lib/net/rte_ptp.h b/lib/net/rte_ptp.h
> new file mode 100644
> index 0000000000..649b944d29
> --- /dev/null
> +++ b/lib/net/rte_ptp.h
> @@ -0,0 +1,264 @@
> +/* SPDX-License-Identifier: BSD-3-Clause
> + * Copyright(c) 2026 Intel Corporation
> + */
> +
> +#ifndef _RTE_PTP_H_
> +#define _RTE_PTP_H_
> +
> +/**
> + * @file
> + *
> + * PTP (IEEE 1588) protocol definitions
> + */
> +
> +#include <stdint.h>
> +#include <stdbool.h>
> +
> +#include <rte_byteorder.h>
> +#include <rte_common.h>
> +
> +#ifdef __cplusplus
> +extern "C" {
> +#endif
> +
> +/*
> + * PTP Constants
> + */
> +
> +/** PTP over UDP event port (Sync, Delay_Req, PDelay_Req,
> PDelay_Resp). */
> +#define RTE_PTP_EVENT_PORT 319
> +
> +/** PTP over UDP general port (Follow_Up, Delay_Resp, Announce, etc.).
> */
> +#define RTE_PTP_GENERAL_PORT 320
The libc header <netinet/in.h> defines IPPORT_xxx.
We should use something similar:
#define RTE_IPPORT_PTP_EVENT and RTE_IPPORT_PTP_GENERAL
> +
> +/** PTP multicast MAC address: 01:1B:19:00:00:00. */
> +#define RTE_PTP_MULTICAST_MAC { 0x01, 0x1B, 0x19, 0x00, 0x00, 0x00
> }
> +
> +/** PTP peer delay multicast MAC: 01:80:C2:00:00:0E. */
> +#define RTE_PTP_PDELAY_MULTICAST_MAC { 0x01, 0x80, 0xC2, 0x00, 0x00,
> 0x0E }
Similarly here; we should establish a convention for MAC addresses,
like RTE_ETHER_TYPE_xxx in DPDK <lib/net/rte_ether.h>.
Suggest:
RTE_ETHER_ADDR_PTP_MULTICAST and RTE_ETHER_ADDR_PTP_MULTICAST_PDELAY
This also follows the general naming convention of having the broadest scope is first and the narrowest scope last.
> +
> +/*
> + * PTP Message Types (IEEE 1588-2019 Table 36)
> + */
> +
> +#define RTE_PTP_MSGTYPE_SYNC 0x0 /**< Sync (event). */
> +#define RTE_PTP_MSGTYPE_DELAY_REQ 0x1 /**< Delay_Req (event).
> */
> +#define RTE_PTP_MSGTYPE_PDELAY_REQ 0x2 /**< Peer_Delay_Req
> (event). */
> +#define RTE_PTP_MSGTYPE_PDELAY_RESP 0x3 /**< Peer_Delay_Resp
> (event). */
> +#define RTE_PTP_MSGTYPE_FOLLOW_UP 0x8 /**< Follow_Up (general).
> */
> +#define RTE_PTP_MSGTYPE_DELAY_RESP 0x9 /**< Delay_Resp
> (general). */
> +#define RTE_PTP_MSGTYPE_PDELAY_RESP_FU 0xA /**<
> Peer_Delay_Resp_Follow_Up. */
Missing in the description: (general)
For consistency, consider renaming RTE_PTP_MSGTYPE_FOLLOW_UP to RTE_PTP_MSGTYPE_FU.
> +#define RTE_PTP_MSGTYPE_ANNOUNCE 0xB /**< Announce (general).
> */
> +#define RTE_PTP_MSGTYPE_SIGNALING 0xC /**< Signaling (general).
> */
> +#define RTE_PTP_MSGTYPE_MANAGEMENT 0xD /**< Management
> (general). */
> +
> +/*
> + * PTP Flag Field Bits (IEEE 1588-2019 Table 37)
> + *
> + * These constants are for use after rte_be_to_cpu_16(hdr->flags).
> + * flagField[0] (octet 6) maps to host bits 8-15.
> + * flagField[1] (octet 7) maps to host bits 0-7.
> + */
> +
> +#define RTE_PTP_FLAG_TWO_STEP (1 << 9) /**< Two-step flag. */
> +#define RTE_PTP_FLAG_UNICAST (1 << 10) /**< Unicast flag. */
> +#define RTE_PTP_FLAG_LI_61 (1 << 0) /**< Leap indicator 61. */
> +#define RTE_PTP_FLAG_LI_59 (1 << 1) /**< Leap indicator 59. */
We don't have a RTE_BIT16() macro like the RTE_BIT32/64() macros, so maybe use:
#define RTE_PTP_FLAG_TWO_STEP (UINT16_C(1) << 9) /**< Two-step flag. */
#define RTE_PTP_FLAG_UNICAST (UINT16_C(1) << 10) /**< Unicast flag. */
#define RTE_PTP_FLAG_LI_61 (UINT16_C(1) << 0) /**< Leap indicator 61. */
#define RTE_PTP_FLAG_LI_59 (UINT16_C(1) << 1) /**< Leap indicator 59. */
> +
> +/*
> + * PTP Header Structures (IEEE 1588-2019)
> + */
> +
> +/**
> + * PTP Port Identity (10 bytes).
> + */
> +struct __rte_packed_begin rte_ptp_port_id {
> + uint8_t clock_id[8]; /**< clockIdentity (EUI-64). */
> + rte_be16_t port_number; /**< portNumber. */
> +} __rte_packed_end;
> +
> +/**
> + * PTP Common Message Header (34 bytes).
> + */
> +struct __rte_packed_begin rte_ptp_hdr {
> + uint8_t msg_type; /**< transportSpecific (4) |
> messageType (4). */
> + uint8_t version; /**< minorVersionPTP (4) | versionPTP
> (4). */
The two fields above should be split into their nibbles, for direct access, like the version_ihl field in the IPv4 header:
https://elixir.bootlin.com/dpdk/v26.03/source/lib/net/rte_ip4.h#L43
E.g.:
__extension__
union {
uint8_t ts_msgtype; /**< Message type */
struct {
#if RTE_BYTE_ORDER == RTE_LITTLE_ENDIAN
uint8_t msg_type:4; /**< messageType */
uint8_t ts:4; /**< transportSpecific */
#elif RTE_BYTE_ORDER == RTE_BIG_ENDIAN
uint8_t ts:4; /**< transportSpecific */
uint8_t msg_type:4; /**< messageType */
#endif
};
};
Warning: I'm not sure I got the nibble order right. Make sure you do! :-)
> + rte_be16_t msg_length; /**< Total message length in bytes. */
> + uint8_t domain_number; /**< PTP domain (0-255). */
> + uint8_t minor_sdo_id; /**< minorSdoId (IEEE 1588-2019). */
> + rte_be16_t flags; /**< Flag field (see RTE_PTP_FLAG_*).
> */
> + rte_be64_t correction; /**< correctionField (scaled ns, 48.16
> fixed). */
> + rte_be32_t msg_type_specific; /**< messageTypeSpecific. */
> + struct rte_ptp_port_id source_port_id; /**< sourcePortIdentity.
> */
> + rte_be16_t sequence_id; /**< sequenceId. */
> + uint8_t control; /**< controlField (deprecated in 1588-
> 2019). */
> + int8_t log_msg_interval; /**< logMessageInterval. */
> +} __rte_packed_end;
> +
> +/**
> + * PTP Timestamp (10 bytes, used in Sync/Delay_Req/Follow_Up bodies).
The PTP timestamp origo should be mentioned here.
E.g. the UNIX time_t origo is Jan 1st 00:00:00 1970.
> + */
> +struct __rte_packed_begin rte_ptp_timestamp {
> + rte_be16_t seconds_hi; /**< Upper 16 bits of seconds. */
> + rte_be32_t seconds_lo; /**< Lower 32 bits of seconds. */
> + rte_be32_t nanoseconds; /**< Nanoseconds (0-999999999). */
> +} __rte_packed_end;
> +
> +/*
> + * Inline Helpers
> + */
> +
> +/**
> + * Extract PTP message type from header.
> + *
> + * @param hdr
> + * Pointer to PTP header.
> + * @return
> + * Message type (0x0-0xF).
> + */
> +static inline uint8_t
> +rte_ptp_msg_type(const struct rte_ptp_hdr *hdr)
> +{
> + return hdr->msg_type & 0x0F;
> +}
> +
> +/**
> + * Extract transport-specific field from header.
> + *
> + * @param hdr
> + * Pointer to PTP header.
> + * @return
> + * Transport-specific value (upper nibble, 0x0-0xF).
> + */
> +static inline uint8_t
> +rte_ptp_transport_specific(const struct rte_ptp_hdr *hdr)
> +{
> + return (hdr->msg_type >> 4) & 0x0F;
> +}
> +
> +/**
> + * Extract PTP version from header.
> + *
> + * @param hdr
> + * Pointer to PTP header.
> + * @return
> + * PTP version number (typically 2).
> + */
> +static inline uint8_t
> +rte_ptp_version(const struct rte_ptp_hdr *hdr)
> +{
> + return hdr->version & 0x0F;
> +}
> +
> +/**
> + * Get sequence ID from PTP header (host byte order).
> + *
> + * @param hdr
> + * Pointer to PTP header.
> + * @return
> + * Sequence ID in host byte order.
> + */
> +static inline uint16_t
> +rte_ptp_seq_id(const struct rte_ptp_hdr *hdr)
> +{
> + return rte_be_to_cpu_16(hdr->sequence_id);
> +}
> +
> +/**
> + * Get PTP domain number.
> + *
> + * @param hdr
> + * Pointer to PTP header.
> + * @return
> + * Domain number (0-255).
> + */
> +static inline uint8_t
> +rte_ptp_domain(const struct rte_ptp_hdr *hdr)
> +{
> + return hdr->domain_number;
> +}
The above "get" accessor functions are superfluous. Please remove:
rte_ptp_msg_type()
rte_ptp_transport_specific()
rte_ptp_version()
rte_ptp_seq_id()
rte_ptp_domain()
> +
> +/**
> + * Check if PTP message type is an event message.
> + * Event messages (msg_type 0x0-0x3) require timestamps.
> + *
> + * @param msg_type
> + * PTP message type value (0x0-0xF).
> + * @return
> + * true if event message, false otherwise.
> + */
> +static inline bool
> +rte_ptp_is_event(int msg_type)
> +{
> + return msg_type >= 0 && msg_type <= RTE_PTP_MSGTYPE_PDELAY_RESP;
> +}
Suggest passing the parameter as const struct rte_ptp_hdr *hdr, like in rte_ptp_is_two_step() below.
> +
> +/**
> + * Check if the two-step flag is set in a PTP header.
> + *
> + * @param hdr
> + * Pointer to PTP header.
> + * @return
> + * true if two-step flag is set.
> + */
> +static inline bool
> +rte_ptp_is_two_step(const struct rte_ptp_hdr *hdr)
> +{
> + return (rte_be_to_cpu_16(hdr->flags) & RTE_PTP_FLAG_TWO_STEP) !=
> 0;
Faster way:
return (hdr->flags & RTE_BE16(RTE_PTP_FLAG_TWO_STEP)) != 0;
> +}
> +
> +/**
> + * Get correctionField value in nanoseconds (from 48.16 fixed-point).
> + *
> + * @param hdr
> + * Pointer to PTP header.
> + * @return
> + * Correction value in nanoseconds.
> + */
> +static inline int64_t
> +rte_ptp_correction_ns(const struct rte_ptp_hdr *hdr)
> +{
> + return (int64_t)rte_be_to_cpu_64(hdr->correction) >> 16;
> +}
This "get" accessor function is superfluous. Please remove:
rte_ptp_correction_ns()
The "correction" field in the PTP header structure should sufficiently describe the field's fixed-point encoding.
> +
> +/**
> + * Add a residence time (in nanoseconds) to the correctionField.
> + * Used by Transparent Clocks to account for relay transit delay.
> + * The correctionField uses IEEE 1588 scaled nanoseconds (48.16 fixed-
> point).
> + *
> + * @param hdr
> + * Pointer to PTP header (will be modified in-place).
> + * @param residence_ns
> + * Residence time in nanoseconds to add.
> + */
> +static inline void
> +rte_ptp_add_correction(struct rte_ptp_hdr *hdr, int64_t residence_ns)
> +{
> + int64_t cf = (int64_t)rte_be_to_cpu_64(hdr->correction);
> +
> + cf += (int64_t)((uint64_t)residence_ns << 16);
> + hdr->correction = rte_cpu_to_be_64(cf);
> +}
I don't think negative time can be added, so please use uint64_t instead of int64_t for the parameter and inside the function.
Note to reviewers: "residence time" is the term in the standard. "sojourn time" is not used.
> +
> +/**
> + * Convert a PTP timestamp structure to nanoseconds since epoch.
> + *
> + * @param ts
> + * Pointer to PTP timestamp.
> + * @return
> + * Time in nanoseconds since epoch.
> + */
> +static inline uint64_t
> +rte_ptp_timestamp_to_ns(const struct rte_ptp_timestamp *ts)
> +{
> + uint64_t sec = ((uint64_t)rte_be_to_cpu_16(ts->seconds_hi) << 32)
> |
> + rte_be_to_cpu_32(ts->seconds_lo);
> +
> + return sec * 1000000000ULL + rte_be_to_cpu_32(ts->nanoseconds);
> +}
1000000000ULL -> UINT64_C(1000000000)
> +
> +#ifdef __cplusplus
> +}
> +#endif
> +
> +#endif /* _RTE_PTP_H_ */
> --
> 2.53.0
^ permalink raw reply
* [PATCH 6/6] cmdline: add null checks for invalid input
From: Bruce Richardson @ 2026-05-07 14:59 UTC (permalink / raw)
To: dev; +Cc: Bruce Richardson
In-Reply-To: <20260507145950.197753-1-bruce.richardson@intel.com>
To harden the public API, add some NULL checks before dereferencing
pointers. Properly written apps should never call these with NULL, but
to increase resilience, we'll add the checks.
Signed-off-by: Bruce Richardson <bruce.richardson@intel.com>
---
lib/cmdline/cmdline.c | 3 +++
lib/cmdline/cmdline_rdline.c | 3 +++
2 files changed, 6 insertions(+)
diff --git a/lib/cmdline/cmdline.c b/lib/cmdline/cmdline.c
index d1003f0b8e..51fbc36cef 100644
--- a/lib/cmdline/cmdline.c
+++ b/lib/cmdline/cmdline.c
@@ -103,6 +103,9 @@ RTE_EXPORT_SYMBOL(cmdline_get_rdline)
struct rdline*
cmdline_get_rdline(struct cmdline *cl)
{
+ if (!cl)
+ return NULL;
+
return &cl->rdl;
}
diff --git a/lib/cmdline/cmdline_rdline.c b/lib/cmdline/cmdline_rdline.c
index 0a5a399b32..15da285c8d 100644
--- a/lib/cmdline/cmdline_rdline.c
+++ b/lib/cmdline/cmdline_rdline.c
@@ -618,6 +618,9 @@ RTE_EXPORT_SYMBOL(rdline_get_history_buffer_size)
size_t
rdline_get_history_buffer_size(struct rdline *rdl)
{
+ if (!rdl)
+ return 0;
+
return sizeof(rdl->history_buf);
}
--
2.51.0
^ permalink raw reply related
* [PATCH 5/6] cmdline: guard zero-size destination buffers
From: Bruce Richardson @ 2026-05-07 14:59 UTC (permalink / raw)
To: dev; +Cc: Bruce Richardson, stable
In-Reply-To: <20260507145950.197753-1-bruce.richardson@intel.com>
Add missing zero-size destination checks in cmdline helper routines.
Always check for size == 0, before doing an assignment of '\0' to string
position of "size - 1".
Fixes: af75078fece3 ("first public release")
Cc: stable@dpdk.org
Signed-off-by: Bruce Richardson <bruce.richardson@intel.com>
---
lib/cmdline/cmdline_parse_num.c | 2 +-
lib/cmdline/cmdline_parse_string.c | 3 +++
2 files changed, 4 insertions(+), 1 deletion(-)
diff --git a/lib/cmdline/cmdline_parse_num.c b/lib/cmdline/cmdline_parse_num.c
index f21796bedb..4cfc900391 100644
--- a/lib/cmdline/cmdline_parse_num.c
+++ b/lib/cmdline/cmdline_parse_num.c
@@ -323,7 +323,7 @@ cmdline_get_help_num(cmdline_parse_token_hdr_t *tk, char *dstbuf, unsigned int s
struct cmdline_token_num_data nd;
int ret;
- if (!tk)
+ if (!tk || !dstbuf || size == 0)
return -1;
memcpy(&nd, &((struct cmdline_token_num *)tk)->num_data, sizeof(nd));
diff --git a/lib/cmdline/cmdline_parse_string.c b/lib/cmdline/cmdline_parse_string.c
index 731947159f..33cf89f84f 100644
--- a/lib/cmdline/cmdline_parse_string.c
+++ b/lib/cmdline/cmdline_parse_string.c
@@ -171,6 +171,9 @@ int cmdline_complete_get_elt_string(cmdline_parse_token_hdr_t *tk, int idx,
if (!tk || !dstbuf || idx < 0)
return -1;
+ if (size == 0)
+ return -1;
+
tk2 = (struct cmdline_token_string *)tk;
sd = &tk2->string_data;
--
2.51.0
^ permalink raw reply related
* [PATCH 4/6] cmdline: add explicit help function for bool type
From: Bruce Richardson @ 2026-05-07 14:59 UTC (permalink / raw)
To: dev; +Cc: Bruce Richardson
In-Reply-To: <20260507145950.197753-1-bruce.richardson@intel.com>
Rather than using the string help output, have a specific boolean help
output that prints out on|off as the options.
Signed-off-by: Bruce Richardson <bruce.richardson@intel.com>
---
lib/cmdline/cmdline_parse_bool.c | 18 +++++++++++++++++-
1 file changed, 17 insertions(+), 1 deletion(-)
diff --git a/lib/cmdline/cmdline_parse_bool.c b/lib/cmdline/cmdline_parse_bool.c
index a3f7adab58..d5d5499531 100644
--- a/lib/cmdline/cmdline_parse_bool.c
+++ b/lib/cmdline/cmdline_parse_bool.c
@@ -13,13 +13,17 @@
#include "cmdline_parse.h"
#include "cmdline_parse_bool.h"
+static int
+cmdline_get_help_bool(cmdline_parse_token_hdr_t *tk, char *dstbuf,
+ unsigned int size);
+
RTE_EXPORT_EXPERIMENTAL_SYMBOL(cmdline_token_bool_ops, 25.03)
struct cmdline_token_ops cmdline_token_bool_ops = {
.parse = cmdline_parse_bool,
.complete_get_nb = NULL,
.complete_get_elt = NULL,
- .get_help = cmdline_get_help_string,
+ .get_help = cmdline_get_help_bool,
};
static cmdline_parse_token_string_t cmd_parse_token_bool = {
@@ -32,6 +36,18 @@ static cmdline_parse_token_string_t cmd_parse_token_bool = {
}
};
+/* get help for bool token */
+static int
+cmdline_get_help_bool(__rte_unused cmdline_parse_token_hdr_t *tk,
+ char *dstbuf, unsigned int size)
+{
+ if (dstbuf == NULL || size == 0)
+ return -1;
+
+ strlcpy(dstbuf, "on|off", size);
+ return 0;
+}
+
/* parse string to bool */
int
cmdline_parse_bool(__rte_unused cmdline_parse_token_hdr_t *tk, const char *srcbuf, void *res,
--
2.51.0
^ permalink raw reply related
* [PATCH 3/6] cmdline: harden parser result buffer handling
From: Bruce Richardson @ 2026-05-07 14:59 UTC (permalink / raw)
To: dev; +Cc: Bruce Richardson, stable, Shani Peretz
In-Reply-To: <20260507145950.197753-1-bruce.richardson@intel.com>
The cmdline parser had a few result-buffer safety gaps.
In boolean token parsing, the parser could write through a NULL output
pointer in parse-only paths (for example completion/match checks). Add
proper output-pointer and output-size checks before storing the parsed
value.
In instruction matching, reject token offsets that are equal to the
result buffer size, not only greater than it, so tokens are never parsed
with a zero-sized output window at the end of the buffer.
In completion formatting, handle truncated strlcpy() output before
appending help text, preventing offset/size misuse when the destination
buffer is small.
Fixes: 985465997b73 ("ethdev: add xstats API to enable/disable counter")
Fixes: af75078fece3 ("first public release")
Cc: stable@dpdk.org
Signed-off-by: Bruce Richardson <bruce.richardson@intel.com>
---
Note: the first fixes line, though strange, is valid. The cmdline
library bool handling was added as part of the ethdev commit.
---
lib/cmdline/cmdline_parse.c | 6 ++++--
lib/cmdline/cmdline_parse_bool.c | 19 ++++++++++++++++---
2 files changed, 20 insertions(+), 5 deletions(-)
diff --git a/lib/cmdline/cmdline_parse.c b/lib/cmdline/cmdline_parse.c
index 201fddb8c3..d55c8db19d 100644
--- a/lib/cmdline/cmdline_parse.c
+++ b/lib/cmdline/cmdline_parse.c
@@ -133,7 +133,7 @@ match_inst(cmdline_parse_inst_t *inst, const char *buf,
} else {
unsigned rb_sz;
- if (token_hdr.offset > resbuf_size) {
+ if (token_hdr.offset >= resbuf_size) {
printf("Parse error(%s:%d): Token offset(%u) "
"exceeds maximum size(%u)\n",
__FILE__, __LINE__,
@@ -519,7 +519,9 @@ cmdline_complete(struct cmdline *cl, const char *buf, int *state,
}
(*state)++;
l=strlcpy(dst, tmpbuf, size);
- if (l>=0 && token_hdr.ops->get_help) {
+ if ((unsigned int)l >= size)
+ return 1;
+ if (token_hdr.ops->get_help) {
token_hdr.ops->get_help(token_p, tmpbuf,
sizeof(tmpbuf));
help_str = inst->help_str;
diff --git a/lib/cmdline/cmdline_parse_bool.c b/lib/cmdline/cmdline_parse_bool.c
index e03cc3d545..a3f7adab58 100644
--- a/lib/cmdline/cmdline_parse_bool.c
+++ b/lib/cmdline/cmdline_parse_bool.c
@@ -35,17 +35,30 @@ static cmdline_parse_token_string_t cmd_parse_token_bool = {
/* parse string to bool */
int
cmdline_parse_bool(__rte_unused cmdline_parse_token_hdr_t *tk, const char *srcbuf, void *res,
- __rte_unused unsigned int ressize)
+ unsigned int ressize)
{
cmdline_fixed_string_t on_off = {0};
+ uint8_t val;
+
+ if (!srcbuf || !*srcbuf)
+ return -1;
+
+ if (res != NULL && ressize < sizeof(uint8_t))
+ return -1;
+
if (cmdline_token_string_ops.parse
(&cmd_parse_token_bool.hdr, srcbuf, on_off, sizeof(on_off)) < 0)
return -1;
if (strcmp((char *)on_off, "on") == 0)
- *(uint8_t *)res = 1;
+ val = 1;
else if (strcmp((char *)on_off, "off") == 0)
- *(uint8_t *)res = 0;
+ val = 0;
+ else
+ return -1;
+
+ if (res != NULL)
+ *(uint8_t *)res = val;
return strlen(on_off);
}
--
2.51.0
^ permalink raw reply related
* [PATCH 2/6] cfgfile: prevent issues with overflow on resize
From: Bruce Richardson @ 2026-05-07 14:59 UTC (permalink / raw)
To: dev; +Cc: Bruce Richardson, stable, Cristian Dumitrescu, Pablo de Lara
In-Reply-To: <20260507145950.197753-1-bruce.richardson@intel.com>
When resizing a cfgfile object to store more sections or entries, the
multiplication in the realloc call could lead to overflow and hence an
incorrect/smaller size being allocated. Prevent this by tightening up
sizing in the library:
- use size_t for sizes rather than int, avoiding negative values
- explicitly limit the number of entries to INT_MAX < SIZE_MAX, ensuring
that all int indexes from the API will work.
- add range checks on allocation before multiplication, to avoid
overflow.
- This means a lower max entry count on 32-bit to avoid 32-bit
allocation overflow.
Fixes: eaafbad419bf ("cfgfile: library to interpret config files")
Cc: stable@dpdk.org
Signed-off-by: Bruce Richardson <bruce.richardson@intel.com>
---
lib/cfgfile/rte_cfgfile.c | 87 ++++++++++++++++++++++-----------------
1 file changed, 49 insertions(+), 38 deletions(-)
diff --git a/lib/cfgfile/rte_cfgfile.c b/lib/cfgfile/rte_cfgfile.c
index 25fc792274..040351ab4d 100644
--- a/lib/cfgfile/rte_cfgfile.c
+++ b/lib/cfgfile/rte_cfgfile.c
@@ -9,6 +9,7 @@
#include <ctype.h>
#include <errno.h>
#include <limits.h>
+#include <stdint.h>
#ifndef LINE_MAX
#define LINE_MAX 2048
@@ -23,15 +24,15 @@
struct rte_cfgfile_section {
char name[CFG_NAME_LEN];
- int num_entries;
- int allocated_entries;
+ size_t num_entries;
+ size_t allocated_entries;
struct rte_cfgfile_entry *entries;
};
struct rte_cfgfile {
int flags;
- int num_sections;
- int allocated_sections;
+ size_t num_sections;
+ size_t allocated_sections;
struct rte_cfgfile_section *sections;
};
@@ -43,12 +44,27 @@ RTE_LOG_REGISTER_DEFAULT(cfgfile_logtype, INFO);
RTE_LOG_LINE_PREFIX(level, CFGFILE, "%s(): ", __func__, __VA_ARGS__)
/* >8 End of setting up dynamic logging */
+/** define a max allocation limit for entry and section types
+ * for 64-bit systems, this is based on INT_MAX since APIs all work on int values.
+ * For 32-bit systems, this is based on SIZE_MAX / sizeof(type) to prevent overflow on allocation.
+ */
+#define CFG_ALLOC_MAX(type) \
+ (sizeof(int) == sizeof(size_t) ? (SIZE_MAX / sizeof(type)) : ((size_t)INT_MAX))
+
/** when we resize a file structure, how many extra entries
* for new sections do we add in */
#define CFG_ALLOC_SECTION_BATCH 8
+/** max number of section entries we can possibly have.
+ * Used to prevent overflow on allocation. Based on INT_MAX since APIs all work on int values
+ */
+#define CFG_ALLOC_SECTION_MAX CFG_ALLOC_MAX(struct rte_cfgfile_section)
/** when we resize a section structure, how many extra entries
* for new entries do we add in */
#define CFG_ALLOC_ENTRY_BATCH 16
+/** max number of data entries we can possibly have.
+ * Used to prevent overflow on allocation. Based on INT_MAX since APIs all work on int values
+ */
+#define CFG_ALLOC_ENTRY_MAX CFG_ALLOC_MAX(struct rte_cfgfile_entry)
/**
* Default cfgfile load parameters.
@@ -99,9 +115,7 @@ _strip(char *str, unsigned len)
static struct rte_cfgfile_section *
_get_section(struct rte_cfgfile *cfg, const char *sectionname)
{
- int i;
-
- for (i = 0; i < cfg->num_sections; i++) {
+ for (size_t i = 0; i < cfg->num_sections; i++) {
if (strncmp(cfg->sections[i].name, sectionname,
sizeof(cfg->sections[0].name)) == 0)
return &cfg->sections[i];
@@ -118,6 +132,9 @@ _add_entry(struct rte_cfgfile_section *section, const char *entryname,
/* resize entry structure if we don't have room for more entries */
if (section->num_entries == section->allocated_entries) {
+ if (section->allocated_entries > CFG_ALLOC_ENTRY_MAX - CFG_ALLOC_ENTRY_BATCH)
+ return -ENOMEM;
+
struct rte_cfgfile_entry *n_entries = realloc(
section->entries,
sizeof(struct rte_cfgfile_entry) *
@@ -305,7 +322,6 @@ RTE_EXPORT_SYMBOL(rte_cfgfile_create)
struct rte_cfgfile *
rte_cfgfile_create(int flags)
{
- int i;
struct rte_cfgfile *cfg;
/* future proof flags usage */
@@ -328,7 +344,7 @@ rte_cfgfile_create(int flags)
cfg->allocated_sections = CFG_ALLOC_SECTION_BATCH;
- for (i = 0; i < CFG_ALLOC_SECTION_BATCH; i++) {
+ for (size_t i = 0; i < CFG_ALLOC_SECTION_BATCH; i++) {
cfg->sections[i].entries = calloc(CFG_ALLOC_ENTRY_BATCH,
sizeof(struct rte_cfgfile_entry));
@@ -345,7 +361,7 @@ rte_cfgfile_create(int flags)
return cfg;
error1:
if (cfg->sections != NULL) {
- for (i = 0; i < cfg->allocated_sections; i++) {
+ for (size_t i = 0; i < cfg->allocated_sections; i++) {
if (cfg->sections[i].entries != NULL) {
free(cfg->sections[i].entries);
cfg->sections[i].entries = NULL;
@@ -362,8 +378,6 @@ RTE_EXPORT_SYMBOL(rte_cfgfile_add_section)
int
rte_cfgfile_add_section(struct rte_cfgfile *cfg, const char *sectionname)
{
- int i;
-
if (cfg == NULL)
return -EINVAL;
@@ -375,6 +389,8 @@ rte_cfgfile_add_section(struct rte_cfgfile *cfg, const char *sectionname)
/* resize overall struct if we don't have room for more sections */
if (cfg->num_sections == cfg->allocated_sections) {
+ if (cfg->allocated_sections > CFG_ALLOC_SECTION_MAX - CFG_ALLOC_SECTION_BATCH)
+ return -ENOMEM;
struct rte_cfgfile_section *n_sections =
realloc(cfg->sections,
@@ -385,7 +401,7 @@ rte_cfgfile_add_section(struct rte_cfgfile *cfg, const char *sectionname)
if (n_sections == NULL)
return -ENOMEM;
- for (i = 0; i < CFG_ALLOC_SECTION_BATCH; i++) {
+ for (size_t i = 0; i < CFG_ALLOC_SECTION_BATCH; i++) {
n_sections[i + cfg->allocated_sections].num_entries = 0;
n_sections[i +
cfg->allocated_sections].allocated_entries = 0;
@@ -428,8 +444,6 @@ RTE_EXPORT_SYMBOL(rte_cfgfile_set_entry)
int rte_cfgfile_set_entry(struct rte_cfgfile *cfg, const char *sectionname,
const char *entryname, const char *entryvalue)
{
- int i;
-
if ((cfg == NULL) || (sectionname == NULL) || (entryname == NULL))
return -EINVAL;
@@ -442,7 +456,7 @@ int rte_cfgfile_set_entry(struct rte_cfgfile *cfg, const char *sectionname,
if (entryvalue == NULL)
entryvalue = "";
- for (i = 0; i < curr_section->num_entries; i++)
+ for (size_t i = 0; i < curr_section->num_entries; i++)
if (!strcmp(curr_section->entries[i].name, entryname)) {
strlcpy(curr_section->entries[i].value, entryvalue,
sizeof(curr_section->entries[i].value));
@@ -456,8 +470,6 @@ int rte_cfgfile_set_entry(struct rte_cfgfile *cfg, const char *sectionname,
RTE_EXPORT_SYMBOL(rte_cfgfile_save)
int rte_cfgfile_save(struct rte_cfgfile *cfg, const char *filename)
{
- int i, j;
-
if ((cfg == NULL) || (filename == NULL))
return -EINVAL;
@@ -466,10 +478,10 @@ int rte_cfgfile_save(struct rte_cfgfile *cfg, const char *filename)
if (f == NULL)
return -EINVAL;
- for (i = 0; i < cfg->num_sections; i++) {
+ for (size_t i = 0; i < cfg->num_sections; i++) {
fprintf(f, "[%s]\n", cfg->sections[i].name);
- for (j = 0; j < cfg->sections[i].num_entries; j++) {
+ for (size_t j = 0; j < cfg->sections[i].num_entries; j++) {
fprintf(f, "%s=%s\n",
cfg->sections[i].entries[j].name,
cfg->sections[i].entries[j].value);
@@ -481,13 +493,11 @@ int rte_cfgfile_save(struct rte_cfgfile *cfg, const char *filename)
RTE_EXPORT_SYMBOL(rte_cfgfile_close)
int rte_cfgfile_close(struct rte_cfgfile *cfg)
{
- int i;
-
if (cfg == NULL)
return -1;
if (cfg->sections != NULL) {
- for (i = 0; i < cfg->allocated_sections; i++) {
+ for (size_t i = 0; i < cfg->allocated_sections; i++) {
if (cfg->sections[i].entries != NULL) {
free(cfg->sections[i].entries);
cfg->sections[i].entries = NULL;
@@ -507,20 +517,20 @@ int
rte_cfgfile_num_sections(struct rte_cfgfile *cfg, const char *sectionname,
size_t length)
{
- int num_sections = 0;
- int i;
+ size_t num_sections = 0;
if (cfg == NULL)
return -1;
if (sectionname == NULL)
- return cfg->num_sections;
+ return (int)cfg->num_sections;
- for (i = 0; i < cfg->num_sections; i++) {
+ for (size_t i = 0; i < cfg->num_sections; i++) {
if (strncmp(cfg->sections[i].name, sectionname, length) == 0)
num_sections++;
}
- return num_sections;
+
+ return (int)num_sections;
}
RTE_EXPORT_SYMBOL(rte_cfgfile_sections)
@@ -533,7 +543,7 @@ rte_cfgfile_sections(struct rte_cfgfile *cfg, char *sections[],
if (cfg == NULL || sections == NULL || max_sections < 0)
return -1;
- for (i = 0; i < cfg->num_sections && i < max_sections; i++) {
+ for (i = 0; (size_t)i < cfg->num_sections && i < max_sections; i++) {
if (sections[i] == NULL)
return -1;
strlcpy(sections[i], cfg->sections[i].name, CFG_NAME_LEN);
@@ -563,7 +573,8 @@ rte_cfgfile_section_num_entries(struct rte_cfgfile *cfg,
const struct rte_cfgfile_section *s = _get_section(cfg, sectionname);
if (s == NULL)
return -1;
- return s->num_entries;
+
+ return (int)s->num_entries;
}
RTE_EXPORT_SYMBOL(rte_cfgfile_section_num_entries_by_index)
@@ -574,13 +585,13 @@ rte_cfgfile_section_num_entries_by_index(struct rte_cfgfile *cfg,
if (cfg == NULL || sectionname == NULL)
return -1;
- if (index < 0 || index >= cfg->num_sections)
+ if (index < 0 || (size_t)index >= cfg->num_sections)
return -1;
const struct rte_cfgfile_section *sect = &(cfg->sections[index]);
strlcpy(sectionname, sect->name, CFG_NAME_LEN);
- return sect->num_entries;
+ return (int)sect->num_entries;
}
RTE_EXPORT_SYMBOL(rte_cfgfile_section_entries)
int
@@ -595,7 +606,7 @@ rte_cfgfile_section_entries(struct rte_cfgfile *cfg, const char *sectionname,
const struct rte_cfgfile_section *sect = _get_section(cfg, sectionname);
if (sect == NULL)
return -1;
- for (i = 0; i < max_entries && i < sect->num_entries; i++)
+ for (i = 0; i < max_entries && (size_t)i < sect->num_entries; i++)
entries[i] = sect->entries[i];
return i;
}
@@ -611,12 +622,14 @@ rte_cfgfile_section_entries_by_index(struct rte_cfgfile *cfg, int index,
if (cfg == NULL || sectionname == NULL || entries == NULL)
return -1;
+ if (max_entries < 0)
+ return -1;
- if (index < 0 || index >= cfg->num_sections)
+ if (index < 0 || (size_t)index >= cfg->num_sections)
return -1;
sect = &cfg->sections[index];
strlcpy(sectionname, sect->name, CFG_NAME_LEN);
- for (i = 0; i < max_entries && i < sect->num_entries; i++)
+ for (i = 0; i < max_entries && (size_t)i < sect->num_entries; i++)
entries[i] = sect->entries[i];
return i;
}
@@ -626,15 +639,13 @@ const char *
rte_cfgfile_get_entry(struct rte_cfgfile *cfg, const char *sectionname,
const char *entryname)
{
- int i;
-
if (cfg == NULL || sectionname == NULL || entryname == NULL)
return NULL;
const struct rte_cfgfile_section *sect = _get_section(cfg, sectionname);
if (sect == NULL)
return NULL;
- for (i = 0; i < sect->num_entries; i++)
+ for (size_t i = 0; i < sect->num_entries; i++)
if (strncmp(sect->entries[i].name, entryname, CFG_NAME_LEN)
== 0)
return sect->entries[i].value;
--
2.51.0
^ permalink raw reply related
* [PATCH 1/6] cfgfile: add null checks to public APIs
From: Bruce Richardson @ 2026-05-07 14:59 UTC (permalink / raw)
To: dev; +Cc: Bruce Richardson, Cristian Dumitrescu
In-Reply-To: <20260507145950.197753-1-bruce.richardson@intel.com>
For safety, add NULL checks to each of the public APIs to avoid crashes
in the library. Even though NULL values are likely symptoms of a wider
problem, this is not a datapath library so there is no harm in taking a
few cycles for additional parameter checking.
Signed-off-by: Bruce Richardson <bruce.richardson@intel.com>
---
lib/cfgfile/rte_cfgfile.c | 35 ++++++++++++++++++++++++++++++++++-
1 file changed, 34 insertions(+), 1 deletion(-)
diff --git a/lib/cfgfile/rte_cfgfile.c b/lib/cfgfile/rte_cfgfile.c
index c495bdf6ae..25fc792274 100644
--- a/lib/cfgfile/rte_cfgfile.c
+++ b/lib/cfgfile/rte_cfgfile.c
@@ -198,6 +198,10 @@ rte_cfgfile_load_with_params(const char *filename, int flags,
return NULL;
cfg = rte_cfgfile_create(flags);
+ if (cfg == NULL) {
+ fclose(f);
+ return NULL;
+ }
while (fgets(buffer, sizeof(buffer), f) != NULL) {
char *pos;
@@ -506,6 +510,9 @@ rte_cfgfile_num_sections(struct rte_cfgfile *cfg, const char *sectionname,
int num_sections = 0;
int i;
+ if (cfg == NULL)
+ return -1;
+
if (sectionname == NULL)
return cfg->num_sections;
@@ -523,8 +530,14 @@ rte_cfgfile_sections(struct rte_cfgfile *cfg, char *sections[],
{
int i;
- for (i = 0; i < cfg->num_sections && i < max_sections; i++)
+ if (cfg == NULL || sections == NULL || max_sections < 0)
+ return -1;
+
+ for (i = 0; i < cfg->num_sections && i < max_sections; i++) {
+ if (sections[i] == NULL)
+ return -1;
strlcpy(sections[i], cfg->sections[i].name, CFG_NAME_LEN);
+ }
return i;
}
@@ -533,6 +546,9 @@ RTE_EXPORT_SYMBOL(rte_cfgfile_has_section)
int
rte_cfgfile_has_section(struct rte_cfgfile *cfg, const char *sectionname)
{
+ if (cfg == NULL || sectionname == NULL)
+ return 0;
+
return _get_section(cfg, sectionname) != NULL;
}
@@ -541,6 +557,9 @@ int
rte_cfgfile_section_num_entries(struct rte_cfgfile *cfg,
const char *sectionname)
{
+ if (cfg == NULL || sectionname == NULL)
+ return -1;
+
const struct rte_cfgfile_section *s = _get_section(cfg, sectionname);
if (s == NULL)
return -1;
@@ -552,6 +571,9 @@ int
rte_cfgfile_section_num_entries_by_index(struct rte_cfgfile *cfg,
char *sectionname, int index)
{
+ if (cfg == NULL || sectionname == NULL)
+ return -1;
+
if (index < 0 || index >= cfg->num_sections)
return -1;
@@ -566,6 +588,10 @@ rte_cfgfile_section_entries(struct rte_cfgfile *cfg, const char *sectionname,
struct rte_cfgfile_entry *entries, int max_entries)
{
int i;
+
+ if (cfg == NULL || sectionname == NULL || entries == NULL)
+ return -1;
+
const struct rte_cfgfile_section *sect = _get_section(cfg, sectionname);
if (sect == NULL)
return -1;
@@ -583,6 +609,9 @@ rte_cfgfile_section_entries_by_index(struct rte_cfgfile *cfg, int index,
int i;
const struct rte_cfgfile_section *sect;
+ if (cfg == NULL || sectionname == NULL || entries == NULL)
+ return -1;
+
if (index < 0 || index >= cfg->num_sections)
return -1;
sect = &cfg->sections[index];
@@ -598,6 +627,10 @@ rte_cfgfile_get_entry(struct rte_cfgfile *cfg, const char *sectionname,
const char *entryname)
{
int i;
+
+ if (cfg == NULL || sectionname == NULL || entryname == NULL)
+ return NULL;
+
const struct rte_cfgfile_section *sect = _get_section(cfg, sectionname);
if (sect == NULL)
return NULL;
--
2.51.0
^ permalink raw reply related
* [PATCH 0/6] add hardening checks to cmdline and cfgfile libs
From: Bruce Richardson @ 2026-05-07 14:59 UTC (permalink / raw)
To: dev; +Cc: Bruce Richardson
Using AI tools to review the cmdline and cfgfile libraries throws up a
couple of places in the libraries where additional hardening could help
prevent future issues. A number of these are purely defensive, e.g.
adding NULL checks to input parameters where a well-behaved app should
never call the function with a NULL value, and so those are not
explicitly marked for backport.
Bruce Richardson (6):
cfgfile: add null checks to public APIs
cfgfile: prevent issues with overflow on resize
cmdline: harden parser result buffer handling
cmdline: add explicit help function for bool type
cmdline: guard zero-size destination buffers
cmdline: add null checks for invalid input
lib/cfgfile/rte_cfgfile.c | 118 ++++++++++++++++++++---------
lib/cmdline/cmdline.c | 3 +
lib/cmdline/cmdline_parse.c | 6 +-
lib/cmdline/cmdline_parse_bool.c | 37 ++++++++-
lib/cmdline/cmdline_parse_num.c | 2 +-
lib/cmdline/cmdline_parse_string.c | 3 +
lib/cmdline/cmdline_rdline.c | 3 +
7 files changed, 128 insertions(+), 44 deletions(-)
--
2.51.0
^ permalink raw reply
* [DPDK/DTS Bug 1943] dts: add missing device capability
From: bugzilla @ 2026-05-07 14:24 UTC (permalink / raw)
To: dev
http://bugs.dpdk.org/show_bug.cgi?id=1943
Bug ID: 1943
Summary: dts: add missing device capability
Product: DPDK
Version: unspecified
Hardware: All
OS: All
Status: UNCONFIRMED
Severity: normal
Priority: Normal
Component: DTS
Assignee: dev@dpdk.org
Reporter: abailey@iol.unh.edu
CC: juraj.linkes@pantheon.tech, probb@iol.unh.edu
Target Milestone: ---
Device capabilities: 0x23( RUNTIME_RX_QUEUE_SETUP RUNTIME_TX_QUEUE_SETUP VMDQ )
There is currently a patch in that adds this VMDQ flag to device capabilities.
This field does not exist in the DeviceCapabilitiesFlag class and will cause
DTS to crash whenever present.
Add VMDQ field to DeviceCapabilitiesFlag class
--
You are receiving this mail because:
You are the assignee for the bug.
^ permalink raw reply
* Re: [PATCH v2 1/2] net/ice: properly handle TM hierarchy deletion
From: Bruce Richardson @ 2026-05-07 13:39 UTC (permalink / raw)
To: Ciara Loftus; +Cc: dev, Mukul Katiyar, stable
In-Reply-To: <afyPdAwI6_ClTYFK@bricha3-mobl1.ger.corp.intel.com>
On Thu, May 07, 2026 at 02:11:16PM +0100, Bruce Richardson wrote:
> On Thu, May 07, 2026 at 12:59:29PM +0000, Ciara Loftus wrote:
> > From: Mukul Katiyar <mukul@versa-networks.com>
> >
> > When a TM hierarchy is fully deleted and then committed, the hardware
> > scheduler nodes may be left with any bandwidth limits that were
> > programmed by the previous hierarchy commit. These stale limits may
> > remain in effect the next time the device starts, permanently throttling
> > traffic even though the TM hierarchy was removed.
> >
> > Fix this by resetting all descendant hardware scheduler nodes to their
> > default state when committing an empty hierarchy. Also restore the port
> > queue count to its hardware default and clear the committed flag so the
> > port starts cleanly without any TM configuration applied.
> >
> > Fixes: 715d449a965b ("net/ice: enhance Tx scheduler hierarchy support")
> > Cc: stable@dpdk.org
> >
> > Signed-off-by: Mukul Katiyar <mukul@versa-networks.com>
> > Signed-off-by: Ciara Loftus <ciara.loftus@intel.com>
> > ---
> > v2:
> > * Ensure restore is only performed if a hierarchy has already been
> > committed
> > ---
> > .mailmap | 1 +
> > drivers/net/intel/ice/ice_tm.c | 24 ++++++++++++++++++++++--
> > 2 files changed, 23 insertions(+), 2 deletions(-)
> Acked-by: Bruce Richardson <bruce.richardson@intel.com>
Series applied to dpdk-next-net-intel.
Thanks,
/Bruce
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox