* [PATCH v17 06/11] drivers: support PCI BAR mapping
From: liujie5 @ 2026-05-19 3:01 UTC (permalink / raw)
To: stephen; +Cc: dev, Jie Liu
In-Reply-To: <20260519030132.3780057-1-liujie5@linkdatatechnology.com>
From: Jie Liu <liujie5@linkdatatechnology.com>
Implement PCI BAR (Base Address Register) mapping and unmapping
logic to enable MMIO (Memory Mapped I/O) access to hardware
registers.
The driver retrieves the BAR0 virtual address from the PCI resource
during the probing phase. This mapping is used for subsequent
register-level operations. Proper cleanup is implemented in the
device close path.
Signed-off-by: Jie Liu <liujie5@linkdatatechnology.com>
---
drivers/common/sxe2/sxe2_ioctl_chnl.c | 34 +++
drivers/net/sxe2/sxe2_ethdev.c | 326 +++++++++++++++++++++++++-
drivers/net/sxe2/sxe2_ethdev.h | 18 ++
3 files changed, 375 insertions(+), 3 deletions(-)
diff --git a/drivers/common/sxe2/sxe2_ioctl_chnl.c b/drivers/common/sxe2/sxe2_ioctl_chnl.c
index edd85b885f..ae22f8e7b7 100644
--- a/drivers/common/sxe2/sxe2_ioctl_chnl.c
+++ b/drivers/common/sxe2/sxe2_ioctl_chnl.c
@@ -159,6 +159,40 @@ sxe2_drv_dev_handshake(struct sxe2_common_device *cdev)
return ret;
}
+RTE_EXPORT_INTERNAL_SYMBOL(sxe2_drv_dev_mmap)
+void
+*sxe2_drv_dev_mmap(struct sxe2_common_device *cdev, uint8_t bar_idx, uint64_t len, uint64_t offset)
+{
+ int32_t cmd_fd = 0;
+ void *virt = NULL;
+
+ if (cdev->config.kernel_reset) {
+ PMD_LOG_WARN(COM, "kernel reset, need restart app.");
+ goto l_err;
+ }
+
+ cmd_fd = SXE2_CDEV_TO_CMD_FD(cdev);
+ if (cmd_fd < 0) {
+ PMD_LOG_ERR(COM, "Failed to exec cmd, fd=%d", cmd_fd);
+ goto l_err;
+ }
+
+ PMD_LOG_DEBUG(COM, "fd=%d, bar idx=%d, len=0x%zx, src=0x%"PRIx64", offset=0x%"PRIx64"",
+ bar_idx, cmd_fd, len, offset, SXE2_COM_PCI_OFFSET_GEN(bar_idx, offset));
+
+ virt = mmap(NULL, len, PROT_READ | PROT_WRITE,
+ MAP_SHARED, cmd_fd, SXE2_COM_PCI_OFFSET_GEN(bar_idx, offset));
+ if (virt == MAP_FAILED) {
+ PMD_LOG_ERR(COM, "Failed mmap, cmd_fd=%d, len=0x%zx, offset=0x%"PRIx64", err:%s",
+ cmd_fd, len, offset, strerror(errno));
+ goto l_err;
+ }
+
+ return virt;
+l_err:
+ return NULL;
+}
+
RTE_EXPORT_INTERNAL_SYMBOL(sxe2_drv_dev_munmap)
int32_t
sxe2_drv_dev_munmap(struct sxe2_common_device *cdev, void *virt, uint64_t len)
diff --git a/drivers/net/sxe2/sxe2_ethdev.c b/drivers/net/sxe2/sxe2_ethdev.c
index f0bdda38a7..204add9c98 100644
--- a/drivers/net/sxe2/sxe2_ethdev.c
+++ b/drivers/net/sxe2/sxe2_ethdev.c
@@ -54,6 +54,27 @@ static const struct rte_pci_id pci_id_sxe2_tbl[] = {
{ .vendor_id = 0, },
};
+static struct sxe2_pci_map_addr_info sxe2_net_map_addr_info_pf[SXE2_PCI_MAP_RES_MAX_COUNT] = {
+ [SXE2_PCI_MAP_RES_INVALID] = {.addr_base = 0,
+ .bar_idx = 0,
+ .reg_width = 0},
+ [SXE2_PCI_MAP_RES_DOORBELL_TX] = {.addr_base = SXE2_TXQ_LEGACY_DBLL(0),
+ .bar_idx = 0,
+ .reg_width = 4},
+ [SXE2_PCI_MAP_RES_DOORBELL_RX_TAIL] = {.addr_base = SXE2_RXQ_TAIL(0),
+ .bar_idx = 0,
+ .reg_width = 4},
+ [SXE2_PCI_MAP_RES_IRQ_DYN] = {.addr_base = SXE2_VF_DYN_CTL(0),
+ .bar_idx = 0,
+ .reg_width = 4},
+ [SXE2_PCI_MAP_RES_IRQ_ITR] = {.addr_base = SXE2_VF_INT_ITR(0, 0),
+ .bar_idx = 0,
+ .reg_width = 4},
+ [SXE2_PCI_MAP_RES_IRQ_MSIX] = {.addr_base = SXE2_BAR4_MSIX_CTL(0),
+ .bar_idx = 4,
+ .reg_width = 10},
+};
+
static int32_t sxe2_dev_configure(struct rte_eth_dev *dev)
{
int32_t ret = 0;
@@ -151,6 +172,7 @@ static int32_t sxe2_dev_close(struct rte_eth_dev *dev)
(void)sxe2_dev_stop(dev);
sxe2_vsi_uninit(dev);
+ sxe2_dev_pci_map_uinit(dev);
return 0;
}
@@ -287,6 +309,31 @@ static const struct eth_dev_ops sxe2_eth_dev_ops = {
.dev_infos_get = sxe2_dev_infos_get,
};
+struct sxe2_pci_map_bar_info *sxe2_dev_get_bar_info(struct sxe2_adapter *adapter,
+ enum sxe2_pci_map_resource res_type)
+{
+ struct sxe2_pci_map_context *map_ctxt = &adapter->map_ctxt;
+ struct sxe2_pci_map_bar_info *bar_info = NULL;
+ uint8_t bar_idx = SXE2_PCI_MAP_BAR_INVALID;
+ uint8_t i;
+
+ bar_idx = map_ctxt->addr_info[res_type].bar_idx;
+ if (bar_idx == SXE2_PCI_MAP_BAR_INVALID) {
+ PMD_DEV_LOG_ERR(adapter, INIT, "Invalid bar index with resource type %d", res_type);
+ goto l_end;
+ }
+
+ for (i = 0; i < map_ctxt->bar_cnt; i++) {
+ if (bar_idx == map_ctxt->bar_info[i].bar_idx) {
+ bar_info = &map_ctxt->bar_info[i];
+ break;
+ }
+ }
+
+l_end:
+ return bar_info;
+}
+
static void sxe2_drv_dev_caps_set(struct sxe2_adapter *adapter,
struct sxe2_drv_dev_caps_resp *dev_caps)
{
@@ -354,6 +401,69 @@ static int32_t sxe2_dev_caps_get(struct sxe2_adapter *adapter)
return ret;
}
+int32_t sxe2_dev_pci_seg_map(struct sxe2_adapter *adapter,
+ enum sxe2_pci_map_resource res_type, uint64_t org_len, uint64_t org_offset)
+{
+ struct sxe2_pci_map_bar_info *bar_info = NULL;
+ struct sxe2_pci_map_segment_info *seg_info = NULL;
+ void *map_addr = NULL;
+ int32_t ret = 0;
+ size_t page_size = 0;
+ size_t aligned_len = 0;
+ size_t page_inner_offset = 0;
+ off_t aligned_offset = 0;
+ uint8_t i = 0;
+
+ if (org_len == 0) {
+ PMD_DEV_LOG_ERR(adapter, INIT, "Invalid length, ori_len = 0");
+ ret = -EFAULT;
+ goto l_end;
+ }
+
+ bar_info = sxe2_dev_get_bar_info(adapter, res_type);
+ if (!bar_info) {
+ PMD_LOG_ERR(INIT, "Failed to get bar info, res_type=[%d]", res_type);
+ ret = -EFAULT;
+ goto l_end;
+ }
+ seg_info = bar_info->seg_info;
+
+ page_size = rte_mem_page_size();
+
+ aligned_offset = RTE_ALIGN_FLOOR(org_offset, page_size);
+ page_inner_offset = org_offset - aligned_offset;
+ aligned_len = RTE_ALIGN(page_inner_offset + org_len, page_size);
+
+ map_addr = sxe2_drv_dev_mmap(adapter->cdev, bar_info->bar_idx,
+ aligned_len, aligned_offset);
+ if (!map_addr) {
+ PMD_LOG_ERR(INIT, "Failed to mmap BAR space, type=%d, len=%" PRIu64
+ ", offset=%" PRIu64 ", page_size=%zu",
+ res_type, org_len, org_offset, page_size);
+ ret = -EFAULT;
+ goto l_end;
+ }
+
+ for (i = 0; i < bar_info->map_cnt; i++) {
+ if (seg_info[i].type != SXE2_PCI_MAP_RES_INVALID)
+ continue;
+ seg_info[i].type = res_type;
+ seg_info[i].addr = map_addr;
+ seg_info[i].page_inner_offset = page_inner_offset;
+ seg_info[i].len = aligned_len;
+ break;
+ }
+ if (i == bar_info->map_cnt) {
+ PMD_LOG_ERR(INIT, "No memory to save resource, res_type=%d", res_type);
+ ret = -ENOMEM;
+ sxe2_drv_dev_munmap(adapter->cdev, map_addr, aligned_len);
+ goto l_end;
+ }
+
+l_end:
+ return ret;
+}
+
static int32_t sxe2_hw_init(struct rte_eth_dev *dev)
{
struct sxe2_adapter *adapter = SXE2_DEV_PRIVATE_TO_ADAPTER(dev);
@@ -368,6 +478,55 @@ static int32_t sxe2_hw_init(struct rte_eth_dev *dev)
return ret;
}
+int32_t sxe2_dev_pci_res_seg_map(struct sxe2_adapter *adapter, uint32_t res_type,
+ uint32_t item_cnt, uint32_t item_base)
+{
+ struct sxe2_pci_map_addr_info *addr_info = NULL;
+ int32_t ret = 0;
+
+ addr_info = &adapter->map_ctxt.addr_info[res_type];
+ if (!addr_info || addr_info->bar_idx == SXE2_PCI_MAP_BAR_INVALID) {
+ PMD_DEV_LOG_ERR(adapter, INIT, "Invalid bar index with resource type %d", res_type);
+ ret = -EFAULT;
+ goto l_end;
+ }
+
+ ret = sxe2_dev_pci_seg_map(adapter, res_type, item_cnt * addr_info->reg_width,
+ addr_info->addr_base + item_base * addr_info->reg_width);
+ if (ret != 0) {
+ PMD_DEV_LOG_ERR(adapter, INIT, "Failed to map resource, res_type=%d", res_type);
+ goto l_end;
+ }
+l_end:
+ return ret;
+}
+
+void sxe2_dev_pci_seg_unmap(struct sxe2_adapter *adapter, uint32_t res_type)
+{
+ struct sxe2_pci_map_bar_info *bar_info = NULL;
+ struct sxe2_pci_map_segment_info *seg_info = NULL;
+ uint32_t i = 0;
+
+ bar_info = sxe2_dev_get_bar_info(adapter, res_type);
+ if (bar_info == NULL) {
+ PMD_DEV_LOG_WARN(adapter, INIT, "Failed to get bar info, res_type=[%d]", res_type);
+ goto l_end;
+ }
+ seg_info = bar_info->seg_info;
+
+ for (i = 0; i < bar_info->map_cnt; i++) {
+ if (res_type == seg_info[i].type) {
+ (void)sxe2_drv_dev_munmap(adapter->cdev, seg_info[i].addr,
+ seg_info[i].len);
+ memset(&seg_info[i], 0, sizeof(struct sxe2_pci_map_segment_info));
+ break;
+ }
+ }
+
+l_end:
+ return;
+}
+
static int32_t sxe2_dev_info_init(struct rte_eth_dev *dev)
{
struct sxe2_adapter *adapter =
@@ -408,6 +567,157 @@ static int32_t sxe2_dev_info_init(struct rte_eth_dev *dev)
return ret;
}
+int32_t sxe2_dev_pci_map_init(struct rte_eth_dev *dev)
+{
+ struct sxe2_adapter *adapter = SXE2_DEV_PRIVATE_TO_ADAPTER(dev);
+ struct rte_pci_device *pci_dev = RTE_DEV_TO_PCI(dev->device);
+ struct sxe2_pci_map_context *map_ctxt = &adapter->map_ctxt;
+ struct sxe2_pci_map_bar_info *bar_info = NULL;
+ struct sxe2_pci_map_segment_info *seg_info = NULL;
+ uint16_t txq_cnt = adapter->q_ctxt.qp_cnt_assign;
+ uint16_t txq_base = adapter->q_ctxt.base_idx_in_pf;
+ uint16_t rxq_cnt = adapter->q_ctxt.qp_cnt_assign;
+ uint16_t irq_cnt = adapter->irq_ctxt.max_cnt_hw;
+ uint16_t irq_base = adapter->irq_ctxt.base_idx_in_func;
+ uint16_t rxq_base = adapter->q_ctxt.base_idx_in_pf;
+ int32_t ret = 0;
+
+ PMD_INIT_FUNC_TRACE();
+
+ adapter->dev_info.dev_data = dev->data;
+
+ if (!pci_dev->mem_resource[0].phys_addr) {
+ PMD_LOG_ERR(INIT, "Physical address not scanned");
+ ret = -ENXIO;
+ goto l_end;
+ }
+
+ map_ctxt->bar_cnt = 2;
+
+ bar_info = rte_zmalloc(NULL, sizeof(*bar_info) * map_ctxt->bar_cnt, 0);
+ if (!bar_info) {
+ PMD_LOG_ERR(INIT, "Failed to alloc bar_info");
+ ret = -ENOMEM;
+ goto l_end;
+ }
+ bar_info[0].bar_idx = 0;
+ bar_info[0].map_cnt = SXE2_PCI_MAP_RES_MAX_COUNT;
+ seg_info = rte_zmalloc(NULL, sizeof(*seg_info) * bar_info[0].map_cnt, 0);
+ if (!seg_info) {
+ PMD_LOG_ERR(INIT, "Failed to alloc seg_info");
+ ret = -ENOMEM;
+ goto l_free_bar;
+ }
+
+ bar_info[0].seg_info = seg_info;
+
+ bar_info[1].bar_idx = 4;
+ bar_info[1].map_cnt = SXE2_PCI_MAP_RES_MAX_COUNT;
+ seg_info = rte_zmalloc(NULL, sizeof(*seg_info) * bar_info[1].map_cnt, 0);
+ if (!seg_info) {
+ PMD_LOG_ERR(INIT, "Failed to alloc seg_info");
+ ret = -ENOMEM;
+ goto l_free_seg0;
+ }
+
+ bar_info[1].seg_info = seg_info;
+ map_ctxt->bar_info = bar_info;
+
+ map_ctxt->addr_info = sxe2_net_map_addr_info_pf;
+
+ ret = sxe2_dev_pci_res_seg_map(adapter, SXE2_PCI_MAP_RES_DOORBELL_TX,
+ txq_cnt, txq_base);
+ if (ret) {
+ PMD_LOG_ERR(INIT, "Failed to map txq doorbell addr, ret=%d", ret);
+ goto l_free_seg1;
+ }
+
+ ret = sxe2_dev_pci_res_seg_map(adapter, SXE2_PCI_MAP_RES_DOORBELL_RX_TAIL,
+ rxq_cnt, rxq_base);
+ if (ret) {
+ PMD_LOG_ERR(INIT, "Failed to map rxq tail doorbell addr, ret=%d", ret);
+ goto l_free_txq;
+ }
+
+ ret = sxe2_dev_pci_res_seg_map(adapter, SXE2_PCI_MAP_RES_IRQ_DYN,
+ irq_cnt, irq_base);
+ if (ret) {
+ PMD_LOG_ERR(INIT, "Failed to map irq dyn addr, ret=%d", ret);
+ goto l_free_rxq_tail;
+ }
+
+ ret = sxe2_dev_pci_res_seg_map(adapter, SXE2_PCI_MAP_RES_IRQ_ITR,
+ irq_cnt, irq_base);
+ if (ret) {
+ PMD_LOG_ERR(INIT, "Failed to map irq itr addr, ret=%d", ret);
+ goto l_free_irq_dyn;
+ }
+
+ ret = sxe2_dev_pci_res_seg_map(adapter, SXE2_PCI_MAP_RES_IRQ_MSIX,
+ irq_cnt, irq_base);
+ if (ret) {
+ PMD_LOG_ERR(INIT, "Failed to map irq msix addr, ret=%d", ret);
+ goto l_free_irq_itr;
+ }
+ goto l_end;
+
+l_free_irq_itr:
+ (void)sxe2_dev_pci_seg_unmap(adapter, SXE2_PCI_MAP_RES_IRQ_ITR);
+l_free_irq_dyn:
+ (void)sxe2_dev_pci_seg_unmap(adapter, SXE2_PCI_MAP_RES_IRQ_DYN);
+l_free_rxq_tail:
+ (void)sxe2_dev_pci_seg_unmap(adapter, SXE2_PCI_MAP_RES_DOORBELL_RX_TAIL);
+l_free_txq:
+ (void)sxe2_dev_pci_seg_unmap(adapter, SXE2_PCI_MAP_RES_DOORBELL_TX);
+l_free_seg1:
+ if (bar_info[1].seg_info) {
+ rte_free(bar_info[1].seg_info);
+ bar_info[1].seg_info = NULL;
+ }
+l_free_seg0:
+ if (bar_info[0].seg_info) {
+ rte_free(bar_info[0].seg_info);
+ bar_info[0].seg_info = NULL;
+ }
+l_free_bar:
+ if (bar_info) {
+ rte_free(bar_info);
+ bar_info = NULL;
+ }
+l_end:
+ return ret;
+}
+
+void sxe2_dev_pci_map_uinit(struct rte_eth_dev *dev)
+{
+ struct sxe2_adapter *adapter = SXE2_DEV_PRIVATE_TO_ADAPTER(dev);
+ struct sxe2_pci_map_context *map_ctxt = &adapter->map_ctxt;
+ struct sxe2_pci_map_bar_info *bar_info = NULL;
+ uint8_t i = 0;
+
+ PMD_INIT_FUNC_TRACE();
+
+ (void)sxe2_dev_pci_seg_unmap(adapter, SXE2_PCI_MAP_RES_DOORBELL_RX_TAIL);
+ (void)sxe2_dev_pci_seg_unmap(adapter, SXE2_PCI_MAP_RES_DOORBELL_TX);
+ (void)sxe2_dev_pci_seg_unmap(adapter, SXE2_PCI_MAP_RES_IRQ_DYN);
+ (void)sxe2_dev_pci_seg_unmap(adapter, SXE2_PCI_MAP_RES_IRQ_ITR);
+ (void)sxe2_dev_pci_seg_unmap(adapter, SXE2_PCI_MAP_RES_IRQ_MSIX);
+
+ if (map_ctxt != NULL && map_ctxt->bar_info != NULL) {
+ for (i = 0; i < map_ctxt->bar_cnt; i++) {
+ bar_info = &map_ctxt->bar_info[i];
+ if (bar_info != NULL && bar_info->seg_info != NULL) {
+ rte_free(bar_info->seg_info);
+ bar_info->seg_info = NULL;
+ }
+ }
+ rte_free(map_ctxt->bar_info);
+ map_ctxt->bar_info = NULL;
+ }
+
+ adapter->dev_info.dev_data = NULL;
+}
+
static int32_t sxe2_dev_init(struct rte_eth_dev *dev,
struct sxe2_dev_kvargs_info *kvargs __rte_unused)
{
@@ -426,6 +736,12 @@ static int32_t sxe2_dev_init(struct rte_eth_dev *dev,
goto l_end;
}
+ ret = sxe2_dev_pci_map_init(dev);
+ if (ret) {
+ PMD_LOG_ERR(INIT, "Failed to pci addr map, ret=[%d]", ret);
+ goto l_end;
+ }
+
ret = sxe2_vsi_init(dev);
if (ret) {
PMD_LOG_ERR(INIT, "create main vsi failed, ret=%d", ret);
@@ -548,8 +864,10 @@ static int32_t sxe2_parse_eth_devargs(struct rte_device *dev,
memset(eth_da, 0, sizeof(*eth_da));
if (dev->devargs->cls_str) {
- ret = rte_eth_devargs_parse(dev->devargs->cls_str, eth_da, 1);
- if (ret != 0) {
+ ret = rte_eth_devargs_parse(dev->devargs->cls_str,
+ eth_da,
+ 1);
+ if (ret) {
PMD_LOG_ERR(INIT, "Failed to parse device arguments: %s",
dev->devargs->cls_str);
return -rte_errno;
@@ -557,7 +875,9 @@ static int32_t sxe2_parse_eth_devargs(struct rte_device *dev,
}
if (eth_da->type == RTE_ETH_REPRESENTOR_NONE && dev->devargs->args) {
- ret = rte_eth_devargs_parse(dev->devargs->args, eth_da, 1);
+ ret = rte_eth_devargs_parse(dev->devargs->args,
+ eth_da,
+ 1);
if (ret) {
PMD_LOG_ERR(INIT, "Failed to parse device arguments: %s",
dev->devargs->args);
diff --git a/drivers/net/sxe2/sxe2_ethdev.h b/drivers/net/sxe2/sxe2_ethdev.h
index c4634685e6..843e652616 100644
--- a/drivers/net/sxe2/sxe2_ethdev.h
+++ b/drivers/net/sxe2/sxe2_ethdev.h
@@ -290,4 +290,22 @@ struct sxe2_adapter {
#define SXE2_DEV_PRIVATE_TO_ADAPTER(dev) \
((struct sxe2_adapter *)(dev)->data->dev_private)
+#define SXE2_DEV_TO_PCI(eth_dev) \
+ RTE_DEV_TO_PCI((eth_dev)->device)
+
+struct sxe2_pci_map_bar_info *sxe2_dev_get_bar_info(struct sxe2_adapter *adapter,
+ enum sxe2_pci_map_resource res_type);
+
+int32_t sxe2_dev_pci_seg_map(struct sxe2_adapter *adapter,
+ enum sxe2_pci_map_resource res_type, uint64_t org_len, uint64_t org_offset);
+
+int32_t sxe2_dev_pci_res_seg_map(struct sxe2_adapter *adapter, uint32_t res_type,
+ uint32_t item_cnt, uint32_t item_base);
+
+void sxe2_dev_pci_seg_unmap(struct sxe2_adapter *adapter, uint32_t res_type);
+
+int32_t sxe2_dev_pci_map_init(struct rte_eth_dev *dev);
+
+void sxe2_dev_pci_map_uinit(struct rte_eth_dev *dev);
+
#endif /* __SXE2_ETHDEV_H__ */
--
2.47.3
^ permalink raw reply related
* [PATCH v17 03/11] common/sxe2: add sxe2 basic structures
From: liujie5 @ 2026-05-19 3:01 UTC (permalink / raw)
To: stephen; +Cc: dev, Jie Liu
In-Reply-To: <20260519030132.3780057-1-liujie5@linkdatatechnology.com>
From: Jie Liu <liujie5@linkdatatechnology.com>
This patch adds the base infrastructure for the sxe2 common
library. It includes the mandatory OS abstraction layer (OSAL),
common structure definitions, error codes, and the logging
system implementation.
Specifically, this commit:
- Implements the logging stream management using RTE_LOG_LINE.
- Defines device-specific error codes and status registers.
- Adds the initial meson build configuration for the common library.
Signed-off-by: Jie Liu <liujie5@linkdatatechnology.com>
---
drivers/common/sxe2/sxe2_common_log.h | 81 +++
drivers/common/sxe2/sxe2_host_regs.h | 707 ++++++++++++++++++++++++
drivers/common/sxe2/sxe2_internal_ver.h | 33 ++
drivers/common/sxe2/sxe2_osal.h | 152 +++++
4 files changed, 973 insertions(+)
create mode 100644 drivers/common/sxe2/sxe2_common_log.h
create mode 100644 drivers/common/sxe2/sxe2_host_regs.h
create mode 100644 drivers/common/sxe2/sxe2_internal_ver.h
create mode 100644 drivers/common/sxe2/sxe2_osal.h
diff --git a/drivers/common/sxe2/sxe2_common_log.h b/drivers/common/sxe2/sxe2_common_log.h
new file mode 100644
index 0000000000..e7a9a5f8d0
--- /dev/null
+++ b/drivers/common/sxe2/sxe2_common_log.h
@@ -0,0 +1,81 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (C), 2025, Wuxi Stars Micro System Technologies Co., Ltd.
+ */
+
+#ifndef __SXE2_COMMON_LOG_H__
+#define __SXE2_COMMON_LOG_H__
+
+extern int32_t sxe2_common_log;
+extern int32_t sxe2_log_init;
+extern int32_t sxe2_log_driver;
+extern int32_t sxe2_log_rx;
+extern int32_t sxe2_log_tx;
+extern int32_t sxe2_log_hw;
+
+#define RTE_LOGTYPE_SXE2_COM sxe2_common_log
+#define RTE_LOGTYPE_SXE2_INIT sxe2_log_init
+#define RTE_LOGTYPE_SXE2_DRV sxe2_log_driver
+#define RTE_LOGTYPE_SXE2_RX sxe2_log_rx
+#define RTE_LOGTYPE_SXE2_TX sxe2_log_tx
+#define RTE_LOGTYPE_SXE2_HW sxe2_log_hw
+
+#define SXE2_PMD_LOG(level, log_type, ...) \
+ RTE_LOG_LINE_PREFIX(level, log_type, "%s(): ", \
+ __func__, __VA_ARGS__)
+
+#define SXE2_PMD_DRV_LOG(level, log_type, adapter, ...) \
+ RTE_LOG_LINE_PREFIX(level, log_type, "%s(): port:%u ", \
+ __func__ RTE_LOG_COMMA \
+ adapter->dev_port_id, __VA_ARGS__)
+
+#define PMD_LOG_DEBUG(logtype, fmt, ...) \
+ SXE2_PMD_LOG(DEBUG, SXE2_##logtype, fmt, ##__VA_ARGS__)
+
+#define PMD_LOG_INFO(logtype, fmt, ...) \
+ SXE2_PMD_LOG(INFO, SXE2_##logtype, fmt, ##__VA_ARGS__)
+
+#define PMD_LOG_NOTICE(logtype, fmt, ...) \
+ SXE2_PMD_LOG(NOTICE, SXE2_##logtype, fmt, ##__VA_ARGS__)
+
+#define PMD_LOG_WARN(logtype, fmt, ...) \
+ SXE2_PMD_LOG(WARNING, SXE2_##logtype, fmt, ##__VA_ARGS__)
+
+#define PMD_LOG_ERR(logtype, fmt, ...) \
+ SXE2_PMD_LOG(ERR, SXE2_##logtype, fmt, ##__VA_ARGS__)
+
+#define PMD_LOG_CRIT(logtype, fmt, ...) \
+ SXE2_PMD_LOG(CRIT, SXE2_##logtype, fmt, ##__VA_ARGS__)
+
+#define PMD_LOG_ALERT(logtype, fmt, ...) \
+ SXE2_PMD_LOG(ALERT, SXE2_##logtype, fmt, ##__VA_ARGS__)
+
+#define PMD_LOG_EMERG(logtype, fmt, ...) \
+ SXE2_PMD_LOG(EMERG, SXE2_##logtype, fmt, ##__VA_ARGS__)
+
+#define PMD_DEV_LOG_DEBUG(adapter, logtype, fmt, ...) \
+ SXE2_PMD_DRV_LOG(DEBUG, SXE2_##logtype, adapter, fmt, ##__VA_ARGS__)
+
+#define PMD_DEV_LOG_INFO(adapter, logtype, fmt, ...) \
+ SXE2_PMD_DRV_LOG(INFO, SXE2_##logtype, adapter, fmt, ##__VA_ARGS__)
+
+#define PMD_DEV_LOG_NOTICE(adapter, logtype, fmt, ...) \
+ SXE2_PMD_DRV_LOG(NOTICE, SXE2_##logtype, adapter, fmt, ##__VA_ARGS__)
+
+#define PMD_DEV_LOG_WARN(adapter, logtype, fmt, ...) \
+ SXE2_PMD_DRV_LOG(WARNING, SXE2_##logtype, adapter, fmt, ##__VA_ARGS__)
+
+#define PMD_DEV_LOG_ERR(adapter, logtype, fmt, ...) \
+ SXE2_PMD_DRV_LOG(ERR, SXE2_##logtype, adapter, fmt, ##__VA_ARGS__)
+
+#define PMD_DEV_LOG_CRIT(adapter, logtype, fmt, ...) \
+ SXE2_PMD_DRV_LOG(CRIT, SXE2_##logtype, adapter, fmt, ##__VA_ARGS__)
+
+#define PMD_DEV_LOG_ALERT(adapter, logtype, fmt, ...) \
+ SXE2_PMD_DRV_LOG(ALERT, SXE2_##logtype, adapter, fmt, ##__VA_ARGS__)
+
+#define PMD_DEV_LOG_EMERG(adapter, logtype, fmt, ...) \
+ SXE2_PMD_DRV_LOG(EMERG, SXE2_##logtype, adapter, fmt, ##__VA_ARGS__)
+
+#define PMD_INIT_FUNC_TRACE() PMD_LOG_DEBUG(INIT, " >>")
+
+#endif /* __SXE2_COMMON_LOG_H__ */
diff --git a/drivers/common/sxe2/sxe2_host_regs.h b/drivers/common/sxe2/sxe2_host_regs.h
new file mode 100644
index 0000000000..984ea6214c
--- /dev/null
+++ b/drivers/common/sxe2/sxe2_host_regs.h
@@ -0,0 +1,707 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (C), 2025, Wuxi Stars Micro System Technologies Co., Ltd.
+ */
+
+#ifndef __SXE2_HOST_REGS_H__
+#define __SXE2_HOST_REGS_H__
+
+#define SXE2_BITS_MASK(m, s) ((m ## UL) << (s))
+
+#define SXE2_RXQ_CTXT(_i, _QRX) (0x0050000 + ((_i) * 4 + (_QRX) * 0x20))
+#define SXE2_RXQ_HEAD(_QRX) (0x0060000 + ((_QRX) * 4))
+#define SXE2_RXQ_TAIL(_QRX) (0x0070000 + ((_QRX) * 4))
+#define SXE2_RXQ_CTRL(_QRX) (0x006d000 + ((_QRX) * 4))
+#define SXE2_RXQ_WB(_QRX) (0x006B000 + ((_QRX) * 4))
+
+#define SXE2_RXQ_CTRL_STATUS_ACTIVE 0x00000004
+#define SXE2_RXQ_CTRL_ENABLED 0x00000001
+#define SXE2_RXQ_CTRL_CDE_ENABLE BIT(3)
+
+#define SXE2_PCIEPROC_BASE 0x002d6000
+
+#define SXE2_PF_INT_BASE 0x00260000
+#define SXE2_PF_INT_ALLOC (SXE2_PF_INT_BASE + 0x0000)
+#define SXE2_PF_INT_ALLOC_FIRST 0x7FF
+#define SXE2_PF_INT_ALLOC_LAST_S 12
+#define SXE2_PF_INT_ALLOC_LAST \
+ (0x7FF << SXE2_PF_INT_ALLOC_LAST_S)
+#define SXE2_PF_INT_ALLOC_VALID BIT(31)
+
+#define SXE2_PF_INT_OICR (SXE2_PF_INT_BASE + 0x0040)
+#define SXE2_PF_INT_OICR_PCIE_TIMEOUT BIT(0)
+#define SXE2_PF_INT_OICR_UR BIT(1)
+#define SXE2_PF_INT_OICR_CA BIT(2)
+#define SXE2_PF_INT_OICR_VFLR BIT(3)
+#define SXE2_PF_INT_OICR_VFR_DONE BIT(4)
+#define SXE2_PF_INT_OICR_LAN_TX_ERR BIT(5)
+#define SXE2_PF_INT_OICR_BFDE BIT(6)
+#define SXE2_PF_INT_OICR_LAN_RX_ERR BIT(7)
+#define SXE2_PF_INT_OICR_ECC_ERR BIT(8)
+#define SXE2_PF_INT_OICR_GPIO BIT(9)
+#define SXE2_PF_INT_OICR_TSYN_TX BIT(11)
+#define SXE2_PF_INT_OICR_TSYN_EVENT BIT(12)
+#define SXE2_PF_INT_OICR_TSYN_TGT BIT(13)
+#define SXE2_PF_INT_OICR_EXHAUST BIT(14)
+#define SXE2_PF_INT_OICR_FW BIT(15)
+#define SXE2_PF_INT_OICR_SWINT BIT(16)
+#define SXE2_PF_INT_OICR_LINKSEC_CHG BIT(17)
+#define SXE2_PF_INT_OICR_INT_CFG_ADDR_ERR BIT(18)
+#define SXE2_PF_INT_OICR_INT_CFG_DATA_ERR BIT(19)
+#define SXE2_PF_INT_OICR_INT_CFG_ADR_UNRANGE BIT(20)
+#define SXE2_PF_INT_OICR_INT_RAM_CONFLICT BIT(21)
+#define SXE2_PF_INT_OICR_GRST BIT(22)
+#define SXE2_PF_INT_OICR_FWQ_INT BIT(29)
+#define SXE2_PF_INT_OICR_FWQ_TOOL_INT BIT(30)
+#define SXE2_PF_INT_OICR_MBXQ_INT BIT(31)
+
+#define SXE2_PF_INT_OICR_ENABLE (SXE2_PF_INT_BASE + 0x0020)
+
+#define SXE2_PF_INT_FW_EVENT (SXE2_PF_INT_BASE + 0x0100)
+#define SXE2_PF_INT_FW_ABNORMAL BIT(0)
+#define SXE2_PF_INT_RDMA_AEQ_OVERFLOW BIT(1)
+#define SXE2_PF_INT_CGMAC_LINK_CHG BIT(18)
+#define SXE2_PF_INT_VFLR_DONE BIT(2)
+
+#define SXE2_PF_INT_OICR_CTL (SXE2_PF_INT_BASE + 0x0060)
+#define SXE2_PF_INT_OICR_CTL_MSIX_IDX 0x7FF
+#define SXE2_PF_INT_OICR_CTL_ITR_IDX_S 11
+#define SXE2_PF_INT_OICR_CTL_ITR_IDX \
+ (0x3 << SXE2_PF_INT_OICR_CTL_ITR_IDX_S)
+#define SXE2_PF_INT_OICR_CTL_CAUSE_ENABLE BIT(30)
+
+#define SXE2_PF_INT_FWQ_CTL (SXE2_PF_INT_BASE + 0x00C0)
+#define SXE2_PF_INT_FWQ_CTL_MSIX_IDX 0x7FFF
+#define SXE2_PF_INT_FWQ_CTL_ITR_IDX_S 11
+#define SXE2_PF_INT_FWQ_CTL_ITR_IDX \
+ (0x3 << SXE2_PF_INT_FWQ_CTL_ITR_IDX_S)
+#define SXE2_PF_INT_FWQ_CTL_CAUSE_ENABLE BIT(30)
+
+#define SXE2_PF_INT_MBX_CTL (SXE2_PF_INT_BASE + 0x00A0)
+#define SXE2_PF_INT_MBX_CTL_MSIX_IDX 0x7FF
+#define SXE2_PF_INT_MBX_CTL_ITR_IDX_S 11
+#define SXE2_PF_INT_MBX_CTL_ITR_IDX (0x3 << SXE2_PF_INT_MBX_CTL_ITR_IDX_S)
+#define SXE2_PF_INT_MBX_CTL_CAUSE_ENABLE BIT(30)
+
+#define SXE2_PF_INT_GPIO_ENA (SXE2_PF_INT_BASE + 0x0100)
+#define SXE2_PF_INT_GPIO_X_ENA(x) BIT(x)
+
+#define SXE2_PFG_INT_CTL (SXE2_PF_INT_BASE + 0x0120)
+#define SXE2_PFG_INT_CTL_ITR_GRAN 0x7
+#define SXE2_PFG_INT_CTL_ITR_GRAN_0 (2)
+#define SXE2_PFG_INT_CTL_CREDIT_GRAN BIT(4)
+#define SXE2_PFG_INT_CTL_CREDIT_GRAN_0 (4)
+#define SXE2_PFG_INT_CTL_CREDIT_GRAN_1 (8)
+
+#define SXE2_VFG_RAM_INIT_DONE \
+ (SXE2_PF_INT_BASE + 0x0128)
+#define SXE2_VFG_RAM_INIT_DONE_0 BIT(0)
+#define SXE2_VFG_RAM_INIT_DONE_1 BIT(1)
+#define SXE2_VFG_RAM_INIT_DONE_2 BIT(2)
+
+#define SXE2_LINK_REG_GET_10G_VALUE 4
+#define SXE2_LINK_REG_GET_25G_VALUE 1
+#define SXE2_LINK_REG_GET_50G_VALUE 2
+#define SXE2_LINK_REG_GET_100G_VALUE 3
+
+#define SXE2_PORT0_CNT 0
+#define SXE2_PORT1_CNT 1
+#define SXE2_PORT2_CNT 2
+#define SXE2_PORT3_CNT 3
+
+#define SXE2_LINK_STATUS_BASE (0x002ac200)
+#define SXE2_LINK_STATUS_PORT0_POS 3
+#define SXE2_LINK_STATUS_PORT1_POS 11
+#define SXE2_LINK_STATUS_PORT2_POS 19
+#define SXE2_LINK_STATUS_PORT3_POS 27
+#define SXE2_LINK_STATUS_MASK 1
+
+#define SXE2_LINK_SPEED_BASE (0x002ac200)
+#define SXE2_LINK_SPEED_PORT0_POS 0
+#define SXE2_LINK_SPEED_PORT1_POS 8
+#define SXE2_LINK_SPEED_PORT2_POS 16
+#define SXE2_LINK_SPEED_PORT3_POS 24
+#define SXE2_LINK_SPEED_MASK 7
+
+#define SXE2_PFVP_INT_ALLOC(vf_idx) (SXE2_PF_INT_BASE + 0x012C + ((vf_idx) * 4))
+#define SXE2_PFVP_INT_ALLOC_FIRST_S 0
+
+#define SXE2_PFVP_INT_ALLOC_FIRST_M (0x7FF << SXE2_PFVP_INT_ALLOC_FIRST_S)
+#define SXE2_PFVP_INT_ALLOC_LAST_S 12
+#define SXE2_PFVP_INT_ALLOC_LAST_M \
+ (0x7FF << SXE2_PFVP_INT_ALLOC_LAST_S)
+#define SXE2_PFVP_INT_ALLOC_VALID BIT(31)
+
+#define SXE2_PCI_PFVP_INT_ALLOC(vf_idx) (SXE2_PCIEPROC_BASE + 0x5800 + ((vf_idx) * 4))
+#define SXE2_PCI_PFVP_INT_ALLOC_FIRST_S 0
+
+#define SXE2_PCI_PFVP_INT_ALLOC_FIRST_M (0x7FF << SXE2_PCI_PFVP_INT_ALLOC_FIRST_S)
+#define SXE2_PCI_PFVP_INT_ALLOC_LAST_S 12
+
+#define SXE2_PCI_PFVP_INT_ALLOC_LAST_M \
+ (0x7FF << SXE2_PCI_PFVP_INT_ALLOC_LAST_S)
+#define SXE2_PCI_PFVP_INT_ALLOC_VALID BIT(31)
+
+#define SXE2_PCIEPROC_INT2FUNC(_INT) (SXE2_PCIEPROC_BASE + 0xe000 + ((_INT) * 4))
+#define SXE2_PCIEPROC_INT2FUNC_VF_NUM_S 0
+#define SXE2_PCIEPROC_INT2FUNC_VF_NUM_M (0xFF << SXE2_PCIEPROC_INT2FUNC_VF_NUM_S)
+#define SXE2_PCIEPROC_INT2FUNC_PF_NUM_S 12
+#define SXE2_PCIEPROC_INT2FUNC_PF_NUM_M (0x7 << SXE2_PCIEPROC_INT2FUNC_PF_NUM_S)
+#define SXE2_PCIEPROC_INT2FUNC_IS_PF_S 16
+#define SXE2_PCIEPROC_INT2FUNC_IS_PF_M BIT(16)
+
+#define SXE2_VSI_PF(vf_idx) (SXE2_PF_INT_BASE + 0x14000 + ((vf_idx) * 4))
+#define SXE2_VSI_PF_ID_S 0
+#define SXE2_VSI_PF_ID_M (0x7 << SXE2_VSI_PF_ID_S)
+#define SXE2_VSI_PF_EN_M BIT(3)
+
+#define SXE2_MBX_CTL(_VSI) (0x0026692C + ((_VSI) * 4))
+#define SXE2_MBX_CTL_MSIX_INDX_S 0
+#define SXE2_MBX_CTL_MSIX_INDX_M (0x7FF << SXE2_MBX_CTL_MSIX_INDX_S)
+#define SXE2_MBX_CTL_CAUSE_ENA_M BIT(30)
+
+#define SXE2_PF_INT_TQCTL(q_idx) (SXE2_PF_INT_BASE + 0x092C + 4 * (q_idx))
+#define SXE2_PF_INT_TQCTL_MSIX_IDX 0x7FF
+#define SXE2_PF_INT_TQCTL_ITR_IDX_S 11
+#define SXE2_PF_INT_TQCTL_ITR_IDX \
+ (0x3 << SXE2_PF_INT_TQCTL_ITR_IDX_S)
+#define SXE2_PF_INT_TQCTL_CAUSE_ENABLE BIT(30)
+
+#define SXE2_PF_INT_RQCTL(q_idx) (SXE2_PF_INT_BASE + 0x292C + 4 * (q_idx))
+#define SXE2_PF_INT_RQCTL_MSIX_IDX 0x7FF
+#define SXE2_PF_INT_RQCTL_ITR_IDX_S 11
+#define SXE2_PF_INT_RQCTL_ITR_IDX \
+ (0x3 << SXE2_PF_INT_RQCTL_ITR_IDX_S)
+#define SXE2_PF_INT_RQCTL_CAUSE_ENABLE BIT(30)
+
+#define SXE2_PF_INT_RATE(irq_idx) (SXE2_PF_INT_BASE + 0x7530 + 4 * (irq_idx))
+#define SXE2_PF_INT_RATE_CREDIT_INTERVAL (0x3F)
+#define SXE2_PF_INT_RATE_CREDIT_INTERVAL_MAX \
+ (0x3F)
+#define SXE2_PF_INT_RATE_INTRL_ENABLE (BIT(6))
+#define SXE2_PF_INT_RATE_CREDIT_MAX_VALUE_SHIFT (7)
+#define SXE2_PF_INT_RATE_CREDIT_MAX_VALUE \
+ (0x3F << SXE2_PF_INT_RATE_CREDIT_MAX_VALUE_SHIFT)
+
+#define SXE2_VF_INT_ITR(itr_idx, irq_idx) \
+ (SXE2_PF_INT_BASE + 0xB530 + 0x2000 * (itr_idx) + 4 * (irq_idx))
+#define SXE2_VF_INT_ITR_INTERVAL 0xFFF
+
+#define SXE2_VF_DYN_CTL(irq_idx) (SXE2_PF_INT_BASE + 0x9530 + 4 * (irq_idx))
+#define SXE2_VF_DYN_CTL_INTENABLE BIT(0)
+#define SXE2_VF_DYN_CTL_CLEARPBA BIT(1)
+#define SXE2_VF_DYN_CTL_SWINT_TRIG BIT(2)
+#define SXE2_VF_DYN_CTL_ITR_IDX_S \
+ 3
+#define SXE2_VF_DYN_CTL_ITR_IDX_M 0x3
+#define SXE2_VF_DYN_CTL_INTERVAL_S 5
+#define SXE2_VF_DYN_CTL_INTERVAL_M 0xFFF
+#define SXE2_VF_DYN_CTL_SW_ITR_IDX_ENABLE BIT(24)
+#define SXE2_VF_DYN_CTL_SW_ITR_IDX_S 25
+#define SXE2_VF_DYN_CTL_SW_ITR_IDX_M 0x3
+
+#define SXE2_VF_DYN_CTL_INTENABLE_MSK \
+ BIT(31)
+
+#define SXE2_BAR4_MSIX_BASE 0
+#define SXE2_BAR4_MSIX_CTL(_idx) (SXE2_BAR4_MSIX_BASE + 0xC + ((_idx) * 0x10))
+#define SXE2_BAR4_MSIX_ENABLE 0
+#define SXE2_BAR4_MSIX_DISABLE 1
+
+#define SXE2_TXQ_LEGACY_DBLL(_DBQM) (0x1000 + ((_DBQM) * 4))
+
+#define SXE2_TXQ_CONTEXT0(_pfIdx) (0x10040 + ((_pfIdx) * 0x100))
+#define SXE2_TXQ_CONTEXT1(_pfIdx) (0x10044 + ((_pfIdx) * 0x100))
+#define SXE2_TXQ_CONTEXT2(_pfIdx) (0x10048 + ((_pfIdx) * 0x100))
+#define SXE2_TXQ_CONTEXT3(_pfIdx) (0x1004C + ((_pfIdx) * 0x100))
+#define SXE2_TXQ_CONTEXT4(_pfIdx) (0x10050 + ((_pfIdx) * 0x100))
+#define SXE2_TXQ_CONTEXT7(_pfIdx) (0x1005C + ((_pfIdx) * 0x100))
+#define SXE2_TXQ_CONTEXT7_HEAD_S 0
+#define SXE2_TXQ_CONTEXT7_HEAD_M SXE2_BITS_MASK(0xFFF, SXE2_TXQ_CONTEXT7_HEAD_S)
+#define SXE2_TXQ_CONTEXT7_READ_HEAD_S 16
+#define SXE2_TXQ_CONTEXT7_READ_HEAD_M SXE2_BITS_MASK(0xFFF, SXE2_TXQ_CONTEXT7_READ_HEAD_S)
+
+#define SXE2_TXQ_CTRL(_pfIdx) (0x10064 + ((_pfIdx) * 0x100))
+#define SXE2_TXQ_CTXT_CTRL(_pfIdx) (0x100C8 + ((_pfIdx) * 0x100))
+#define SXE2_TXQ_DIS_CNT(_pfIdx) (0x100D0 + ((_pfIdx) * 0x100))
+
+#define SXE2_TXQ_CTXT_CTRL_USED_MASK 0x00000800
+#define SXE2_TXQ_CTRL_SW_EN_M BIT(0)
+#define SXE2_TXQ_CTRL_HW_EN_M BIT(1)
+
+#define SXE2_TXQ_CTXT2_PROT_IDX_S 0
+#define SXE2_TXQ_CTXT2_PROT_IDX_M SXE2_BITS_MASK(0x7, 0)
+#define SXE2_TXQ_CTXT2_CGD_IDX_S 4
+#define SXE2_TXQ_CTXT2_CGD_IDX_M SXE2_BITS_MASK(0x1F, 4)
+#define SXE2_TXQ_CTXT2_PF_IDX_S 9
+#define SXE2_TXQ_CTXT2_PF_IDX_M SXE2_BITS_MASK(0x7, 9)
+#define SXE2_TXQ_CTXT2_VMVF_IDX_S 12
+#define SXE2_TXQ_CTXT2_VMVF_IDX_M SXE2_BITS_MASK(0x3FF, 12)
+#define SXE2_TXQ_CTXT2_VMVF_TYPE_S 23
+#define SXE2_TXQ_CTXT2_VMVF_TYPE_M SXE2_BITS_MASK(0x3, 23)
+#define SXE2_TXQ_CTXT2_TSYN_ENA_S 25
+#define SXE2_TXQ_CTXT2_TSYN_ENA_M BIT(25)
+#define SXE2_TXQ_CTXT2_ALT_VLAN_S 26
+#define SXE2_TXQ_CTXT2_ALT_VLAN_M BIT(26)
+#define SXE2_TXQ_CTXT2_WB_MODE_S 27
+#define SXE2_TXQ_CTXT2_WB_MODE_M BIT(27)
+#define SXE2_TXQ_CTXT2_ITR_WB_S 28
+#define SXE2_TXQ_CTXT2_ITR_WB_M BIT(28)
+#define SXE2_TXQ_CTXT2_LEGACY_EN_S 29
+#define SXE2_TXQ_CTXT2_LEGACY_EN_M BIT(29)
+#define SXE2_TXQ_CTXT2_SSO_EN_S 30
+#define SXE2_TXQ_CTXT2_SSO_EN_M BIT(30)
+
+#define SXE2_TXQ_CTXT3_SRC_VSI_S 0
+#define SXE2_TXQ_CTXT3_SRC_VSI_M SXE2_BITS_MASK(0x3FF, 0)
+#define SXE2_TXQ_CTXT3_CPU_ID_S 12
+#define SXE2_TXQ_CTXT3_CPU_ID_M SXE2_BITS_MASK(0xFF, 12)
+#define SXE2_TXQ_CTXT3_TPH_RDDESC_S 20
+#define SXE2_TXQ_CTXT3_TPH_RDDESC_M BIT(20)
+#define SXE2_TXQ_CTXT3_TPH_RDDATA_S 21
+#define SXE2_TXQ_CTXT3_TPH_RDDATA_M BIT(21)
+#define SXE2_TXQ_CTXT3_TPH_WRDESC_S 22
+#define SXE2_TXQ_CTXT3_TPH_WRDESC_M BIT(22)
+
+#define SXE2_TXQ_CTXT3_QID_IN_FUNC_S 0
+#define SXE2_TXQ_CTXT3_QID_IN_FUNC_M SXE2_BITS_MASK(0x7FF, 0)
+#define SXE2_TXQ_CTXT3_RDDESC_RO_S 13
+#define SXE2_TXQ_CTXT3_RDDESC_RO_M BIT(13)
+#define SXE2_TXQ_CTXT3_WRDESC_RO_S 14
+#define SXE2_TXQ_CTXT3_WRDESC_RO_M BIT(14)
+#define SXE2_TXQ_CTXT3_RDDATA_RO_S 15
+#define SXE2_TXQ_CTXT3_RDDATA_RO_M BIT(15)
+#define SXE2_TXQ_CTXT3_QLEN_S 16
+#define SXE2_TXQ_CTXT3_QLEN_M SXE2_BITS_MASK(0x1FFF, 16)
+
+#define SXE2_RX_BUF_CHAINED_MAX 10
+#define SXE2_RX_DESC_BASE_ADDR_UNIT 7
+#define SXE2_RX_HBUF_LEN_UNIT 6
+#define SXE2_RX_DBUF_LEN_UNIT 7
+#define SXE2_RX_DBUF_LEN_MASK (~0x7F)
+#define SXE2_RX_HWTAIL_VALUE_MASK (~0x7)
+
+enum {
+ SXE2_RX_CTXT0 = 0,
+ SXE2_RX_CTXT1,
+ SXE2_RX_CTXT2,
+ SXE2_RX_CTXT3,
+ SXE2_RX_CTXT4,
+ SXE2_RX_CTXT_CNT,
+};
+
+#define SXE2_RX_CTXT_BASE_L_S 0
+#define SXE2_RX_CTXT_BASE_L_W 32
+
+#define SXE2_RX_CTXT_BASE_H_S 0
+#define SXE2_RX_CTXT_BASE_H_W 25
+#define SXE2_RX_CTXT_DEPTH_L_S 25
+#define SXE2_RX_CTXT_DEPTH_L_W 7
+
+#define SXE2_RX_CTXT_DEPTH_H_S 0
+#define SXE2_RX_CTXT_DEPTH_H_W 6
+
+#define SXE2_RX_CTXT_DBUFF_S 6
+#define SXE2_RX_CTXT_DBUFF_W 7
+
+#define SXE2_RX_CTXT_HBUFF_S 13
+#define SXE2_RX_CTXT_HBUFF_W 5
+
+#define SXE2_RX_CTXT_HSPLT_TYPE_S 18
+#define SXE2_RX_CTXT_HSPLT_TYPE_W 2
+
+#define SXE2_RX_CTXT_DESC_TYPE_S 20
+#define SXE2_RX_CTXT_DESC_TYPE_W 1
+
+#define SXE2_RX_CTXT_CRC_S 21
+#define SXE2_RX_CTXT_CRC_W 1
+
+#define SXE2_RX_CTXT_L2TAG_FLAG_S 23
+#define SXE2_RX_CTXT_L2TAG_FLAG_W 1
+
+#define SXE2_RX_CTXT_HSPLT_0_S 24
+#define SXE2_RX_CTXT_HSPLT_0_W 4
+
+#define SXE2_RX_CTXT_HSPLT_1_S 28
+#define SXE2_RX_CTXT_HSPLT_1_W 2
+
+#define SXE2_RX_CTXT_INVALN_STP_S 31
+#define SXE2_RX_CTXT_INVALN_STP_W 1
+
+#define SXE2_RX_CTXT_LRO_ENABLE_S 0
+#define SXE2_RX_CTXT_LRO_ENABLE_W 1
+
+#define SXE2_RX_CTXT_CPUID_S 3
+#define SXE2_RX_CTXT_CPUID_W 8
+
+#define SXE2_RX_CTXT_MAX_FRAME_SIZE_S 11
+#define SXE2_RX_CTXT_MAX_FRAME_SIZE_W 14
+
+#define SXE2_RX_CTXT_LRO_DESC_MAX_S 25
+#define SXE2_RX_CTXT_LRO_DESC_MAX_W 4
+
+#define SXE2_RX_CTXT_RELAX_DATA_S 29
+#define SXE2_RX_CTXT_RELAX_DATA_W 1
+
+#define SXE2_RX_CTXT_RELAX_WB_S 30
+#define SXE2_RX_CTXT_RELAX_WB_W 1
+
+#define SXE2_RX_CTXT_RELAX_RD_S 31
+#define SXE2_RX_CTXT_RELAX_RD_W 1
+
+#define SXE2_RX_CTXT_THPRDESC_ENABLE_S 1
+#define SXE2_RX_CTXT_THPRDESC_ENABLE_W 1
+
+#define SXE2_RX_CTXT_THPWDESC_ENABLE_S 2
+#define SXE2_RX_CTXT_THPWDESC_ENABLE_W 1
+
+#define SXE2_RX_CTXT_THPRDATA_ENABLE_S 3
+#define SXE2_RX_CTXT_THPRDATA_ENABLE_W 1
+
+#define SXE2_RX_CTXT_THPHEAD_ENABLE_S 4
+#define SXE2_RX_CTXT_THPHEAD_ENABLE_W 1
+
+#define SXE2_RX_CTXT_LOW_DESC_LINE_S 6
+#define SXE2_RX_CTXT_LOW_DESC_LINE_W 3
+
+#define SXE2_RX_CTXT_VF_ID_S 9
+#define SXE2_RX_CTXT_VF_ID_W 8
+
+#define SXE2_RX_CTXT_PF_ID_S 17
+#define SXE2_RX_CTXT_PF_ID_W 3
+
+#define SXE2_RX_CTXT_VF_ENABLE_S 20
+#define SXE2_RX_CTXT_VF_ENABLE_W 1
+
+#define SXE2_RX_CTXT_VSI_ID_S 21
+#define SXE2_RX_CTXT_VSI_ID_W 10
+
+#define SXE2_PF_CTRLQ_FW_BASE 0x00312000
+#define SXE2_PF_CTRLQ_FW_ATQBAL (SXE2_PF_CTRLQ_FW_BASE + 0x0000)
+#define SXE2_PF_CTRLQ_FW_ARQBAL (SXE2_PF_CTRLQ_FW_BASE + 0x0080)
+#define SXE2_PF_CTRLQ_FW_ATQBAH (SXE2_PF_CTRLQ_FW_BASE + 0x0100)
+#define SXE2_PF_CTRLQ_FW_ARQBAH (SXE2_PF_CTRLQ_FW_BASE + 0x0180)
+#define SXE2_PF_CTRLQ_FW_ATQLEN (SXE2_PF_CTRLQ_FW_BASE + 0x0200)
+#define SXE2_PF_CTRLQ_FW_ARQLEN (SXE2_PF_CTRLQ_FW_BASE + 0x0280)
+#define SXE2_PF_CTRLQ_FW_ATQH (SXE2_PF_CTRLQ_FW_BASE + 0x0300)
+#define SXE2_PF_CTRLQ_FW_ARQH (SXE2_PF_CTRLQ_FW_BASE + 0x0380)
+#define SXE2_PF_CTRLQ_FW_ATQT (SXE2_PF_CTRLQ_FW_BASE + 0x0400)
+#define SXE2_PF_CTRLQ_FW_ARQT (SXE2_PF_CTRLQ_FW_BASE + 0x0480)
+
+#define SXE2_PF_CTRLQ_MBX_BASE 0x00316000
+#define SXE2_PF_CTRLQ_MBX_ATQBAL (SXE2_PF_CTRLQ_MBX_BASE + 0xE100)
+#define SXE2_PF_CTRLQ_MBX_ATQBAH (SXE2_PF_CTRLQ_MBX_BASE + 0xE180)
+#define SXE2_PF_CTRLQ_MBX_ATQLEN (SXE2_PF_CTRLQ_MBX_BASE + 0xE200)
+#define SXE2_PF_CTRLQ_MBX_ATQH (SXE2_PF_CTRLQ_MBX_BASE + 0xE280)
+#define SXE2_PF_CTRLQ_MBX_ATQT (SXE2_PF_CTRLQ_MBX_BASE + 0xE300)
+#define SXE2_PF_CTRLQ_MBX_ARQBAL (SXE2_PF_CTRLQ_MBX_BASE + 0xE380)
+#define SXE2_PF_CTRLQ_MBX_ARQBAH (SXE2_PF_CTRLQ_MBX_BASE + 0xE400)
+#define SXE2_PF_CTRLQ_MBX_ARQLEN (SXE2_PF_CTRLQ_MBX_BASE + 0xE480)
+#define SXE2_PF_CTRLQ_MBX_ARQH (SXE2_PF_CTRLQ_MBX_BASE + 0xE500)
+#define SXE2_PF_CTRLQ_MBX_ARQT (SXE2_PF_CTRLQ_MBX_BASE + 0xE580)
+
+#define SXE2_CMD_REG_LEN_M 0x3FF
+#define SXE2_CMD_REG_LEN_VFE_M BIT(28)
+#define SXE2_CMD_REG_LEN_OVFL_M BIT(29)
+#define SXE2_CMD_REG_LEN_CRIT_M BIT(30)
+#define SXE2_CMD_REG_LEN_ENABLE_M BIT(31)
+
+#define SXE2_CMD_REG_HEAD_M 0x3FF
+
+#define SXE2_PF_CTRLQ_FW_HW_STS (SXE2_PF_CTRLQ_FW_BASE + 0x0500)
+#define SXE2_PF_CTRLQ_FW_ATQ_IDLE_MASK BIT(0)
+#define SXE2_PF_CTRLQ_FW_ARQ_IDLE_MASK BIT(1)
+
+#define SXE2_TOP_CFG_BASE 0x00292000
+#define SXE2_HW_VER (SXE2_TOP_CFG_BASE + 0x48c)
+#define SXE2_HW_FPGA_VER_M SXE2_BITS_MASK(0xFFF, 0)
+
+#define SXE2_FW_VER (SXE2_TOP_CFG_BASE + 0x214)
+#define SXE2_FW_VER_BUILD_M SXE2_BITS_MASK(0xFF, 0)
+#define SXE2_FW_VER_FIX_M SXE2_BITS_MASK(0xFF, 8)
+#define SXE2_FW_VER_SUB_M SXE2_BITS_MASK(0xFF, 16)
+#define SXE2_FW_VER_MAIN_M SXE2_BITS_MASK(0xFF, 24)
+#define SXE2_FW_VER_FIX_SHIFT (8)
+#define SXE2_FW_VER_SUB_SHIFT (16)
+#define SXE2_FW_VER_MAIN_SHIFT (24)
+
+#define SXE2_FW_COMP_VER_ADDR (SXE2_TOP_CFG_BASE + 0x20c)
+
+#define SXE2_STATUS SXE2_FW_VER
+
+#define SXE2_FW_STATE (SXE2_TOP_CFG_BASE + 0x210)
+
+#define SXE2_FW_HEARTBEAT (SXE2_TOP_CFG_BASE + 0x218)
+
+#define SXE2_FW_MISC (SXE2_TOP_CFG_BASE + 0x21c)
+#define SXE2_FW_MISC_MODE_M SXE2_BITS_MASK(0xF, 0)
+#define SXE2_FW_MISC_POP_M SXE2_BITS_MASK(0x80000000, 0)
+
+#define SXE2_TX_OE_BASE 0x00030000
+#define SXE2_RX_OE_BASE 0x00050000
+
+#define SXE2_PFP_L2TAGSEN(_i) (SXE2_TX_OE_BASE + 0x00300 + ((_i) * 4))
+#define SXE2_VSI_L2TAGSTXVALID(_i) \
+ (SXE2_TX_OE_BASE + 0x01000 + ((_i) * 4))
+#define SXE2_VSI_TIR0(_i) (SXE2_TX_OE_BASE + 0x01C00 + ((_i) * 4))
+#define SXE2_VSI_TIR1(_i) (SXE2_TX_OE_BASE + 0x02800 + ((_i) * 4))
+#define SXE2_VSI_TAR(_i) (SXE2_TX_OE_BASE + 0x04C00 + ((_i) * 4))
+#define SXE2_VSI_TSR(_i) (SXE2_RX_OE_BASE + 0x18000 + ((_i) * 4))
+
+#define SXE2_STATS_TX_LAN_CONFIG(_i) (SXE2_TX_OE_BASE + 0x08300 + ((_i) * 4))
+#define SXE2_STATS_TX_LAN_PKT_CNT_GET(_i) (SXE2_TX_OE_BASE + 0x08340 + ((_i) * 4))
+#define SXE2_STATS_TX_LAN_BYTE_CNT_GET(_i) (SXE2_TX_OE_BASE + 0x08380 + ((_i) * 4))
+
+#define SXE2_STATS_RX_CONFIG(_i) (SXE2_RX_OE_BASE + 0x230B0 + ((_i) * 4))
+#define SXE2_STATS_RX_LAN_PKT_CNT_GET(_i) (SXE2_RX_OE_BASE + 0x230C0 + ((_i) * 8))
+#define SXE2_STATS_RX_LAN_BYTE_CNT_GET(_i) (SXE2_RX_OE_BASE + 0x23120 + ((_i) * 8))
+#define SXE2_STATS_RX_FD_PKT_CNT_GET(_i) (SXE2_RX_OE_BASE + 0x230E0 + ((_i) * 8))
+#define SXE2_STATS_RX_MNG_IN_PKT_CNT_GET(_i) (SXE2_RX_OE_BASE + 0x23100 + ((_i) * 8))
+#define SXE2_STATS_RX_MNG_IN_BYTE_CNT_GET(_i) (SXE2_RX_OE_BASE + 0x23140 + ((_i) * 8))
+#define SXE2_STATS_RX_MNG_OUT_PKT_CNT_GET(_i) (SXE2_RX_OE_BASE + 0x23160 + ((_i) * 8))
+
+#define SXE2_L2TAG_ID_STAG 0
+#define SXE2_L2TAG_ID_OUT_VLAN1 1
+#define SXE2_L2TAG_ID_OUT_VLAN2 2
+#define SXE2_L2TAG_ID_VLAN 3
+
+#define SXE2_PFP_L2TAGSEN_ALL_TAG 0xFF
+#define SXE2_PFP_L2TAGSEN_DVM BIT(10)
+
+#define SXE2_VSI_TSR_STRIP_TAG_S 0
+#define SXE2_VSI_TSR_SHOW_TAG_S 4
+
+#define SXE2_VSI_TSR_ID_STAG BIT(0)
+#define SXE2_VSI_TSR_ID_OUT_VLAN1 BIT(1)
+#define SXE2_VSI_TSR_ID_OUT_VLAN2 BIT(2)
+#define SXE2_VSI_TSR_ID_VLAN BIT(3)
+
+#define SXE2_VSI_L2TAGSTXVALID_L2TAG1_ID_S 0
+#define SXE2_VSI_L2TAGSTXVALID_L2TAG1_ID_M 0x7
+#define SXE2_VSI_L2TAGSTXVALID_L2TAG1_VALID BIT(3)
+#define SXE2_VSI_L2TAGSTXVALID_L2TAG2_ID_S 4
+#define SXE2_VSI_L2TAGSTXVALID_L2TAG2_ID_M 0x7
+#define SXE2_VSI_L2TAGSTXVALID_L2TAG2_VALID BIT(7)
+#define SXE2_VSI_L2TAGSTXVALID_TIR0_ID_S 16
+#define SXE2_VSI_L2TAGSTXVALID_TIR0_VALID BIT(19)
+#define SXE2_VSI_L2TAGSTXVALID_TIR1_ID_S 20
+#define SXE2_VSI_L2TAGSTXVALID_TIR1_VALID BIT(23)
+
+#define SXE2_VSI_L2TAGSTXVALID_ID_STAG 0
+#define SXE2_VSI_L2TAGSTXVALID_ID_OUT_VLAN1 2
+#define SXE2_VSI_L2TAGSTXVALID_ID_OUT_VLAN2 3
+#define SXE2_VSI_L2TAGSTXVALID_ID_VLAN 4
+
+#define SXE2_SWITCH_OG_BASE 0x00140000
+#define SXE2_SWITCH_SWE_BASE 0x00150000
+#define SXE2_SWITCH_RG_BASE 0x00160000
+
+#define SXE2_VSI_RX_SWITCH_CTRL(_i) (SXE2_SWITCH_RG_BASE + 0x01074 + ((_i) * 4))
+#define SXE2_VSI_TX_SWITCH_CTRL(_i) (SXE2_SWITCH_RG_BASE + 0x01C74 + ((_i) * 4))
+
+#define SXE2_VSI_RX_SW_CTRL_VLAN_PRUNE BIT(9)
+
+#define SXE2_VSI_TX_SW_CTRL_LOOPBACK_EN BIT(1)
+#define SXE2_VSI_TX_SW_CTRL_LAN_EN BIT(2)
+#define SXE2_VSI_TX_SW_CTRL_MACAS_EN BIT(3)
+#define SXE2_VSI_TX_SW_CTRL_VLAN_PRUNE BIT(9)
+
+#define SXE2_VSI_TAR_UNTAGGED_SHIFT (16)
+
+#define SXE2_PCIE_SYS_READY 0x38c
+#define SXE2_PCIE_SYS_READY_CORER_ASSERT BIT(0)
+#define SXE2_PCIE_SYS_READY_STOP_DROP_DONE BIT(2)
+#define SXE2_PCIE_SYS_READY_R5 BIT(3)
+#define SXE2_PCIE_SYS_READY_STOP_DROP BIT(16)
+
+#define SXE2_PCIE_DEV_CTRL_DEV_STATUS 0x78
+#define SXE2_PCIE_DEV_CTRL_DEV_STATUS_TRANS_PENDING BIT(21)
+
+#define SXE2_TOP_CFG_CORE (SXE2_TOP_CFG_BASE + 0x0630)
+#define SXE2_TOP_CFG_CORE_RST_CODE 0x09FBD586
+
+#define SXE2_PFGEN_CTRL (0x00336000)
+#define SXE2_PFGEN_CTRL_PFSWR BIT(0)
+
+#define SXE2_VFGEN_CTRL(_vf) (0x00337000 + ((_vf) * 4))
+#define SXE2_VFGEN_CTRL_VFSWR BIT(0)
+
+#define SXE2_VF_VRC_VFGEN_RSTAT(_vf) (0x00338000 + (_vf)*4)
+#define SXE2_VF_VRC_VFGEN_VFRSTAT (0x3)
+#define SXE2_VF_VRC_VFGEN_VFRSTAT_VFR (0)
+#define SXE2_VF_VRC_VFGEN_VFRSTAT_COMPLETE (BIT(0))
+#define SXE2_VF_VRC_VFGEN_VFRSTAT_VF_ACTIVE (BIT(1))
+#define SXE2_VF_VRC_VFGEN_VFRSTAT_MASK (BIT(2))
+#define SXE2_VF_VRC_VFGEN_VFRSTAT_FORVF (0x300)
+#define SXE2_VF_VRC_VFGEN_VFRSTAT_FORVF_NO_VFR (0)
+#define SXE2_VF_VRC_VFGEN_VFRSTAT_FORVF_VFR (1)
+#define SXE2_VF_VRC_VFGEN_VFRSTAT_FORVF_MASK (BIT(10))
+
+#define SXE2_GLGEN_VFLRSTAT(_reg) (0x0033A000 + ((_reg)*4))
+
+#define SXE2_ACCEPT_RULE_TAGGED_S 0
+#define SXE2_ACCEPT_RULE_UNTAGGED_S 16
+
+#define SXE2_VF_RXQ_BASE(_VF) (0x000b0800 + ((_VF) * 4))
+#define SXE2_VF_RXQ_BASE_FIRST_Q_S 0
+#define SXE2_VF_RXQ_BASE_FIRST_Q_M (0x7FF << SXE2_VF_RXQ_BASE_FIRST_Q_S)
+#define SXE2_VF_RXQ_BASE_Q_NUM_S 16
+#define SXE2_VF_RXQ_BASE_Q_NUM_M (0x7FF << SXE2_VF_RXQ_BASE_Q_NUM_S)
+
+#define SXE2_VF_RXQ_MAPENA(_VF) (0x000b0400 + ((_VF) * 4))
+#define SXE2_VF_RXQ_MAPENA_M BIT(0)
+
+#define SXE2_VF_TXQ_BASE(_VF) (0x00040400 + ((_VF) * 4))
+#define SXE2_VF_TXQ_BASE_FIRST_Q_S 0
+#define SXE2_VF_TXQ_BASE_FIRST_Q_M (0x3FFF << SXE2_VF_TXQ_BASE_FIRST_Q_S)
+#define SXE2_VF_TXQ_BASE_Q_NUM_S 16
+#define SXE2_VF_TXQ_BASE_Q_NUM_M (0xFF << SXE2_VF_TXQ_BASE_Q_NUM_S)
+
+#define SXE2_VF_TXQ_MAPENA(_VF) (0x00045000 + ((_VF) * 4))
+#define SXE2_VF_TXQ_MAPENA_M BIT(0)
+
+#define PRI_PTP_BASEADDR 0x2a8000
+
+#define GLTSYN (PRI_PTP_BASEADDR + 0x0)
+#define GLTSYN_ENA_M BIT(0)
+
+#define GLTSYN_CMD (PRI_PTP_BASEADDR + 0x4)
+#define GLTSYN_CMD_INIT_TIME 0x01
+#define GLTSYN_CMD_INIT_INCVAL 0x02
+#define GLTSYN_CMD_ADJ_TIME 0x04
+#define GLTSYN_CMD_ADJ_TIME_AT_TIME 0x0C
+#define GLTSYN_CMD_LATCHING_SHTIME 0x80
+
+#define GLTSYN_SYNC (PRI_PTP_BASEADDR + 0x8)
+#define GLTSYN_SYNC_PLUS_1NS 0x1
+#define GLTSYN_SYNC_MINUS_1NS 0x2
+#define GLTSYN_SYNC_EXEC 0x3
+#define GLTSYN_SYNC_GEN_PULSE 0x4
+
+#define GLTSYN_SEM (PRI_PTP_BASEADDR + 0xC)
+#define GLTSYN_SEM_BUSY_M BIT(0)
+
+#define GLTSYN_STAT (PRI_PTP_BASEADDR + 0x10)
+#define GLTSYN_STAT_EVENT0_M BIT(0)
+#define GLTSYN_STAT_EVENT1_M BIT(1)
+#define GLTSYN_STAT_EVENT2_M BIT(2)
+
+#define GLTSYN_TIME_SUBNS (PRI_PTP_BASEADDR + 0x20)
+#define GLTSYN_TIME_NS (PRI_PTP_BASEADDR + 0x24)
+#define GLTSYN_TIME_S_H (PRI_PTP_BASEADDR + 0x28)
+#define GLTSYN_TIME_S_L (PRI_PTP_BASEADDR + 0x2C)
+
+#define GLTSYN_SHTIME_SUBNS (PRI_PTP_BASEADDR + 0x30)
+#define GLTSYN_SHTIME_NS (PRI_PTP_BASEADDR + 0x34)
+#define GLTSYN_SHTIME_S_H (PRI_PTP_BASEADDR + 0x38)
+#define GLTSYN_SHTIME_S_L (PRI_PTP_BASEADDR + 0x3C)
+
+#define GLTSYN_SHADJ_SUBNS (PRI_PTP_BASEADDR + 0x40)
+#define GLTSYN_SHADJ_NS (PRI_PTP_BASEADDR + 0x44)
+
+#define GLTSYN_INCVAL_NS (PRI_PTP_BASEADDR + 0x50)
+#define GLTSYN_INCVAL_SUBNS (PRI_PTP_BASEADDR + 0x54)
+
+#define GLTSYN_TGT_NS(_i) \
+ (PRI_PTP_BASEADDR + 0x60 + ((_i) * 16))
+#define GLTSYN_TGT_S_H(_i) (PRI_PTP_BASEADDR + 0x64 + ((_i) * 16))
+#define GLTSYN_TGT_S_L(_i) (PRI_PTP_BASEADDR + 0x68 + ((_i) * 16))
+
+#define GLTSYN_EVENT_NS(_i) \
+ (PRI_PTP_BASEADDR + 0xA0 + ((_i) * 16))
+
+#define GLTSYN_EVENT_S_H(_i) (PRI_PTP_BASEADDR + 0xA4 + ((_i) * 16))
+#define GLTSYN_EVENT_S_H_MASK (0xFFFF)
+
+#define GLTSYN_EVENT_S_L(_i) (PRI_PTP_BASEADDR + 0xA8 + ((_i) * 16))
+
+#define GLTSYN_AUXOUT(_i) \
+ (PRI_PTP_BASEADDR + 0xD0 + ((_i) * 4))
+#define GLTSYN_AUXOUT_OUT_ENA BIT(0)
+#define GLTSYN_AUXOUT_OUT_MOD (0x03 << 1)
+#define GLTSYN_AUXOUT_OUTLVL BIT(3)
+#define GLTSYN_AUXOUT_INT_ENA BIT(4)
+#define GLTSYN_AUXOUT_PULSEW (0x1fff << 3)
+
+#define GLTSYN_CLKO(_i) \
+ (PRI_PTP_BASEADDR + 0xE0 + ((_i) * 4))
+
+#define GLTSYN_AUXIN(_i) (PRI_PTP_BASEADDR + 0xF4 + ((_i) * 4))
+#define GLTSYN_AUXIN_RISING_EDGE BIT(0)
+#define GLTSYN_AUXIN_FALLING_EDGE BIT(1)
+#define GLTSYN_AUXIN_ENABLE BIT(4)
+
+#define CGMAC_CSR_BASE 0x2B4000
+
+#define CGMAC_PORT_OFFSET 0x00004000
+
+#define PFP_CGM_TX_TSMEM(_port, _i) \
+ (CGMAC_CSR_BASE + 0x100 + \
+ + CGMAC_PORT_OFFSET * _port + ((_i) * 4))
+
+#define PFP_CGM_TX_TXHI(_port, _i) (CGMAC_CSR_BASE + CGMAC_PORT_OFFSET * _port + 0x108 + ((_i) * 8))
+#define PFP_CGM_TX_TXLO(_port, _i) (CGMAC_CSR_BASE + CGMAC_PORT_OFFSET * _port + 0x10C + ((_i) * 8))
+
+#define CGMAC_CSR_MAC0_OFFSET 0x2B4000
+#define CGMAC_CSR_MAC_OFFSET(_i) (CGMAC_CSR_MAC0_OFFSET + ((_i) * 0x4000))
+
+#define PFP_CGM_MAC_TX_TSMEM(_phy, _i) \
+ (CGMAC_CSR_MAC_OFFSET(_phy) + 0x100 + \
+ ((_i) * 4))
+
+#define PFP_CGM_MAC_TX_TXHI(_phy, _i) (CGMAC_CSR_MAC_OFFSET(_phy) + 0x108 + ((_i) * 8))
+#define PFP_CGM_MAC_TX_TXLO(_phy, _i) (CGMAC_CSR_MAC_OFFSET(_phy) + 0x10C + ((_i) * 8))
+
+#define SXE2_VF_GLINT_CEQCTL_MSIX_INDX_M SXE2_BITS_MASK(0x7FF, 0)
+#define SXE2_VF_GLINT_CEQCTL_ITR_INDX_S 11
+#define SXE2_VF_GLINT_CEQCTL_ITR_INDX_M SXE2_BITS_MASK(0x3, 11)
+#define SXE2_VF_GLINT_CEQCTL_CAUSE_ENA_M BIT(30)
+#define SXE2_VF_GLINT_CEQCTL(_INT) (0x0026492C + ((_INT) * 4))
+
+#define SXE2_VF_PFINT_AEQCTL_MSIX_INDX_M SXE2_BITS_MASK(0x7FF, 0)
+#define SXE2_VF_VPINT_AEQCTL_ITR_INDX_S 11
+#define SXE2_VF_VPINT_AEQCTL_ITR_INDX_M SXE2_BITS_MASK(0x3, 11)
+#define SXE2_VF_VPINT_AEQCTL_CAUSE_ENA_M BIT(30)
+#define SXE2_VF_VPINT_AEQCTL(_VF) (0x0026052c + ((_VF) * 4))
+
+#define SXE2_IPSEC_TX_BASE (0x2A0000)
+#define SXE2_IPSEC_RX_BASE (0x2A2000)
+
+#define SXE2_IPSEC_RX_IPSIDX_ADDR (SXE2_IPSEC_RX_BASE + 0x0084)
+#define SXE2_IPSEC_RX_IPSIDX_RST (0x00040000)
+#define SXE2_IPSEC_RX_IPSIDX_VBI_SHIFT (18)
+#define SXE2_IPSEC_RX_IPSIDX_VBI_MASK (0x00040000)
+#define SXE2_IPSEC_RX_IPSIDX_SWRITE_SHIFT (17)
+#define SXE2_IPSEC_RX_IPSIDX_SWRITE_MASK (0x00020000)
+#define SXE2_IPSEC_RX_IPSIDX_SA_IDX_SHIFT (4)
+#define SXE2_IPSEC_RX_IPSIDX_SA_IDX_MASK (0x0000fff0)
+#define SXE2_IPSEC_RX_IPSIDX_TABLE_SHIFT (2)
+#define SXE2_IPSEC_RX_IPSIDX_TABLE_MASK (0x0000000c)
+
+#define SXE2_IPSEC_RX_IPSIPID_ADDR (SXE2_IPSEC_RX_BASE + 0x0088)
+#define SXE2_IPSEC_RX_IPSIPID_IP_ID_X_SHIFT (0)
+#define SXE2_IPSEC_RX_IPSIPID_IP_ID_X_MASK (0x000000ff)
+
+#define SXE2_IPSEC_RX_IPSSPI0_ADDR (SXE2_IPSEC_RX_BASE + 0x008c)
+#define SXE2_IPSEC_RX_IPSSPI0_SPI_X_SHIFT (0)
+#define SXE2_IPSEC_RX_IPSSPI0_SPI_X_MASK (0xffffffff)
+
+#define SXE2_IPSEC_RX_IPSSPI1_ADDR (SXE2_IPSEC_RX_BASE + 0x0090)
+#define SXE2_IPSEC_RX_IPSSPI1_SPI_Y_MASK (0xffffffff)
+
+#define SXE2_PAUSE_STATS_BASE(port) (0x002b2000 + port * 0x4000)
+#define SXE2_TXPAUSEXONFRAMES_LO(port) (SXE2_PAUSE_STATS_BASE(port) + 0x0894)
+#define SXE2_TXPAUSEXOFFFRAMES_LO(port) (SXE2_PAUSE_STATS_BASE(port) + 0x0a18)
+#define SXE2_TXPFCXONFRAMES_LO(port, pri) (SXE2_PAUSE_STATS_BASE(port) + \
+ (0x0a20 + 8 * (pri)))
+#define SXE2_TXPFCXOFFFRAMES_LO(port, pri) (SXE2_PAUSE_STATS_BASE(port) + \
+ (0x0a60 + 8 * (pri)))
+#define SXE2_TXPFCXONTOXOFFFRAMES_LO(port, pri) (SXE2_PAUSE_STATS_BASE(port) + \
+ (0x0aa0 + 8 * (pri)))
+#define SXE2_RXPAUSEXONFRAMES_LO(port) (SXE2_PAUSE_STATS_BASE(port) + 0x0988)
+#define SXE2_RXPAUSEXOFFFRAMES_LO(port) (SXE2_PAUSE_STATS_BASE(port) + 0x0b28)
+#define SXE2_RXPFCXONFRAMES_LO(port, pri) (SXE2_PAUSE_STATS_BASE(port) + \
+ (0x0b30 + 8 * (pri)))
+#define SXE2_RXPFCXOFFFRAMES_LO(port, pri) (SXE2_PAUSE_STATS_BASE(port) + \
+ (0x0b70 + 8 * (pri)))
+
+#endif
diff --git a/drivers/common/sxe2/sxe2_internal_ver.h b/drivers/common/sxe2/sxe2_internal_ver.h
new file mode 100644
index 0000000000..92f49e7a20
--- /dev/null
+++ b/drivers/common/sxe2/sxe2_internal_ver.h
@@ -0,0 +1,33 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (C), 2025, Wuxi Stars Micro System Technologies Co., Ltd.
+ */
+
+#ifndef __SXE2_INTERNAL_VER_H__
+#define __SXE2_INTERNAL_VER_H__
+
+#define SXE2_VER_MAJOR_OFFSET (16)
+#define SXE2_MK_VER(major, minor) \
+ (major << SXE2_VER_MAJOR_OFFSET | minor)
+#define SXE2_MK_VER_MAJOR(ver) (((ver) >> SXE2_VER_MAJOR_OFFSET) & 0xff)
+#define SXE2_MK_VER_MINOR(ver) ((ver) & 0xff)
+
+#define SXE2_ITR_VER_MAJOR_V100 1
+#define SXE2_ITR_VER_MAJOR_V200 2
+
+#define SXE2_ITR_VER_MAJOR 1
+#define SXE2_ITR_VER_MINOR 1
+#define SXE2_ITR_VER SXE2_MK_VER(SXE2_ITR_VER_MAJOR, SXE2_ITR_VER_MINOR)
+
+#define SXE2_CTRL_VER_IS_V100(ver) (SXE2_MK_VER_MAJOR(ver) == SXE2_ITR_VER_MAJOR_V100)
+#define SXE2_CTRL_VER_IS_V200(ver) (SXE2_MK_VER_MAJOR(ver) == SXE2_ITR_VER_MAJOR_V200)
+
+#define SXE2LIB_ITR_VER_MAJOR 1
+#define SXE2LIB_ITR_VER_MINOR 1
+#define SXE2LIB_ITR_VER SXE2_MK_VER(SXE2LIB_ITR_VER_MAJOR, SXE2LIB_ITR_VER_MINOR)
+
+#define SXE2_DRV_CLI_VER_MAJOR 1
+#define SXE2_DRV_CLI_VER_MINOR 1
+#define SXE2_DRV_CLI_VER \
+ SXE2_MK_VER(SXE2_DRV_CLI_VER_MAJOR, SXE2_DRV_CLI_VER_MINOR)
+
+#endif /* __SXE2_INTERNAL_VER_H__ */
diff --git a/drivers/common/sxe2/sxe2_osal.h b/drivers/common/sxe2/sxe2_osal.h
new file mode 100644
index 0000000000..930498f3c2
--- /dev/null
+++ b/drivers/common/sxe2/sxe2_osal.h
@@ -0,0 +1,152 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (C), 2025, Wuxi Stars Micro System Technologies Co., Ltd.
+ */
+
+#ifndef __SXE2_OSAL_H__
+#define __SXE2_OSAL_H__
+#include <string.h>
+#include <stdint.h>
+#include <stdarg.h>
+#include <inttypes.h>
+#include <stdbool.h>
+
+#include <rte_common.h>
+#include <rte_cycles.h>
+#include <rte_malloc.h>
+#include <rte_ether.h>
+#include <rte_version.h>
+#include <rte_bitops.h>
+
+#ifndef __BITS_PER_LONG
+#define __BITS_PER_LONG (__SIZEOF_LONG__ * 8)
+#endif
+#define BIT_WORD(nr) ((nr) / __BITS_PER_LONG)
+#define BIT_MASK(nr) (1UL << ((nr) % __BITS_PER_LONG))
+
+#ifndef TAILQ_FOREACH_SAFE
+#define TAILQ_FOREACH_SAFE(var, head, field, tvar) \
+ for ((var) = TAILQ_FIRST((head)); \
+ (var) && ((tvar) = TAILQ_NEXT((var), field), 1); \
+ (var) = (tvar))
+#endif
+
+#define upper_32_bits(n) ((uint32_t)(((n) >> 16) >> 16))
+#define lower_32_bits(n) ((uint32_t)((n) & 0xffffffff))
+
+#ifndef SXE2_DIV_ROUND_UP
+#define SXE2_DIV_ROUND_UP(n, d) \
+ (((n) + (typeof(n))(d) - (typeof(n))1) / (typeof(n))(d))
+#endif
+
+enum sxe2_itr_idx {
+ SXE2_ITR_IDX_0 = 0,
+ SXE2_ITR_IDX_1,
+ SXE2_ITR_IDX_2,
+ SXE2_ITR_IDX_NONE,
+};
+
+#define SXE2_ETH_ALEN 6
+#define SXE2_BITS_PER_BYTE 8
+#define BITS_TO_LONGS(nr) SXE2_DIV_ROUND_UP(nr, SXE2_BITS_PER_BYTE * sizeof(unsigned long))
+#define BITS_TO_U32(nr) SXE2_DIV_ROUND_UP(nr, 32)
+
+#define BITMAP_LAST_WORD_MASK(nbits) (~0UL >> (-(nbits) & (__BITS_PER_LONG - 1)))
+
+#define DECLARE_BITMAP(name, bits) \
+ unsigned long name[BITS_TO_LONGS(bits)]
+#define BITMAP_TYPE unsigned long
+
+static inline void sxe2_set_bit(uint32_t nr, unsigned long *addr)
+{
+ addr[nr / __BITS_PER_LONG] |= 1UL << (nr % __BITS_PER_LONG);
+}
+
+static inline void sxe2_clear_bit(uint32_t nr, unsigned long *addr)
+{
+ addr[nr / __BITS_PER_LONG] &= ~(1UL << (nr % __BITS_PER_LONG));
+}
+
+static inline uint32_t sxe2_test_bit(uint32_t nr, const unsigned long *addr)
+{
+ return 1UL & (addr[BIT_WORD(nr)] >> (nr & (__BITS_PER_LONG-1)));
+}
+
+static inline uint32_t sxe2_bitmap_weight(const unsigned long *src, uint32_t nbits)
+{
+ uint32_t cnt = 0;
+ uint16_t i;
+
+ for (i = 0; i < nbits; i++) {
+ if (sxe2_test_bit(i, src))
+ cnt++;
+ }
+ return cnt;
+}
+
+static inline bool sxe2_bitmap_empty(const unsigned long *src, uint32_t nbits)
+{
+ uint16_t i;
+
+ for (i = 0; i < nbits; i++) {
+ if (sxe2_test_bit(i, src))
+ return false;
+ }
+ return true;
+}
+
+static inline void sxe2_bitmap_zero(unsigned long *dst, uint32_t nbits)
+{
+ uint32_t len = BITS_TO_LONGS(nbits) * sizeof(unsigned long);
+ memset(dst, 0, len);
+}
+
+static inline void sxe2_bitmap_copy(unsigned long *dst, const unsigned long *src,
+ uint32_t nbits)
+{
+ uint32_t len = BITS_TO_LONGS(nbits) * sizeof(unsigned long);
+ memcpy(dst, src, len);
+}
+
+#define sxe2_small_const_nbits(nbits) \
+ ((nbits) && (nbits) <= __BITS_PER_LONG && (nbits) > 0)
+
+#define SXE2_BITMAP_LAST_WORD_MASK(nbits) \
+ (((nbits) % __BITS_PER_LONG) ? \
+ ((1UL << ((nbits) % __BITS_PER_LONG)) - 1UL) : ~0UL)
+
+static inline bool
+__sxe2_bitmap_equal_generic(const unsigned long *src1,
+ const unsigned long *src2, uint32_t nbits)
+{
+ uint32_t i;
+ uint32_t words = nbits / __BITS_PER_LONG;
+ uint32_t trailing = nbits % __BITS_PER_LONG;
+
+ for (i = 0; i < words; i++) {
+ if (src1[i] != src2[i])
+ return false;
+ }
+
+ if (trailing) {
+ unsigned long mask = (1UL << trailing) - 1UL;
+ if ((src1[words] & mask) != (src2[words] & mask))
+ return false;
+ }
+
+ return true;
+}
+
+static inline bool
+sxe2_bitmap_equal(const unsigned long *src1,
+ const unsigned long *src2, uint32_t nbits)
+{
+ if (sxe2_small_const_nbits(nbits))
+ return !((*src1 ^ *src2) & SXE2_BITMAP_LAST_WORD_MASK(nbits));
+
+ if (__rte_constant(nbits & 7) && (nbits % 8 == 0))
+ return !memcmp(src1, src2, nbits / 8);
+
+ return __sxe2_bitmap_equal_generic(src1, src2, nbits);
+}
+
+#endif /* __SXE2_OSAL_H__ */
--
2.47.3
^ permalink raw reply related
* [PATCH v17 01/11] mailmap: add Jie Liu
From: liujie5 @ 2026-05-19 3:01 UTC (permalink / raw)
To: stephen; +Cc: dev, Jie Liu
In-Reply-To: <20260519030132.3780057-1-liujie5@linkdatatechnology.com>
From: Jie Liu <liujie5@linkdatatechnology.com>
Signed-off-by: Jie Liu <liujie5@linkdatatechnology.com>
---
.mailmap | 1 +
1 file changed, 1 insertion(+)
diff --git a/.mailmap b/.mailmap
index 895412e568..d2c4485636 100644
--- a/.mailmap
+++ b/.mailmap
@@ -739,6 +739,7 @@ Jiawen Wu <jiawenwu@trustnetic.com>
Jiayu Hu <hujiayu.hu@foxmail.com> <jiayu.hu@intel.com>
Jie Hai <haijie1@huawei.com>
Jie Liu <jie2.liu@hxt-semitech.com>
+Jie Liu <liujie5@linkdatatechnology.com>
Jie Pan <panjie5@jd.com>
Jie Wang <jie1x.wang@intel.com>
Jie Zhou <jizh@linux.microsoft.com> <jizh@microsoft.com>
--
2.47.3
^ permalink raw reply related
* Re: [PATCH dpdk v6] net/tap: use offsets provided by rte_net_get_ptype
From: Stephen Hemminger @ 2026-05-18 22:26 UTC (permalink / raw)
To: Robin Jarry; +Cc: dev
In-Reply-To: <20260518205448.142884-2-rjarry@redhat.com>
On Mon, 18 May 2026 22:54:49 +0200
Robin Jarry <rjarry@redhat.com> wrote:
> Instead of guessing what are the proper header lengths, pass
> a rte_net_hdr_lens struct to rte_net_get_ptype and use it to get the
> proper header lengths/offsets in tap_verify_csum.
>
> This allows supporting stacked VLAN/QinQ tags.
>
> Signed-off-by: Robin Jarry <rjarry@redhat.com>
> ---
Applied to next-net
^ permalink raw reply
* Re: [PATCH 2/4] ethdev: announce VMDq capability
From: Kishore Padmanabha @ 2026-05-18 22:12 UTC (permalink / raw)
To: David Marchand
Cc: dev, rjarry, cfontain, Ajit Khaparde, Bruce Richardson, Rosen Xu,
Anatoly Burakov, Vladimir Medvedkin, Jiawen Wu, Zaiyu Wang,
Thomas Monjalon, Andrew Rybchenko
In-Reply-To: <CAJFAV8wGDsx+SD4JerfduxVvOOj5KJ-FxXeLS-78JOZiAtTgDA@mail.gmail.com>
[-- Attachment #1.1: Type: text/plain, Size: 1380 bytes --]
On Wed, Apr 29, 2026 at 10:18 AM David Marchand <david.marchand@redhat.com>
wrote:
> On Tue, 7 Apr 2026 at 00:22, Kishore Padmanabha
> <kishore.padmanabha@broadcom.com> wrote:
> >> diff --git a/drivers/net/bnxt/bnxt_ethdev.c
> b/drivers/net/bnxt/bnxt_ethdev.c
> >> index b677f9491d..0f783b9e98 100644
> >> --- a/drivers/net/bnxt/bnxt_ethdev.c
> >> +++ b/drivers/net/bnxt/bnxt_ethdev.c
> >> @@ -1214,7 +1214,8 @@ static int bnxt_dev_info_get_op(struct
> rte_eth_dev *eth_dev,
> >>
> >> dev_info->speed_capa = bnxt_get_speed_capabilities(bp);
> >> dev_info->dev_capa = RTE_ETH_DEV_CAPA_RUNTIME_RX_QUEUE_SETUP |
> >> - RTE_ETH_DEV_CAPA_RUNTIME_TX_QUEUE_SETUP;
> >> + RTE_ETH_DEV_CAPA_RUNTIME_TX_QUEUE_SETUP |
> >> + RTE_ETH_DEV_CAPA_VMDQ;
> >
> > We have not been testing the VMDq feature for sometime, planning to
> deprecate this feature. Please remove this change.
>
> Currently, the driver supports VMDq:
> $ git grep -i vmdq doc/guides/nics/features/bnxt.ini
> doc/guides/nics/features/bnxt.ini:VMDq = Y
>
> I don't intend to own this feature drop with my series :-).
> Please announce such a deprecation and flag it properly.
>
> I will post another patch to remove VMDq feaure from bnxt driver.
>
> --
> David Marchand
>
>
[-- Attachment #1.2: Type: text/html, Size: 2118 bytes --]
[-- Attachment #2: S/MIME Cryptographic Signature --]
[-- Type: application/pkcs7-signature, Size: 5493 bytes --]
^ permalink raw reply
* [PATCH dpdk v6] net/tap: use offsets provided by rte_net_get_ptype
From: Robin Jarry @ 2026-05-18 20:54 UTC (permalink / raw)
To: dev, Stephen Hemminger
In-Reply-To: <20260422133615.680318-2-rjarry@redhat.com>
Instead of guessing what are the proper header lengths, pass
a rte_net_hdr_lens struct to rte_net_get_ptype and use it to get the
proper header lengths/offsets in tap_verify_csum.
This allows supporting stacked VLAN/QinQ tags.
Signed-off-by: Robin Jarry <rjarry@redhat.com>
---
Notes:
v6: Removed invalid claim about IPv6 ext support in commit message.
v5: Fixed invalid L4 checksum verification when there are IPv6 extension headers.
v4: Fixed stale comments.
v3: Fixed double count for ipv6 extension headers.
v2:
* Restored packet length checks in IPv4/IPv6 before checking L4
checksums.
* Moved struct rte_net_hdr_lens variable next to rte_net_get_ptype call
and initialize it to 0 instead of calling memset().
drivers/net/tap/rte_eth_tap.c | 41 +++++++++++++----------------------
1 file changed, 15 insertions(+), 26 deletions(-)
diff --git a/drivers/net/tap/rte_eth_tap.c b/drivers/net/tap/rte_eth_tap.c
index a5d460a0b3cb..3b8e19afb7af 100644
--- a/drivers/net/tap/rte_eth_tap.c
+++ b/drivers/net/tap/rte_eth_tap.c
@@ -327,52 +327,41 @@ tun_alloc(struct pmd_internals *pmd, int is_keepalive, int persistent)
}
static void
-tap_verify_csum(struct rte_mbuf *mbuf)
+tap_verify_csum(struct rte_mbuf *mbuf, const struct rte_net_hdr_lens *hlen)
{
- uint32_t l2 = mbuf->packet_type & RTE_PTYPE_L2_MASK;
uint32_t l3 = mbuf->packet_type & RTE_PTYPE_L3_MASK;
uint32_t l4 = mbuf->packet_type & RTE_PTYPE_L4_MASK;
- unsigned int l2_len = sizeof(struct rte_ether_hdr);
- unsigned int l3_len;
+ uint32_t l4_off = hlen->l2_len + hlen->l3_len;
uint16_t cksum = 0;
void *l3_hdr;
void *l4_hdr;
- struct rte_udp_hdr *udp_hdr;
- if (l2 == RTE_PTYPE_L2_ETHER_VLAN)
- l2_len += 4;
- else if (l2 == RTE_PTYPE_L2_ETHER_QINQ)
- l2_len += 8;
/* Don't verify checksum for packets with discontinuous L2 header */
- if (unlikely(l2_len + sizeof(struct rte_ipv4_hdr) >
- rte_pktmbuf_data_len(mbuf)))
+ if (unlikely(l4_off > rte_pktmbuf_data_len(mbuf)))
return;
- l3_hdr = rte_pktmbuf_mtod_offset(mbuf, void *, l2_len);
+
+ l3_hdr = rte_pktmbuf_mtod_offset(mbuf, void *, hlen->l2_len);
if (l3 == RTE_PTYPE_L3_IPV4 || l3 == RTE_PTYPE_L3_IPV4_EXT) {
struct rte_ipv4_hdr *iph = l3_hdr;
- l3_len = rte_ipv4_hdr_len(iph);
- if (unlikely(l2_len + l3_len > rte_pktmbuf_data_len(mbuf)))
- return;
/* check that the total length reported by header is not
* greater than the total received size
*/
- if (l2_len + rte_be_to_cpu_16(iph->total_length) >
+ if (hlen->l2_len + rte_be_to_cpu_16(iph->total_length) >
rte_pktmbuf_data_len(mbuf))
return;
- cksum = ~rte_raw_cksum(iph, l3_len);
+ cksum = ~rte_raw_cksum(iph, hlen->l3_len);
mbuf->ol_flags |= cksum ?
RTE_MBUF_F_RX_IP_CKSUM_BAD :
RTE_MBUF_F_RX_IP_CKSUM_GOOD;
} else if (l3 == RTE_PTYPE_L3_IPV6) {
struct rte_ipv6_hdr *iph = l3_hdr;
- l3_len = sizeof(struct rte_ipv6_hdr);
/* check that the total length reported by header is not
* greater than the total received size
*/
- if (l2_len + l3_len + rte_be_to_cpu_16(iph->payload_len) >
+ if (hlen->l2_len + sizeof(*iph) + rte_be_to_cpu_16(iph->payload_len) >
rte_pktmbuf_data_len(mbuf))
return;
} else {
@@ -386,20 +375,19 @@ tap_verify_csum(struct rte_mbuf *mbuf)
if (l4 == RTE_PTYPE_L4_UDP || l4 == RTE_PTYPE_L4_TCP) {
int cksum_ok;
- const unsigned int l4_min_len = (l4 == RTE_PTYPE_L4_UDP)
- ? sizeof(struct rte_udp_hdr) : sizeof(struct rte_tcp_hdr);
/* Don't verify checksum if L4 header is truncated */
- if (l2_len + l3_len + l4_min_len > rte_pktmbuf_data_len(mbuf))
+ if (l4_off + hlen->l4_len > rte_pktmbuf_data_len(mbuf))
return;
- l4_hdr = rte_pktmbuf_mtod_offset(mbuf, void *, l2_len + l3_len);
/* Don't verify checksum for multi-segment packets. */
if (mbuf->nb_segs > 1)
return;
+
+ l4_hdr = rte_pktmbuf_mtod_offset(mbuf, void *, l4_off);
if (l3 == RTE_PTYPE_L3_IPV4 || l3 == RTE_PTYPE_L3_IPV4_EXT) {
if (l4 == RTE_PTYPE_L4_UDP) {
- udp_hdr = (struct rte_udp_hdr *)l4_hdr;
+ struct rte_udp_hdr *udp_hdr = l4_hdr;
if (udp_hdr->dgram_cksum == 0) {
/*
* For IPv4, a zero UDP checksum
@@ -561,10 +549,11 @@ pmd_rx_burst(void *queue, struct rte_mbuf **bufs, uint16_t nb_pkts)
continue;
}
- mbuf->packet_type = rte_net_get_ptype(mbuf, NULL,
+ struct rte_net_hdr_lens hlen = {0};
+ mbuf->packet_type = rte_net_get_ptype(mbuf, &hlen,
RTE_PTYPE_ALL_MASK);
if (rxq->rxmode->offloads & RTE_ETH_RX_OFFLOAD_CHECKSUM)
- tap_verify_csum(mbuf);
+ tap_verify_csum(mbuf, &hlen);
/* account for the receive frame */
bufs[num_rx++] = mbuf;
--
2.54.0
^ permalink raw reply related
* [DPDK/ethdev Bug 1947] bnxt: rte_eth_stats_get returns invalid counter values on BCM57608
From: bugzilla @ 2026-05-18 18:52 UTC (permalink / raw)
To: dev
http://bugs.dpdk.org/show_bug.cgi?id=1947
Bug ID: 1947
Summary: bnxt: rte_eth_stats_get returns invalid counter values
on BCM57608
Product: DPDK
Version: 26.03
Hardware: All
OS: All
Status: UNCONFIRMED
Severity: normal
Priority: Normal
Component: ethdev
Assignee: dev@dpdk.org
Reporter: oleksandrn@interfacemasters.com
Target Milestone: ---
On BCM57608 rte_eth_stats_get occasionally returns a single statistic counter
with a value that doesn't make sense given the actual traffic sent. Only one
counter is bad per call; I have never seen two simultaneously, which maybe
suggests some sort of timing issue, and I don't think I even saw like 2 ports
of 6 have bad values, always one port one counter. (but maybe I need to do more
runs to see this, so maybe it doesn't mean anything)
Observed 2 cases:
1. A counter returns an exact power of 2 that is far beyond what the traffic
could produce in a short timeframe. For example, after 60 seconds of 6×100 Gbps
traffic, we observe ierrors = 70368744177664 = 2^46 or 140737488355328 =
2^47 on single port.
2. ipackets is much larger than ibytes, which doesn't make sense, and should
be impossible.
Example for case 1:
ipackets 1423589410
opackets 1423588386
ibytes 1452060153720
obytes 1469240022904
imissed 229789201
ierrors 70368744177664 <<<<< 2^46
oerrors 0
rx_nombuf 0
Additionally, rte_eth_stats_reset / rte_eth_xstats_reset do not prevent
recurrence.
There is also a suspected issue with stats reset itself: running a second
60-second traffic burst after reset shows more packets reported by the NIC than
the traffic generator actually sent, suggesting the hardware counters are not
actually cleared, but I haven't investigated this enough to give more details.
Rough reproduction steps (we did this testing with our application, but
haven't seen anything similar for other NICs we use, so it shouldn't matter
much):
1. Initialize port with BCM57608 NIC (we have 10rx queues per port)
2. Optionally call rte_eth_stats_reset after port init
3. Send 6×100 Gbps traffic for 60 seconds (1024bytes per packet in this case,
but I don't think it should matter)
4. Call rte_eth_stats_get -> observe inconsistent values sometimes
Seems to be the same with 25.11 and 26.03
NIC :
Part Number : BCM957608-P2100GQF20
Chip Number : BCM57608
Chip Name : THOR2
Firmware Version : 236.1.153.0
RoCE Firmware Version : 236.1.153.0
HWRM Interface Spec : 1.10.3
Active Package Version : 236.1.155.0
Package Version on NVM : 236.1.155.0
Active NVM config version : 236.0.2
--
You are receiving this mail because:
You are the assignee for the bug.
^ permalink raw reply
* Re: [PATCH v2 0/6] net/gve: add hardware timestamping support
From: Mark Blasko @ 2026-05-18 18:43 UTC (permalink / raw)
To: Stephen Hemminger; +Cc: dev
In-Reply-To: <20260517161545.2b33289a@phoenix.local>
On Sun, May 17, 2026 at 4:15 PM Stephen Hemminger
<stephen@networkplumber.org> wrote:
>
> Warning: (unchanged from v1) gve_read_clock and the periodic
> gve_read_nic_clock alarm callback both issue
> GVE_ADMINQ_REPORT_NIC_TIMESTAMP into the single shared DMA buffer
> priv->nic_ts_report, then read it after gve_adminq_execute_cmd
> has released adminq_lock. If gve_read_clock is preempted between
> gve_adminq_report_nic_timestamp returning and the be64_to_cpu
> read, the alarm callback can memset() and reissue its own
> command, so the user thread will read either zero or another
> command's response. The simplest fix is for gve_read_clock to
> return the cached priv->last_read_nic_timestamp instead of
> issuing a fresh adminq command - the 250ms periodic sync keeps
> it fresh enough for .read_clock semantics. Once the dev_op
> registration is restored this race becomes reachable.
I want to make sure I fully understand the API contract here. Is the
fact that .read_clock does not require a fresh device read documented
in the DPDK specification, or is this based on typical application
use cases? If the latter, what are those typical use cases?
^ permalink raw reply
* [PATCH] dma/ae4dma: add AMD AE4DMA DMA PMD
From: Raghavendra Ningoji @ 2026-05-18 18:18 UTC (permalink / raw)
To: dev; +Cc: thomas, rjarry, Bhagyada.Modali, Selwin.Sebastian,
Raghavendra Ningoji
Add a new dmadev poll-mode driver for the AMD AE4DMA hardware DMA
engine. An AE4DMA engine exposes 16 hardware command queues, each
with a 32-entry descriptor ring; the PMD maps each hardware channel
to its own dmadev with a single virtual channel, so a PCI function
appears as 16 dmadevs named "<pci-bdf>-ch0" .. "<pci-bdf>-ch15".
Driver characteristics:
- Memory-to-memory copy operations only (RTE_DMA_CAPA_MEM_TO_MEM).
- Completion is detected via the hardware's per-queue read_idx
register, which the engine advances as it processes descriptors.
The descriptor status / err_code bytes are read only to classify
each drained slot as success or failure.
- vchan_status reports IDLE/ACTIVE based on HW read_idx vs write_idx
and HALTED_ERROR when the queue is not enabled.
- depends on bus_pci and dmadev.
Signed-off-by: Raghavendra Ningoji <raghavendra.ningoji@amd.com>
---
MAINTAINERS | 5 +
doc/guides/dmadevs/ae4dma.rst | 75 +++
doc/guides/dmadevs/index.rst | 1 +
doc/guides/rel_notes/release_26_07.rst | 7 +
drivers/dma/ae4dma/ae4dma_dmadev.c | 742 +++++++++++++++++++++++++
drivers/dma/ae4dma/ae4dma_hw_defs.h | 164 ++++++
drivers/dma/ae4dma/ae4dma_internal.h | 117 ++++
drivers/dma/ae4dma/meson.build | 7 +
drivers/dma/meson.build | 1 +
usertools/dpdk-devbind.py | 5 +-
10 files changed, 1123 insertions(+), 1 deletion(-)
create mode 100644 doc/guides/dmadevs/ae4dma.rst
create mode 100644 drivers/dma/ae4dma/ae4dma_dmadev.c
create mode 100644 drivers/dma/ae4dma/ae4dma_hw_defs.h
create mode 100644 drivers/dma/ae4dma/ae4dma_internal.h
create mode 100644 drivers/dma/ae4dma/meson.build
diff --git a/MAINTAINERS b/MAINTAINERS
index 9143d028bc..0b5a6e08d8 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1361,6 +1361,11 @@ F: doc/guides/compressdevs/features/zsda.ini
DMAdev Drivers
--------------
+AMD AE4DMA
+M: Bhagyada Modali <Bhagyada.Modali@amd.com>
+F: drivers/dma/ae4dma/
+F: doc/guides/dmadevs/ae4dma.rst
+
Intel IDXD - EXPERIMENTAL
M: Bruce Richardson <bruce.richardson@intel.com>
M: Kevin Laatz <kevin.laatz@intel.com>
diff --git a/doc/guides/dmadevs/ae4dma.rst b/doc/guides/dmadevs/ae4dma.rst
new file mode 100644
index 0000000000..37a2096ccf
--- /dev/null
+++ b/doc/guides/dmadevs/ae4dma.rst
@@ -0,0 +1,75 @@
+.. SPDX-License-Identifier: BSD-3-Clause
+ Copyright(c) 2025 Advanced Micro Devices, Inc.
+
+.. include:: <isonum.txt>
+
+AMD AE4DMA DMA Device Driver
+============================
+
+The ``ae4dma`` dmadev driver is a poll-mode driver (PMD) for the
+AMD AE4DMA hardware DMA engine. The engine exposes 16 independent
+hardware command queues, each with a ring of 32 descriptors. The PMD
+maps each hardware command queue to a separate DPDK dmadev with a
+single virtual channel, so a single PCI function appears as 16 dmadevs
+named ``<pci-bdf>-ch0`` through ``<pci-bdf>-ch15``.
+
+The driver supports memory-to-memory copy operations only.
+
+Hardware Requirements
+---------------------
+
+The ``dpdk-devbind.py`` script can be used to list AE4DMA devices on
+the system::
+
+ dpdk-devbind.py --status-dev dma
+
+AE4DMA devices appear with vendor ID ``0x1022`` and device ID
+``0x149b``.
+
+Compilation
+-----------
+
+The driver is built as part of the standard DPDK build on x86 platforms
+using ``meson`` and ``ninja``; no extra configuration is required.
+
+Device Setup
+------------
+
+The AE4DMA device must be bound to a DPDK-compatible kernel module such
+as ``vfio-pci`` before it can be used::
+
+ dpdk-devbind.py -b vfio-pci <pci-bdf>
+
+Initialization
+~~~~~~~~~~~~~~
+
+On probe the PMD performs the following steps for each PCI function:
+
+* Reads BAR0 and programs the common configuration register with the
+ number of hardware queues to enable (16).
+* For each hardware queue it allocates a 32-entry descriptor ring in
+ IOVA-contiguous memory, programs the queue base address and ring
+ depth into the per-queue registers, and enables the queue.
+* Interrupts are masked; completion is polled by the application.
+
+Usage
+-----
+
+Once a dmadev has been started, copies are submitted with
+``rte_dma_copy()`` and completions are reaped with ``rte_dma_completed()``
+or ``rte_dma_completed_status()``. See the
+:ref:`Enqueue / Dequeue API <dmadev_enqueue_dequeue>` section of the
+dmadev library documentation for details.
+
+Limitations
+-----------
+
+* Only memory-to-memory copies are supported. Fill, scatter-gather and
+ any other operation types are not advertised in
+ ``rte_dma_info::dev_capa``.
+* The maximum number of descriptors per virtual channel is fixed by
+ hardware at 32. The PMD rounds the requested ring size up to a
+ power of two and clamps it to 32.
+* Only a single virtual channel per dmadev is supported; use the 16
+ per-PCI-function dmadevs to obtain channel-level parallelism.
+* Interrupt-driven completion is not supported.
diff --git a/doc/guides/dmadevs/index.rst b/doc/guides/dmadevs/index.rst
index 56beb1733f..97399590f6 100644
--- a/doc/guides/dmadevs/index.rst
+++ b/doc/guides/dmadevs/index.rst
@@ -11,6 +11,7 @@ an application through DMA API.
:maxdepth: 1
:numbered:
+ ae4dma
cnxk
dpaa
dpaa2
diff --git a/doc/guides/rel_notes/release_26_07.rst b/doc/guides/rel_notes/release_26_07.rst
index f012d47a4b..9a78a7ef62 100644
--- a/doc/guides/rel_notes/release_26_07.rst
+++ b/doc/guides/rel_notes/release_26_07.rst
@@ -63,6 +63,13 @@ New Features
``rte_eal_init`` and the application is responsible for probing each device,
* ``--auto-probing`` enables the initial bus probing, which is the current default behavior.
+* **Added AMD AE4DMA DMA PMD.**
+
+ Added a new ``dma/ae4dma`` driver for the AMD AE4DMA hardware DMA engine.
+ Each PCI function exposes 16 hardware command queues; the PMD registers one
+ dmadev per channel with a single virtual channel and supports
+ memory-to-memory copy operations.
+
Removed Items
-------------
diff --git a/drivers/dma/ae4dma/ae4dma_dmadev.c b/drivers/dma/ae4dma/ae4dma_dmadev.c
new file mode 100644
index 0000000000..eb6ea88f55
--- /dev/null
+++ b/drivers/dma/ae4dma/ae4dma_dmadev.c
@@ -0,0 +1,742 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2021-2025 Advanced Micro Devices, Inc. All rights reserved.
+ */
+
+#include <errno.h>
+#include <inttypes.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <rte_bus_pci.h>
+#include <bus_pci_driver.h>
+#include <rte_dmadev_pmd.h>
+#include <rte_malloc.h>
+
+#include "ae4dma_internal.h"
+
+/*
+ * One dmadev per AE4DMA hardware channel; each dmadev has exactly one
+ * virtual channel. The HW's per-queue register block must be densely
+ * packed right after the engine-common config register at BAR0+0; the
+ * build-time check below catches an accidental layout change.
+ */
+static_assert(sizeof(struct ae4dma_hwq_regs) == 32,
+ "ae4dma_hwq_regs stride changed; per-queue offset math will break");
+
+RTE_LOG_REGISTER_DEFAULT(ae4dma_pmd_logtype, INFO);
+
+#define AE4DMA_PMD_NAME dmadev_ae4dma
+#define AE4DMA_PMD_NAME_STR RTE_STR(AE4DMA_PMD_NAME)
+
+static const struct rte_memzone *
+ae4dma_queue_dma_zone_reserve(const char *queue_name,
+ uint32_t queue_size, int socket_id)
+{
+ const struct rte_memzone *mz;
+
+ mz = rte_memzone_lookup(queue_name);
+ if (mz != 0) {
+ if (((size_t)queue_size <= mz->len) &&
+ ((socket_id == SOCKET_ID_ANY) ||
+ (socket_id == mz->socket_id))) {
+ AE4DMA_PMD_INFO("re-use memzone already "
+ "allocated for %s", queue_name);
+ return mz;
+ }
+ AE4DMA_PMD_ERR("Incompatible memzone already "
+ "allocated %s, size %u, socket %d. "
+ "Requested size %u, socket %u",
+ queue_name, (uint32_t)mz->len,
+ mz->socket_id, queue_size, socket_id);
+ return NULL;
+ }
+ return rte_memzone_reserve_aligned(queue_name, queue_size,
+ socket_id, RTE_MEMZONE_IOVA_CONTIG, queue_size);
+}
+
+/* Configure a device. */
+static int
+ae4dma_dev_configure(struct rte_dma_dev *dev __rte_unused,
+ const struct rte_dma_conf *dev_conf,
+ uint32_t conf_sz)
+{
+ if (sizeof(struct rte_dma_conf) != conf_sz)
+ return -EINVAL;
+
+ if (dev_conf->nb_vchans != 1)
+ return -EINVAL;
+
+ return 0;
+}
+
+/* Setup a virtual channel for AE4DMA, only 1 vchan is supported per dmadev. */
+static int
+ae4dma_vchan_setup(struct rte_dma_dev *dev, uint16_t vchan __rte_unused,
+ const struct rte_dma_vchan_conf *qconf, uint32_t qconf_sz)
+{
+ struct ae4dma_dmadev *ae4dma = dev->fp_obj->dev_private;
+ struct ae4dma_cmd_queue *cmd_q = &ae4dma->cmd_q;
+ uint16_t max_desc = qconf->nb_desc;
+
+ if (sizeof(struct rte_dma_vchan_conf) != qconf_sz)
+ return -EINVAL;
+
+ if (max_desc < 2)
+ return -EINVAL;
+
+ if (!rte_is_power_of_2(max_desc))
+ max_desc = rte_align32pow2(max_desc);
+
+ if (max_desc > AE4DMA_DESCRIPTORS_PER_CMDQ) {
+ AE4DMA_PMD_DEBUG("DMA dev %u nb_desc clamped to %u",
+ dev->data->dev_id, AE4DMA_DESCRIPTORS_PER_CMDQ);
+ max_desc = AE4DMA_DESCRIPTORS_PER_CMDQ;
+ }
+
+ cmd_q->qcfg = *qconf;
+ cmd_q->qcfg.nb_desc = max_desc;
+
+ /* Ensure all counters are reset, if reconfiguring/restarting device. */
+ memset(&cmd_q->stats, 0, sizeof(cmd_q->stats));
+ return 0;
+}
+
+/* Start a configured device. */
+static int
+ae4dma_dev_start(struct rte_dma_dev *dev)
+{
+ struct ae4dma_dmadev *ae4dma = dev->fp_obj->dev_private;
+ struct ae4dma_cmd_queue *cmd_q = &ae4dma->cmd_q;
+ uint16_t nb = cmd_q->qcfg.nb_desc;
+
+ if (nb == 0)
+ return -EBUSY;
+
+ /* Program ring depth expected by hardware. */
+ AE4DMA_WRITE_REG(&cmd_q->hwq_regs->max_idx, nb);
+ return 0;
+}
+
+/* Stop a configured device. */
+static int
+ae4dma_dev_stop(struct rte_dma_dev *dev)
+{
+ struct ae4dma_dmadev *ae4dma = dev->fp_obj->dev_private;
+ struct ae4dma_cmd_queue *cmd_q = &ae4dma->cmd_q;
+
+ if (cmd_q->hwq_regs != NULL)
+ AE4DMA_WRITE_REG(&cmd_q->hwq_regs->control_reg.control_raw,
+ AE4DMA_CMD_QUEUE_DISABLE);
+ return 0;
+}
+
+/* Get device information of a device. */
+static int
+ae4dma_dev_info_get(const struct rte_dma_dev *dev, struct rte_dma_info *info,
+ uint32_t size)
+{
+ if (size < sizeof(*info))
+ return -EINVAL;
+ info->dev_name = dev->device->name;
+ info->dev_capa = RTE_DMA_CAPA_MEM_TO_MEM;
+ info->max_vchans = 1;
+ info->min_desc = 2;
+ info->max_desc = AE4DMA_DESCRIPTORS_PER_CMDQ;
+ info->nb_vchans = 1;
+ return 0;
+}
+
+/* Close a configured device. */
+static int
+ae4dma_dev_close(struct rte_dma_dev *dev)
+{
+ struct ae4dma_dmadev *ae4dma = dev->fp_obj->dev_private;
+ struct ae4dma_cmd_queue *cmd_q = &ae4dma->cmd_q;
+
+ if (cmd_q->hwq_regs != NULL)
+ AE4DMA_WRITE_REG(&cmd_q->hwq_regs->control_reg.control_raw,
+ AE4DMA_CMD_QUEUE_DISABLE);
+
+ if (cmd_q->memz_name[0] != '\0') {
+ const struct rte_memzone *mz = rte_memzone_lookup(cmd_q->memz_name);
+
+ if (mz != NULL)
+ rte_memzone_free(mz);
+ }
+ cmd_q->qbase_desc = NULL;
+ cmd_q->qbase_addr = NULL;
+ cmd_q->qbase_phys_addr = 0;
+ return 0;
+}
+
+/* trigger h/w to process enqued desc:doorbell - by next_write */
+static inline void
+__submit(struct ae4dma_dmadev *ae4dma)
+{
+ struct ae4dma_cmd_queue *cmd_q = &ae4dma->cmd_q;
+ uint16_t write_idx = cmd_q->next_write;
+ uint16_t nb = cmd_q->qcfg.nb_desc;
+
+ AE4DMA_WRITE_REG(&cmd_q->hwq_regs->write_idx, write_idx);
+ if (nb != 0)
+ cmd_q->stats.submitted += (uint16_t)((cmd_q->next_write - cmd_q->last_write +
+ nb) % nb);
+ cmd_q->last_write = cmd_q->next_write;
+}
+
+static int
+ae4dma_submit(void *dev_private, uint16_t vchan __rte_unused)
+{
+ struct ae4dma_dmadev *ae4dma = dev_private;
+
+ __submit(ae4dma);
+ return 0;
+}
+
+/* Write descriptor for enqueue (copy only). */
+static inline int
+__write_desc_copy(void *dev_private, rte_iova_t src, phys_addr_t dst,
+ uint32_t len, uint64_t flags)
+{
+ struct ae4dma_dmadev *ae4dma = dev_private;
+ struct ae4dma_cmd_queue *cmd_q = &ae4dma->cmd_q;
+ struct ae4dma_desc *dma_desc;
+ uint16_t ret;
+ uint16_t nb = cmd_q->qcfg.nb_desc;
+ uint16_t write = cmd_q->next_write;
+
+ if (nb == 0)
+ return -EINVAL;
+
+ /* Reserve one slot to distinguish full from empty (power-of-two ring). */
+ if ((uint32_t)cmd_q->ring_buff_count >= (uint32_t)(nb - 1))
+ return -ENOSPC;
+
+ dma_desc = &cmd_q->qbase_desc[write];
+ memset(dma_desc, 0, sizeof(*dma_desc));
+ dma_desc->length = len;
+ dma_desc->src_hi = upper_32_bits(src);
+ dma_desc->src_lo = lower_32_bits(src);
+ dma_desc->dst_hi = upper_32_bits(dst);
+ dma_desc->dst_lo = lower_32_bits(dst);
+ cmd_q->ring_buff_count++;
+ cmd_q->next_write = (uint16_t)((write + 1) % nb);
+ ret = write;
+ if (flags & RTE_DMA_OP_FLAG_SUBMIT)
+ __submit(ae4dma);
+ return ret;
+}
+
+/* Enqueue a copy operation onto the ae4dma device. */
+static int
+ae4dma_enqueue_copy(void *dev_private, uint16_t vchan __rte_unused,
+ rte_iova_t src, rte_iova_t dst, uint32_t length, uint64_t flags)
+{
+ return __write_desc_copy(dev_private, src, dst, length, flags);
+}
+
+/* Dump DMA device info. */
+static int
+ae4dma_dev_dump(const struct rte_dma_dev *dev, FILE *f)
+{
+ struct ae4dma_dmadev *ae4dma = dev->fp_obj->dev_private;
+ struct ae4dma_cmd_queue *cmd_q;
+ void *ae4dma_mmio_base_addr = (uint8_t *)ae4dma->io_regs;
+
+ cmd_q = &ae4dma->cmd_q;
+ fprintf(f, "cmd_q->id = %" PRIx64 "\n", cmd_q->id);
+ fprintf(f, "cmd_q->qidx = %" PRIx64 "\n", cmd_q->qidx);
+ fprintf(f, "cmd_q->qsize = %" PRIx64 "\n", cmd_q->qsize);
+ fprintf(f, "mmio_base_addr = %p\n", ae4dma_mmio_base_addr);
+ fprintf(f, "queues per ae4dma engine = %d\n", AE4DMA_READ_REG_OFFSET(
+ ae4dma_mmio_base_addr, AE4DMA_COMMON_CONFIG_OFFSET));
+ fprintf(f, "== Private Data ==\n");
+ fprintf(f, " Config: { ring_size: %u }\n", cmd_q->qcfg.nb_desc);
+ fprintf(f, " Ring virt: %p\tphys: %#" PRIx64 "\n",
+ (void *)cmd_q->qbase_desc,
+ (uint64_t)cmd_q->qbase_phys_addr);
+ fprintf(f, " Next write: %u\n", cmd_q->next_write);
+ fprintf(f, " Next read: %u\n", cmd_q->next_read);
+ fprintf(f, " current queue depth: %u\n", cmd_q->ring_buff_count);
+ fprintf(f, " }\n");
+ fprintf(f, " Key Stats { submitted: %" PRIu64 ", comp: %" PRIu64 ", failed: %" PRIu64 " }\n",
+ cmd_q->stats.submitted,
+ cmd_q->stats.completed,
+ cmd_q->stats.errors);
+ return 0;
+}
+
+/* Translates AE4DMA ChanERRs to DMA error codes. */
+static inline enum rte_dma_status_code
+__translate_status_ae4dma_to_dma(enum ae4dma_dma_err status)
+{
+ AE4DMA_PMD_DEBUG("ae4dma desc status = %d", status);
+
+ switch (status) {
+ case AE4DMA_DMA_ERR_NO_ERR:
+ return RTE_DMA_STATUS_SUCCESSFUL;
+ case AE4DMA_DMA_ERR_INV_LEN:
+ return RTE_DMA_STATUS_INVALID_LENGTH;
+ case AE4DMA_DMA_ERR_INV_SRC:
+ return RTE_DMA_STATUS_INVALID_SRC_ADDR;
+ case AE4DMA_DMA_ERR_INV_DST:
+ return RTE_DMA_STATUS_INVALID_DST_ADDR;
+ case AE4DMA_DMA_ERR_INV_ALIGN:
+ /* Name matches DPDK public enum spelling. */
+ return RTE_DMA_STATUS_DATA_POISION;
+ case AE4DMA_DMA_ERR_INV_HEADER:
+ case AE4DMA_DMA_ERR_INV_STATUS:
+ return RTE_DMA_STATUS_ERROR_UNKNOWN;
+ default:
+ return RTE_DMA_STATUS_ERROR_UNKNOWN;
+ }
+}
+
+/*
+ * Scan HW queue for completed descriptors (non-blocking).
+ *
+ * The AE4DMA engine signals completion by advancing the per-queue
+ * `read_idx` register; it does not (reliably) write a status value
+ * back into the descriptor. We therefore use the HW `read_idx`
+ * register as the source of truth and only inspect the descriptor's
+ * `dw1.err_code` byte to classify each completion as success or
+ * failure.
+ *
+ * @param cmd_q
+ * The AE4DMA command queue.
+ * @param max_ops
+ * Maximum descriptors to process this call.
+ * @param[out] failed_count
+ * Number of completed descriptors that did not report success.
+ * @return
+ * Number of descriptors completed (success + failure), <= max_ops.
+ */
+static inline uint16_t
+ae4dma_scan_hwq(struct ae4dma_cmd_queue *cmd_q, uint16_t max_ops,
+ uint16_t *failed_count)
+{
+ volatile struct ae4dma_desc *hw_desc;
+ uint16_t events_count = 0, fails = 0;
+ uint16_t tail;
+ uint16_t nb = cmd_q->qcfg.nb_desc;
+ uint16_t mask;
+ uint16_t hw_read_idx;
+ uint16_t in_flight;
+ uint16_t scan_cap;
+
+ if (nb == 0 || cmd_q->ring_buff_count == 0) {
+ *failed_count = 0;
+ return 0;
+ }
+ mask = nb - 1;
+
+ hw_read_idx = (uint16_t)(AE4DMA_READ_REG(&cmd_q->hwq_regs->read_idx) & mask);
+ tail = cmd_q->next_read;
+
+ /*
+ * Descriptors completed since our last visit live in the
+ * half-open ring range [tail, hw_read_idx). If HW hasn't
+ * moved we have nothing to do.
+ */
+ in_flight = (uint16_t)((hw_read_idx - tail) & mask);
+ if (in_flight == 0) {
+ *failed_count = 0;
+ return 0;
+ }
+
+ scan_cap = max_ops;
+ if (scan_cap > AE4DMA_DESCRIPTORS_PER_CMDQ)
+ scan_cap = AE4DMA_DESCRIPTORS_PER_CMDQ;
+ if (scan_cap > in_flight)
+ scan_cap = in_flight;
+ if (scan_cap > cmd_q->ring_buff_count)
+ scan_cap = (uint16_t)cmd_q->ring_buff_count;
+
+ while (events_count < scan_cap) {
+ uint8_t hw_status;
+ uint8_t hw_err;
+
+ hw_desc = &cmd_q->qbase_desc[tail];
+ hw_status = hw_desc->dw1.status;
+ hw_err = hw_desc->dw1.err_code;
+
+ /*
+ * read_idx advancing is the definitive completion
+ * signal. The per-descriptor status byte is informational
+ * and may not yet be written when we observe it:
+ *
+ * AE4DMA_DMA_DESC_ERROR (4)
+ * Hard failure - err_code names the precise cause.
+ * AE4DMA_DMA_DESC_COMPLETED (3) or 0
+ * Success.
+ * AE4DMA_DMA_DESC_VALIDATED (1) / _PROCESSED (2)
+ * Benign race: HW had not finished updating the
+ * status byte at the instant we read it. Since
+ * read_idx has moved past this slot, treat it as
+ * success unless err_code says otherwise.
+ *
+ * A non-zero err_code is treated as a failure regardless
+ * of the observed status value.
+ */
+ if (hw_status == AE4DMA_DMA_DESC_ERROR ||
+ hw_err != AE4DMA_DMA_ERR_NO_ERR) {
+ fails++;
+ AE4DMA_PMD_WARN("Desc failed: status=%u err=%u",
+ hw_status, hw_err);
+ }
+ cmd_q->status[events_count] = (enum ae4dma_dma_err)hw_err;
+ cmd_q->ring_buff_count--;
+ events_count++;
+ tail = (tail + 1) & mask;
+ }
+
+ cmd_q->stats.completed += events_count;
+ cmd_q->stats.errors += fails;
+ cmd_q->next_read = tail;
+ *failed_count = fails;
+ return events_count;
+}
+
+/* Returns successful operations count and sets error flag if any errors. */
+static uint16_t
+ae4dma_completed(void *dev_private, uint16_t vchan __rte_unused,
+ const uint16_t max_ops, uint16_t *last_idx, bool *has_error)
+{
+ struct ae4dma_dmadev *ae4dma = dev_private;
+ struct ae4dma_cmd_queue *cmd_q = &ae4dma->cmd_q;
+ uint16_t cpl_count, sl_count;
+ uint16_t err_count = 0;
+ uint16_t nb = cmd_q->qcfg.nb_desc;
+
+ *has_error = false;
+
+ cpl_count = ae4dma_scan_hwq(cmd_q, max_ops, &err_count);
+
+ if (cpl_count > max_ops)
+ cpl_count = max_ops;
+
+ if (cpl_count > 0 && last_idx != NULL)
+ *last_idx = (uint16_t)((cmd_q->next_read - 1 + nb) % nb);
+
+ sl_count = cpl_count - err_count;
+ if (err_count)
+ *has_error = true;
+
+ return sl_count;
+}
+
+static uint16_t
+ae4dma_completed_status(void *dev_private, uint16_t vchan __rte_unused,
+ uint16_t max_ops, uint16_t *last_idx,
+ enum rte_dma_status_code *status)
+{
+ struct ae4dma_dmadev *ae4dma = dev_private;
+ struct ae4dma_cmd_queue *cmd_q = &ae4dma->cmd_q;
+ uint16_t cpl_count;
+ uint16_t i;
+ uint16_t err_count = 0;
+ uint16_t nb = cmd_q->qcfg.nb_desc;
+
+ cpl_count = ae4dma_scan_hwq(cmd_q, max_ops, &err_count);
+
+ if (cpl_count > max_ops)
+ cpl_count = max_ops;
+
+ if (cpl_count > 0 && last_idx != NULL)
+ *last_idx = (uint16_t)((cmd_q->next_read - 1 + nb) % nb);
+
+ if (likely(err_count == 0)) {
+ for (i = 0; i < cpl_count; i++)
+ status[i] = RTE_DMA_STATUS_SUCCESSFUL;
+ } else {
+ for (i = 0; i < cpl_count; i++)
+ status[i] = __translate_status_ae4dma_to_dma(cmd_q->status[i]);
+ }
+
+ return cpl_count;
+}
+
+/* Get the remaining capacity of the ring. */
+static uint16_t
+ae4dma_burst_capacity(const void *dev_private, uint16_t vchan __rte_unused)
+{
+ const struct ae4dma_dmadev *ae4dma = dev_private;
+ const struct ae4dma_cmd_queue *cmd_q = &ae4dma->cmd_q;
+ uint16_t nb = cmd_q->qcfg.nb_desc;
+ uint16_t mask;
+ uint16_t read_idx = cmd_q->next_read;
+ uint16_t write_idx = cmd_q->next_write;
+ uint16_t used;
+
+ if (nb < 2 || !rte_is_power_of_2(nb))
+ return 0;
+
+ mask = nb - 1;
+ used = (uint16_t)((write_idx - read_idx) & mask);
+ /* One slot reserved (same rule as enqueue). */
+ if (used >= nb - 1)
+ return 0;
+ return (uint16_t)(nb - 1 - used);
+}
+
+/* Retrieve the generic stats of a DMA device. */
+static int
+ae4dma_stats_get(const struct rte_dma_dev *dev, uint16_t vchan __rte_unused,
+ struct rte_dma_stats *rte_stats, uint32_t size)
+{
+ const struct ae4dma_dmadev *ae4dma = dev->fp_obj->dev_private;
+ const struct ae4dma_cmd_queue *cmd_q = &ae4dma->cmd_q;
+ const struct rte_dma_stats *stats = &cmd_q->stats;
+
+ if (size < sizeof(*rte_stats))
+ return -EINVAL;
+ if (rte_stats == NULL)
+ return -EINVAL;
+
+ *rte_stats = *stats;
+ return 0;
+}
+
+/* Reset the generic stat counters for the DMA device. */
+static int
+ae4dma_stats_reset(struct rte_dma_dev *dev, uint16_t vchan __rte_unused)
+{
+ struct ae4dma_dmadev *ae4dma = dev->fp_obj->dev_private;
+ struct ae4dma_cmd_queue *cmd_q = &ae4dma->cmd_q;
+
+ memset(&cmd_q->stats, 0, sizeof(cmd_q->stats));
+ return 0;
+}
+
+/*
+ * Report channel state to the dmadev framework.
+ *
+ * RTE_DMA_VCHAN_HALTED_ERROR - HW queue is disabled (never started, or
+ * stopped via dev_stop()).
+ * RTE_DMA_VCHAN_IDLE - HW has caught up: read_idx == write_idx,
+ * no descriptors in flight.
+ * RTE_DMA_VCHAN_ACTIVE - HW still has descriptors to process.
+ */
+static int
+ae4dma_vchan_status(const struct rte_dma_dev *dev, uint16_t vchan __rte_unused,
+ enum rte_dma_vchan_status *status)
+{
+ const struct ae4dma_dmadev *ae4dma = dev->fp_obj->dev_private;
+ const struct ae4dma_cmd_queue *cmd_q = &ae4dma->cmd_q;
+ uint32_t ctrl, hw_read, hw_write;
+
+ if (cmd_q->hwq_regs == NULL) {
+ *status = RTE_DMA_VCHAN_HALTED_ERROR;
+ return 0;
+ }
+
+ ctrl = AE4DMA_READ_REG(&cmd_q->hwq_regs->control_reg.control_raw);
+ if ((ctrl & AE4DMA_CMD_QUEUE_ENABLE) == 0) {
+ *status = RTE_DMA_VCHAN_HALTED_ERROR;
+ return 0;
+ }
+
+ hw_read = AE4DMA_READ_REG(&cmd_q->hwq_regs->read_idx);
+ hw_write = AE4DMA_READ_REG(&cmd_q->hwq_regs->write_idx);
+
+ *status = (hw_read == hw_write) ? RTE_DMA_VCHAN_IDLE
+ : RTE_DMA_VCHAN_ACTIVE;
+ return 0;
+}
+
+static int
+ae4dma_add_queue(struct ae4dma_dmadev *dev, uint8_t qn, const char *pci_name)
+{
+ uint32_t dma_addr_lo, dma_addr_hi;
+ struct ae4dma_cmd_queue *cmd_q;
+ const struct rte_memzone *q_mz;
+
+ if (dev == NULL)
+ return -EINVAL;
+
+ dev->io_regs = dev->pci->mem_resource[AE4DMA_PCIE_BAR].addr;
+
+ cmd_q = &dev->cmd_q;
+ cmd_q->id = qn;
+ cmd_q->qidx = 0;
+ cmd_q->qsize = AE4DMA_QUEUE_SIZE(AE4DMA_QUEUE_DESC_SIZE);
+ cmd_q->hwq_regs = (volatile struct ae4dma_hwq_regs *)dev->io_regs + (qn + 1);
+
+ /*
+ * Memzone name must be globally unique. Embed PCI BDF so multiple
+ * PCI functions probed concurrently don't collide.
+ */
+ snprintf(cmd_q->memz_name, sizeof(cmd_q->memz_name),
+ "ae4dma_%s_q%u", pci_name, (unsigned int)qn);
+
+ q_mz = ae4dma_queue_dma_zone_reserve(cmd_q->memz_name,
+ cmd_q->qsize, rte_socket_id());
+ if (q_mz == NULL) {
+ AE4DMA_PMD_ERR("memzone reserve failed for %s", cmd_q->memz_name);
+ return -ENOMEM;
+ }
+
+ cmd_q->qbase_addr = (void *)q_mz->addr;
+ cmd_q->qbase_desc = (struct ae4dma_desc *)q_mz->addr;
+ cmd_q->qbase_phys_addr = q_mz->iova;
+
+ AE4DMA_WRITE_REG(&cmd_q->hwq_regs->max_idx, AE4DMA_DESCRIPTORS_PER_CMDQ);
+ AE4DMA_WRITE_REG(&cmd_q->hwq_regs->control_reg.control_raw,
+ AE4DMA_CMD_QUEUE_ENABLE);
+ AE4DMA_WRITE_REG(&cmd_q->hwq_regs->intr_status_reg.intr_status_raw,
+ AE4DMA_DISABLE_INTR);
+ cmd_q->next_write = (uint16_t)AE4DMA_READ_REG(&cmd_q->hwq_regs->write_idx);
+ cmd_q->next_read = (uint16_t)AE4DMA_READ_REG(&cmd_q->hwq_regs->read_idx);
+ cmd_q->ring_buff_count = 0;
+
+ dma_addr_lo = low32_value(cmd_q->qbase_phys_addr);
+ AE4DMA_WRITE_REG(&cmd_q->hwq_regs->qbase_lo, dma_addr_lo);
+ dma_addr_hi = high32_value(cmd_q->qbase_phys_addr);
+ AE4DMA_WRITE_REG(&cmd_q->hwq_regs->qbase_hi, dma_addr_hi);
+
+ return 0;
+}
+
+static void
+ae4dma_channel_dev_name(char *out, size_t outlen, const char *pci_name,
+ unsigned int ch)
+{
+ snprintf(out, outlen, "%s-ch%u", pci_name, ch);
+}
+
+/* Create a dmadev(dpdk DMA device) */
+static int
+ae4dma_dmadev_create(const char *name, struct rte_pci_device *dev, uint8_t qn)
+{
+ static const struct rte_dma_dev_ops ae4dma_dmadev_ops = {
+ .dev_close = ae4dma_dev_close,
+ .dev_configure = ae4dma_dev_configure,
+ .dev_dump = ae4dma_dev_dump,
+ .dev_info_get = ae4dma_dev_info_get,
+ .dev_start = ae4dma_dev_start,
+ .dev_stop = ae4dma_dev_stop,
+ .stats_get = ae4dma_stats_get,
+ .stats_reset = ae4dma_stats_reset,
+ .vchan_status = ae4dma_vchan_status,
+ .vchan_setup = ae4dma_vchan_setup,
+ };
+
+ struct rte_dma_dev *dmadev = NULL;
+ struct ae4dma_dmadev *ae4dma = NULL;
+ char hwq_dev_name[RTE_DEV_NAME_MAX_LEN];
+
+ if (!name) {
+ AE4DMA_PMD_ERR("Invalid name of the device!");
+ return -EINVAL;
+ }
+ memset(hwq_dev_name, 0, sizeof(hwq_dev_name));
+ ae4dma_channel_dev_name(hwq_dev_name, sizeof(hwq_dev_name), name, qn);
+
+ dmadev = rte_dma_pmd_allocate(hwq_dev_name, dev->device.numa_node,
+ sizeof(struct ae4dma_dmadev));
+ if (dmadev == NULL) {
+ AE4DMA_PMD_ERR("Unable to allocate dma device");
+ return -ENOMEM;
+ }
+ dmadev->device = &dev->device;
+ dmadev->fp_obj->dev_private = dmadev->data->dev_private;
+ dmadev->dev_ops = &ae4dma_dmadev_ops;
+
+ dmadev->fp_obj->burst_capacity = ae4dma_burst_capacity;
+ dmadev->fp_obj->completed = ae4dma_completed;
+ dmadev->fp_obj->completed_status = ae4dma_completed_status;
+ dmadev->fp_obj->copy = ae4dma_enqueue_copy;
+ dmadev->fp_obj->submit = ae4dma_submit;
+ /* fill capability not advertised: leave fp_obj->fill as zero-initialised. */
+
+ ae4dma = dmadev->data->dev_private;
+ ae4dma->dmadev = dmadev;
+ ae4dma->pci = dev;
+
+ if (ae4dma_add_queue(ae4dma, qn, name) != 0)
+ goto init_error;
+ return 0;
+
+init_error:
+ AE4DMA_PMD_ERR("driver %s(): failed", __func__);
+ rte_dma_pmd_release(hwq_dev_name);
+ return -EFAULT;
+}
+
+/* Probe DMA device. */
+static int
+ae4dma_dmadev_probe(struct rte_pci_driver *drv, struct rte_pci_device *dev)
+{
+ char name[32];
+ char chname[RTE_DEV_NAME_MAX_LEN];
+ void *mmio_base;
+ uint32_t q_per_eng;
+ int ret = 0;
+ uint8_t i;
+
+ rte_pci_device_name(&dev->addr, name, sizeof(name));
+ AE4DMA_PMD_INFO("Init %s on NUMA node %d", name, dev->device.numa_node);
+ dev->device.driver = &drv->driver;
+
+ mmio_base = dev->mem_resource[AE4DMA_PCIE_BAR].addr;
+ if (mmio_base == NULL) {
+ AE4DMA_PMD_ERR("%s: BAR%d not mapped", name, AE4DMA_PCIE_BAR);
+ return -ENODEV;
+ }
+
+ /* Program the per-engine HW queue count once. */
+ AE4DMA_WRITE_REG_OFFSET(mmio_base, AE4DMA_COMMON_CONFIG_OFFSET,
+ AE4DMA_MAX_HW_QUEUES);
+ q_per_eng = AE4DMA_READ_REG_OFFSET(mmio_base, AE4DMA_COMMON_CONFIG_OFFSET);
+ AE4DMA_PMD_INFO("%s: AE4DMA queues per engine = %u", name, q_per_eng);
+
+ for (i = 0; i < AE4DMA_MAX_HW_QUEUES; i++) {
+ ret = ae4dma_dmadev_create(name, dev, i);
+ if (ret != 0) {
+ AE4DMA_PMD_ERR("%s create dmadev %u failed!", name, i);
+ while (i > 0) {
+ i--;
+ ae4dma_channel_dev_name(chname, sizeof(chname), name, i);
+ rte_dma_pmd_release(chname);
+ }
+ break;
+ }
+ }
+ return ret;
+}
+
+/* Remove DMA device. */
+static int
+ae4dma_dmadev_remove(struct rte_pci_device *dev)
+{
+ char name[32];
+ char chname[RTE_DEV_NAME_MAX_LEN];
+ unsigned int i;
+
+ rte_pci_device_name(&dev->addr, name, sizeof(name));
+
+ AE4DMA_PMD_INFO("Closing %s on NUMA node %d",
+ name, dev->device.numa_node);
+
+ for (i = 0; i < AE4DMA_MAX_HW_QUEUES; i++) {
+ ae4dma_channel_dev_name(chname, sizeof(chname), name, i);
+ rte_dma_pmd_release(chname);
+ }
+ return 0;
+}
+
+static const struct rte_pci_id pci_id_ae4dma_map[] = {
+ { RTE_PCI_DEVICE(AMD_VENDOR_ID, AE4DMA_DEVICE_ID) },
+ { .vendor_id = 0, /* sentinel */ },
+};
+
+static struct rte_pci_driver ae4dma_pmd_drv = {
+ .id_table = pci_id_ae4dma_map,
+ .drv_flags = RTE_PCI_DRV_NEED_MAPPING,
+ .probe = ae4dma_dmadev_probe,
+ .remove = ae4dma_dmadev_remove,
+};
+
+RTE_PMD_REGISTER_PCI(AE4DMA_PMD_NAME, ae4dma_pmd_drv);
+RTE_PMD_REGISTER_PCI_TABLE(AE4DMA_PMD_NAME, pci_id_ae4dma_map);
+RTE_PMD_REGISTER_KMOD_DEP(AE4DMA_PMD_NAME, "* igb_uio | uio_pci_generic | vfio-pci");
diff --git a/drivers/dma/ae4dma/ae4dma_hw_defs.h b/drivers/dma/ae4dma/ae4dma_hw_defs.h
new file mode 100644
index 0000000000..235819778e
--- /dev/null
+++ b/drivers/dma/ae4dma/ae4dma_hw_defs.h
@@ -0,0 +1,164 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2024 Advanced Micro Devices, Inc. All rights reserved.
+ */
+
+#ifndef __AE4DMA_HW_DEFS_H__
+#define __AE4DMA_HW_DEFS_H__
+
+#include <rte_bus_pci.h>
+#include <rte_byteorder.h>
+#include <rte_io.h>
+#include <rte_pci.h>
+#include <rte_memzone.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define AE4DMA_BIT(nr) (1UL << (nr))
+
+#define AE4DMA_BITS_PER_LONG (__SIZEOF_LONG__ * 8)
+#define AE4DMA_GENMASK(h, l) \
+ (((~0UL) << (l)) & (~0UL >> (AE4DMA_BITS_PER_LONG - 1 - (h))))
+
+/* ae4dma device details */
+#define AMD_VENDOR_ID 0x1022
+#define AE4DMA_DEVICE_ID 0x149b
+#define AE4DMA_PCIE_BAR 0
+
+/*
+ * An AE4DMA engine has 16 DMA queues. Each queue supports 32 descriptors.
+ */
+#define AE4DMA_MAX_HW_QUEUES 16
+#define AE4DMA_QUEUE_START_INDEX 0
+#define AE4DMA_CMD_QUEUE_ENABLE 0x1
+#define AE4DMA_CMD_QUEUE_DISABLE 0x0
+
+/* Common to all queues */
+#define AE4DMA_COMMON_CONFIG_OFFSET 0x00
+
+#define AE4DMA_DISABLE_INTR 0x01
+
+/* Descriptor status */
+enum ae4dma_dma_status {
+ AE4DMA_DMA_DESC_SUBMITTED = 0,
+ AE4DMA_DMA_DESC_VALIDATED = 1,
+ AE4DMA_DMA_DESC_PROCESSED = 2,
+ AE4DMA_DMA_DESC_COMPLETED = 3,
+ AE4DMA_DMA_DESC_ERROR = 4,
+};
+
+/* Descriptor error-code */
+enum ae4dma_dma_err {
+ AE4DMA_DMA_ERR_NO_ERR = 0,
+ AE4DMA_DMA_ERR_INV_HEADER = 1,
+ AE4DMA_DMA_ERR_INV_STATUS = 2,
+ AE4DMA_DMA_ERR_INV_LEN = 3,
+ AE4DMA_DMA_ERR_INV_SRC = 4,
+ AE4DMA_DMA_ERR_INV_DST = 5,
+ AE4DMA_DMA_ERR_INV_ALIGN = 6,
+ AE4DMA_DMA_ERR_UNKNOWN = 7,
+};
+
+/* HW Queue status */
+enum ae4dma_hwqueue_status {
+ AE4DMA_HWQUEUE_EMPTY = 0,
+ AE4DMA_HWQUEUE_FULL = 1,
+ AE4DMA_HWQUEUE_NOT_EMPTY = 4
+};
+/*
+ * descriptor for AE4DMA commands
+ * 8 32-bit words:
+ * word 0: source memory type; destination memory type ; control bits
+ * word 1: desc_id; error code; status
+ * word 2: length
+ * word 3: reserved
+ * word 4: upper 32 bits of source pointer
+ * word 5: low 32 bits of source pointer
+ * word 6: upper 32 bits of destination pointer
+ * word 7: low 32 bits of destination pointer
+ */
+
+/* AE4DMA Descriptor - DWORD0 - Controls bits: Reserved for future use */
+#define AE4DMA_DWORD0_STOP_ON_COMPLETION AE4DMA_BIT(0)
+#define AE4DMA_DWORD0_INTERRUPT_ON_COMPLETION AE4DMA_BIT(1)
+#define AE4DMA_DWORD0_START_OF_MESSAGE AE4DMA_BIT(3)
+#define AE4DMA_DWORD0_END_OF_MESSAGE AE4DMA_BIT(4)
+#define AE4DMA_DWORD0_DESTINATION_MEMORY_TYPE AE4DMA_GENMASK(5, 4)
+#define AE4DMA_DWORD0_SOURCE_MEMEORY_TYPE AE4DMA_GENMASK(7, 6)
+
+#define AE4DMA_DWORD0_DESTINATION_MEMORY_TYPE_MEMORY (0x0)
+#define AE4DMA_DWORD0_DESTINATION_MEMORY_TYPE_IOMEMORY (1<<4)
+#define AE4DMA_DWORD0_SOURCE_MEMEORY_TYPE_MEMORY (0x0)
+#define AE4DMA_DWORD0_SOURCE_MEMEORY_TYPE_IOMEMORY (1<<6)
+
+struct ae4dma_desc_dword0 {
+ uint8_t byte0;
+ uint8_t byte1;
+ uint16_t timestamp;
+};
+
+struct ae4dma_desc_dword1 {
+ uint8_t status;
+ uint8_t err_code;
+ uint16_t desc_id;
+};
+
+struct ae4dma_desc {
+ struct ae4dma_desc_dword0 dw0;
+ struct ae4dma_desc_dword1 dw1;
+ uint32_t length;
+ uint32_t reserved;
+ uint32_t src_lo;
+ uint32_t src_hi;
+ uint32_t dst_lo;
+ uint32_t dst_hi;
+};
+
+/*
+ * Registers for each queue :4 bytes length
+ * Effective address : offset + reg
+ */
+struct ae4dma_hwq_regs {
+ union {
+ uint32_t control_raw;
+ struct {
+ uint32_t queue_enable: 1;
+ uint32_t reserved_internal: 31;
+ } control;
+ } control_reg;
+
+ union {
+ uint32_t status_raw;
+ struct {
+ uint32_t reserved0: 1;
+ /* 0–empty, 1–full, 2–stopped, 3–error , 4–Not Empty */
+ uint32_t queue_status: 2;
+ uint32_t reserved1: 21;
+ uint32_t interrupt_type: 4;
+ uint32_t reserved2: 4;
+ } status;
+ } status_reg;
+
+ uint32_t max_idx;
+ uint32_t read_idx;
+ uint32_t write_idx;
+
+ union {
+ uint32_t intr_status_raw;
+ struct {
+ uint32_t intr_status: 1;
+ uint32_t reserved: 31;
+ } intr_status;
+ } intr_status_reg;
+
+ uint32_t qbase_lo;
+ uint32_t qbase_hi;
+
+};
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* AE4DMA_HW_DEFS_H */
diff --git a/drivers/dma/ae4dma/ae4dma_internal.h b/drivers/dma/ae4dma/ae4dma_internal.h
new file mode 100644
index 0000000000..d55cfbe3b8
--- /dev/null
+++ b/drivers/dma/ae4dma/ae4dma_internal.h
@@ -0,0 +1,117 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2024 Advanced Micro Devices, Inc. All rights reserved.
+ */
+
+#ifndef _AE4DMA_INTERNAL_H_
+#define _AE4DMA_INTERNAL_H_
+
+#include <stdint.h>
+
+#include "ae4dma_hw_defs.h"
+
+/**
+ * upper_32_bits - return bits 32-63 of a number
+ * @n: the number we're accessing
+ */
+#define upper_32_bits(n) ((uint32_t)(((n) >> 16) >> 16))
+
+/**
+ * lower_32_bits - return bits 0-31 of a number
+ * @n: the number we're accessing
+ */
+#define lower_32_bits(n) ((uint32_t)((n) & 0xffffffff))
+
+/** Hardware ring depth (slots per queue); must be power of two. */
+#define AE4DMA_DESCRIPTORS_PER_CMDQ 32
+#define AE4DMA_QUEUE_DESC_SIZE sizeof(struct ae4dma_desc)
+#define AE4DMA_QUEUE_SIZE(n) (AE4DMA_DESCRIPTORS_PER_CMDQ * (n))
+
+
+/** AE4DMA registers Write/Read */
+static inline void ae4dma_pci_reg_write(void *base, int offset,
+ uint32_t value)
+{
+ volatile void *reg_addr = ((uint8_t *)base + offset);
+
+ rte_write32((rte_cpu_to_le_32(value)), reg_addr);
+}
+
+static inline uint32_t ae4dma_pci_reg_read(void *base, int offset)
+{
+ volatile void *reg_addr = ((uint8_t *)base + offset);
+
+ return rte_le_to_cpu_32(rte_read32(reg_addr));
+}
+
+#define AE4DMA_READ_REG_OFFSET(hw_addr, reg_offset) \
+ ae4dma_pci_reg_read(hw_addr, reg_offset)
+
+#define AE4DMA_WRITE_REG_OFFSET(hw_addr, reg_offset, value) \
+ ae4dma_pci_reg_write(hw_addr, reg_offset, value)
+
+
+#define AE4DMA_READ_REG(hw_addr) \
+ ae4dma_pci_reg_read((void *)(uintptr_t)(hw_addr), 0)
+
+#define AE4DMA_WRITE_REG(hw_addr, value) \
+ ae4dma_pci_reg_write((void *)(uintptr_t)(hw_addr), 0, value)
+
+static inline uint32_t
+low32_value(unsigned long addr)
+{
+ return ((uint64_t)addr) & 0xffffffffUL;
+}
+
+static inline uint32_t
+high32_value(unsigned long addr)
+{
+ return (uint32_t)(((uint64_t)addr) >> 32);
+}
+
+/**
+ * A structure describing a AE4DMA command queue.
+ */
+struct ae4dma_cmd_queue {
+ char memz_name[RTE_MEMZONE_NAMESIZE];
+ volatile struct ae4dma_hwq_regs *hwq_regs;
+
+ struct rte_dma_vchan_conf qcfg;
+ struct rte_dma_stats stats;
+ /* Queue address */
+ struct ae4dma_desc *qbase_desc;
+ void *qbase_addr;
+ phys_addr_t qbase_phys_addr;
+ enum ae4dma_dma_err status[AE4DMA_DESCRIPTORS_PER_CMDQ];
+ /* Queue identifier */
+ uint64_t id; /**< queue id */
+ uint64_t qidx; /**< queue index */
+ uint64_t qsize; /**< queue size */
+ uint32_t ring_buff_count;
+ unsigned short next_read;
+ unsigned short next_write;
+ unsigned short last_write; /* Used to compute submitted count. */
+} __rte_cache_aligned;
+
+/*
+ * One dmadev per AE4DMA hardware channel: probe creates AE4DMA_MAX_HW_QUEUES
+ * dmadevs per PCI function, each owning a single HW command queue.
+ */
+struct ae4dma_dmadev {
+ struct rte_dma_dev *dmadev;
+ void *io_regs;
+ struct ae4dma_cmd_queue cmd_q; /**< single HW queue owned by this dmadev */
+ struct rte_pci_device *pci; /**< owning PCI device (not owned) */
+};
+
+
+extern int ae4dma_pmd_logtype;
+
+#define AE4DMA_PMD_LOG(level, fmt, args...) rte_log(RTE_LOG_ ## level, \
+ ae4dma_pmd_logtype, "AE4DMA: %s(): " fmt "\n", __func__, ##args)
+
+#define AE4DMA_PMD_DEBUG(fmt, args...) AE4DMA_PMD_LOG(DEBUG, fmt, ## args)
+#define AE4DMA_PMD_INFO(fmt, args...) AE4DMA_PMD_LOG(INFO, fmt, ## args)
+#define AE4DMA_PMD_ERR(fmt, args...) AE4DMA_PMD_LOG(ERR, fmt, ## args)
+#define AE4DMA_PMD_WARN(fmt, args...) AE4DMA_PMD_LOG(WARNING, fmt, ## args)
+
+#endif /* _AE4DMA_INTERNAL_H_ */
diff --git a/drivers/dma/ae4dma/meson.build b/drivers/dma/ae4dma/meson.build
new file mode 100644
index 0000000000..e48ab0d561
--- /dev/null
+++ b/drivers/dma/ae4dma/meson.build
@@ -0,0 +1,7 @@
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright 2024 Advanced Micro Devices, Inc. All rights reserved.
+
+build = dpdk_conf.has('RTE_ARCH_X86')
+reason = 'only supported on x86'
+sources = files('ae4dma_dmadev.c')
+deps += ['bus_pci', 'dmadev']
diff --git a/drivers/dma/meson.build b/drivers/dma/meson.build
index e0d94db967..c230ac5a06 100644
--- a/drivers/dma/meson.build
+++ b/drivers/dma/meson.build
@@ -2,6 +2,7 @@
# Copyright 2021 HiSilicon Limited
drivers = [
+ 'ae4dma',
'cnxk',
'dpaa',
'dpaa2',
diff --git a/usertools/dpdk-devbind.py b/usertools/dpdk-devbind.py
index 93f2383dff..ec6d6713b4 100755
--- a/usertools/dpdk-devbind.py
+++ b/usertools/dpdk-devbind.py
@@ -86,6 +86,9 @@
cn9k_ree = {'Class': '08', 'Vendor': '177d', 'Device': 'a0f4',
'SVendor': None, 'SDevice': None}
+amd_ae4dma = {'Class': '08', 'Vendor': '1022', 'Device': '149b',
+ 'SVendor': None, 'SDevice': None}
+
virtio_blk = {'Class': '01', 'Vendor': "1af4", 'Device': '1001,1042',
'SVendor': None, 'SDevice': None}
@@ -95,7 +98,7 @@
network_devices = [network_class, cavium_pkx, avp_vnic, ifpga_class]
baseband_devices = [acceleration_class]
crypto_devices = [encryption_class, intel_processor_class]
-dma_devices = [cnxk_dma, hisilicon_dma,
+dma_devices = [amd_ae4dma, cnxk_dma, hisilicon_dma,
intel_idxd_gnrd, intel_idxd_dmr, intel_idxd_spr,
intel_ioat_bdw, intel_ioat_icx, intel_ioat_skx,
odm_dma]
--
2.34.1
^ permalink raw reply related
* Re: [PATCH dpdk v5 4/5] net: parse L3 protocol after MPLS labels
From: Stephen Hemminger @ 2026-05-18 18:00 UTC (permalink / raw)
To: Robin Jarry; +Cc: dev
In-Reply-To: <20260518132712.70913-12-rjarry@redhat.com>
On Mon, 18 May 2026 15:27:18 +0200
Robin Jarry <rjarry@redhat.com> wrote:
> rte_net_get_ptype stops at the MPLS layer and never identifies the L3
> protocol of the payload. Also, the label parsing uses a fixed maximum of
> 5 headers instead of checking the bottom of stack bit.
>
> Use the bottom of stack bit to consume all labels and inspect the first
> nibble of the payload to determine if it is IPv4 or IPv6.
>
> Add test cases to verify this works. Ensure that an unknown protocol
> after MPLS (e.g. ARP) does not produce a bogus L3 type.
>
> Signed-off-by: Robin Jarry <rjarry@redhat.com>
> ---
Re: [PATCH dpdk v5 4/5] net: parse L3 protocol after MPLS labels
Info:
The local variable name `nimble` (and its companion `nimble_copy`) appears
to be a typo for `nibble`. A nibble is the standard term for 4 bits, which
is what the code reads and masks against `0xf0`. The commit message itself
uses the correct spelling ("inspect the first nibble of the payload"), so
only the code is affected.
const uint8_t *nimble;
uint8_t nimble_copy;
...
nimble = rte_pktmbuf_read(m, off, sizeof(*nimble), &nimble_copy);
if (nimble == NULL)
return pkt_type;
switch (*nimble & 0xf0) {
Suggest renaming to `nibble`/`nibble_copy` for clarity.
^ permalink raw reply
* Re: [PATCH V2 00/15] power: unify and improve lcore ID verification
From: Stephen Hemminger @ 2026-05-18 17:59 UTC (permalink / raw)
To: Huisong Li
Cc: anatoly.burakov, sivaprasad.tummala, dev, thomas, fengchengwen,
yangxingui, zhanjie9
In-Reply-To: <20260507024230.1198111-1-lihuisong@huawei.com>
On Thu, 7 May 2026 10:42:15 +0800
Huisong Li <lihuisong@huawei.com> wrote:
> This patch series reworks the lcore ID verification logic within the
> power library to ensure consistency and improve maintainability.
>
> Currently, various cpufreq drivers implement their own lcore ID checks,
> which are limited to simple range validation and involve significant
> code duplication. Moreover, these checks do not account for whether the
> core is actually managed by the application.
>
> For the verification in cpufreq-related APIs and power QoS APIs, although
> service cores do not typically invoke these APIs, they may operate in
> polling modes where power management is required. To maintain compatibility
> with applications using service cores, the validation logic now explicitly
> allows both ROLE_RTE and ROLE_SERVICE.
>
> But the lcore ID in the pmd_mgmt library must be ROLE_RTE because it is
> mainly used together with the data plane of ethdev PMD. So use
> rte_lcore_is_enabled to verify.
>
> Key Changes:
> 1. Add lcore role verification to individual cpufreq drivers.
> 2. Introduces a unified macro in the power library to standardize lcore ID
> checks.
> 3. Moves verification logic from individualdrivers to the framework level.
> This reduces code duplication.
> 4. Allow the service cores to configure power QoS.
> 5. Use rte_lcore_is_enabled to verfify the lcore ID in pmd_mgmt.
>
> ---
> v2:
> - Allow the service cores to set power API.
>
> ---
>
> Huisong Li (15):
> eal: add interface to check if lcore is EAL managed
> power/kvm_vm: validate lcore role in cpufreq API
> power/acpi_cpufreq: validate lcore role in cpufreq API
> power/amd_pstate: validate lcore role in cpufreq API
> power/cppc_cpufreq: validate lcore role in cpufreq API
> power/intel_pstate: validate lcore role in cpufreq API
> power: add a common macro to verify lcore ID
> power/cpufreq: add the lcore ID verification to framework
> power/acpi_cpufreq: remove the verification of lcore ID
> power/amd_pstate: remove the verification of lcore ID
> power/cppc_cpufreq: remove the verification of lcore ID
> power/intel_pstate: remove the verification of lcore ID
> power/kvm_vm: remove the verification of lcore ID
> power: allow the service core to config power QoS
> power: add lcore ID check for PMD mgmt
>
> drivers/power/acpi/acpi_cpufreq.c | 65 -------------------
> drivers/power/amd_pstate/amd_pstate_cpufreq.c | 65 -------------------
> drivers/power/cppc/cppc_cpufreq.c | 65 -------------------
> .../power/intel_pstate/intel_pstate_cpufreq.c | 65 -------------------
> drivers/power/kvm_vm/kvm_vm.c | 10 ---
> lib/eal/common/eal_common_lcore.c | 11 ++++
> lib/eal/include/rte_lcore.h | 11 ++++
> lib/power/power_common.h | 7 ++
> lib/power/rte_power_cpufreq.c | 14 +++-
> lib/power/rte_power_pmd_mgmt.c | 21 +++---
> lib/power/rte_power_qos.c | 10 +--
> 11 files changed, 55 insertions(+), 289 deletions(-)
>
Lots of AI review feedback on this one:
Patch 01/15: eal: add interface to check if lcore is EAL managed
=================================================================
Warning: New public API rte_lcore_is_eal_managed() is exported as
a stable ABI symbol (RTE_EXPORT_SYMBOL), but DPDK policy requires
new public APIs to be introduced as experimental:
+RTE_EXPORT_SYMBOL(rte_lcore_is_eal_managed)
+int rte_lcore_is_eal_managed(unsigned int lcore_id)
The declaration in rte_lcore.h is also missing the __rte_experimental
attribute. The export should be:
RTE_EXPORT_EXPERIMENTAL_SYMBOL(rte_lcore_is_eal_managed, 26.07)
int rte_lcore_is_eal_managed(unsigned int lcore_id)
and the prototype in lib/eal/include/rte_lcore.h should carry
__rte_experimental.
Warning: This series adds a new public API but does not update the
release notes in doc/guides/rel_notes/release_26_07.rst (or whatever
the current target release is). New API additions must be documented
in the release notes under the "New Features" section.
Patch 07/15: power: add a common macro to verify lcore ID
=========================================================
Info: The macro evaluates lcore_id twice (once in
rte_lcore_is_eal_managed() and once in POWER_LOG). All current
call-sites pass a plain variable so there is no observable bug, but
a side-effecting argument would be evaluated twice. Same caveat
applies to the existing RTE_ETH_VALID_PORTID_OR_ERR_RET pattern, so
this is consistent with existing DPDK convention.
Patch 14/15: power: allow the service core to config power QoS
==============================================================
Info: The check changes from rte_lcore_is_enabled() (ROLE_RTE only)
to rte_lcore_is_eal_managed() (ROLE_RTE + ROLE_SERVICE), so the log
message wording changes from "is not enabled" to the macro's "is
invalid" text. For a service core that was previously rejected, the
new "invalid" wording is less informative than the prior message,
but this is minor.
Patch 15/15: power: add lcore ID check for PMD mgmt
====================================================
Info: This patch tightens the validation from "lcore_id within
RTE_MAX_LCORE range" to "lcore_id is ROLE_RTE". Combined with the
Fixes: tag and Cc: stable, this is a behavioural change in stable
branches: callers that previously passed a non-enabled lcore_id
(ROLE_OFF / ROLE_SERVICE / ROLE_NON_EAL) will now receive -EINVAL
where they previously succeeded into the function body. The commit
message correctly justifies the data-plane (ROLE_RTE) requirement,
but the behaviour change should be called out explicitly in the
commit log so stable maintainers and downstream users are aware.
Series-level observation
========================
Info: Patches 02-06 add per-driver lcore role checks, and patches
09-13 remove the same checks once patch 08 lifts validation into
the framework. The net effect is that each driver's lcore check is
modified and then deleted within the same series. The series could
be reorganized to:
1. Patch 01: add rte_lcore_is_eal_managed()
2. Patch 07: add the RTE_POWER_VALID_LCOREID_OR_ERR_RET macro
3. Patch 08: add validation to the framework (rte_power_cpufreq.c)
4. Patches 14, 15: pmd_mgmt and QoS adjustments
skipping patches 02-06 and 09-13 entirely. This would reduce review
load and avoid intermediate states where the same check exists in
two places. If the current structure is intentional for incremental
bisection, that rationale would be worth mentioning in a cover
letter.
^ permalink raw reply
* Re: [PATCH v3 02/10] bpf: introduce extensible load API
From: Stephen Hemminger @ 2026-05-18 17:56 UTC (permalink / raw)
To: Marat Khalili; +Cc: Konstantin Ananyev, Wathsala Vithanage, dev
In-Reply-To: <20260518084912.57006-3-marat.khalili@huawei.com>
On Mon, 18 May 2026 09:49:02 +0100
Marat Khalili <marat.khalili@huawei.com> wrote:
> Introduce new BPF load parameters struct rte_bpf_prm_ex that can be
> extended without breaking backward or forward compatibility. Introduce
> new function rte_bpf_load_ex consolidating in one code path loading from
> both ELF file and raw memory image, with possibility to add more options
> in the future.
>
> Some changes in code layout and sequence:
> * Both old APIs now only forwarding calls to a new single entry point.
> * There is now a centralized cleanup point for all temporary resources
> created during the load process.
> * External symbols (xsyms) are now checked for validity just after the
> load started, not after they were already used for relocation.
> * File bpf_load_elf.c now only handles opening ELF file and providing
> patched instruction array to the load process. These are left as two
> separate functions to support other ELF sources like memory image in
> the future.
> * Function stubs for the case libelf is not available are moved to
> bpf_load_elf.c to make keeping track of them easier (forgetting to
> update stubs is a common problem).
>
> Signed-off-by: Marat Khalili <marat.khalili@huawei.com>
> Acked-by: Konstantin Ananyev <konstantin.ananyev@huawei.com>
> ---Review of bundle-1895: bpf load (PATCH v3 01..10/10)
Lots of AI review on this (with Claude Opus 4.7)
====================================================
Patches 01, 06, 09, and 10 had no findings and are omitted from this
review.
PATCH v3 02/10 -- bpf: introduce extensible load API
----------------------------------------------------
Errors:
* lib/bpf/bpf_load.c, rte_bpf_load() and rte_bpf_elf_load() wrappers:
These now unconditionally dereference `prm` when constructing the
compound literal passed to rte_bpf_load_ex():
return rte_bpf_load_ex(&(struct rte_bpf_prm_ex){
...
.raw.ins = prm->ins, /* NULL deref if prm == NULL */
.raw.nb_ins = prm->nb_ins,
.xsym = prm->xsym,
.nb_xsym = prm->nb_xsym,
.prog_arg = prm->prog_arg,
});
Pre-patch both functions explicitly checked `prm == NULL` and
returned NULL with rte_errno = EINVAL. The NULL check inside
load_try() does not help: NULL is dereferenced before
rte_bpf_load_ex() is even called. Add explicit
`if (prm == NULL) { rte_errno = EINVAL; return NULL; }` guards at
the top of both wrappers.
Info:
* lib/bpf/bpf_load_elf.c: the success log
"successfully creates %p(jit={.func=%p,.sz=%zu})" and the error log
in rte_bpf_elf_load() are removed silently. If intentional, mention
in the commit message.
PATCH v3 03/10 -- bpf: support up to 5 arguments
------------------------------------------------
Warnings:
* lib/bpf/bpf_exec.c, rte_bpf_exec_burst(), exec_jit_burst_ex(), and
rte_bpf_exec_burst_ex(): these return `uint32_t` but the patch
introduces `return -EINVAL` on error:
if (bpf->prm.nb_prog_arg != 1)
/* Use rte_bpf_exec_burst_ex with this program. */
return -EINVAL;
-22 reinterpreted as uint32_t is 0xFFFFFFEA, which the caller cannot
distinguish from a valid "number of successfully processed inputs"
result. The doxygen for rte_bpf_exec_burst_ex does not mention error
returns. Either change the return type, document the encoding (e.g.
any value > num indicates an error), or return 0 on these paths.
PATCH v3 04/10 -- bpf: add cBPF origin to rte_bpf_load_ex
---------------------------------------------------------
Info:
* lib/bpf/bpf_convert.c: the no-libpcap stub for rte_bpf_convert()
loses its `if (prog == NULL) return EINVAL` check when moved out of
bpf_stub.c. Previously rte_bpf_convert(NULL) returned EINVAL; now it
returns ENOTSUP. Both produce NULL so pointer-checking callers are
unaffected, but rte_errno semantics changed. Worth a mention or a
restore for symmetry with the libpcap-enabled path.
PATCH v3 05/10 -- bpf: support rte_bpf_prm_ex with port callbacks
-----------------------------------------------------------------
Errors:
* lib/bpf/rte_bpf_ethdev.h: the doxygen comments for the two new
functions are swapped:
/**
* Install callback to execute specified BPF program on given
* TX port/queue.
* ...
* @param queue
* The identifier of the TX queue on the given port
*/
__rte_experimental
int
rte_bpf_eth_rx_install(...);
/**
* Install callback to execute specified BPF program on given
* RX port/queue.
* ...
* @param queue
* The identifier of the RX queue on the given port
*/
__rte_experimental
int
rte_bpf_eth_tx_install(...);
Swap the two doc blocks.
PATCH v3 07/10 -- test/bpf: test loading cBPF directly
------------------------------------------------------
Info:
* app/test/test_bpf.c, test_bpf_filter(): the failure path previously
invoked test_bpf_dump(&fcode, prm) when load failed, useful for
diagnosing converter regressions. The refactor drops this. Consider
preserving the dump in the failure branch (or moving it into
load_cbpf_program_convert on its NULL return).
PATCH v3 08/10 -- test/bpf: test loading ELF file from memory
-------------------------------------------------------------
Errors:
* app/test/test_bpf.c, bpf_rx_test(): when rte_bpf_eth_rx_install()
fails, the loaded bpf program is leaked:
ret = rte_bpf_eth_rx_install(port, 0, bpf, flags);
if (ret != 0) {
printf(...);
return ret; /* no rte_bpf_destroy(bpf) */
}
bpf_tx_test() in the same patch correctly calls rte_bpf_destroy(bpf)
on the equivalent error path. Per the patch 05 contract, ownership
transfers to the library only on successful install. Add
`rte_bpf_destroy(bpf);` before the return.
^ permalink raw reply
* [PATCH v2] dts: add retry loop to trex traffic generation
From: Andrew Bailey @ 2026-05-18 17:54 UTC (permalink / raw)
To: luca.vizzarro, patrickrobb
Cc: dev, lylavoie, knimoji, ahassick, Andrew Bailey
In-Reply-To: <20260515174714.211668-1-abailey@iol.unh.edu>
There was an issue where the single core forward test would report zero
MPPS intermittently. This was due to TREX reporting that the link was
down when the client was called to start generating traffic. The links
were being reported down by TREX on the tg even when testpmd was
reporting them up on the SUT. Adding a retry loop to the generate
traffic method of TREX gives the tg enough time to set up and send
traffic.
Bugzilla ID: 1946
Fixes: d77d7f04f24c ("dts: add single-core performance test suite")
Signed-off-by: Andrew Bailey <abailey@iol.unh.edu>
---
.../testbed_model/traffic_generator/trex.py | 37 ++++++++++++++-----
1 file changed, 28 insertions(+), 9 deletions(-)
diff --git a/dts/framework/testbed_model/traffic_generator/trex.py b/dts/framework/testbed_model/traffic_generator/trex.py
index 22cd20dea9..daf07355f2 100644
--- a/dts/framework/testbed_model/traffic_generator/trex.py
+++ b/dts/framework/testbed_model/traffic_generator/trex.py
@@ -13,6 +13,7 @@
from framework.config.node import OS, NodeConfiguration
from framework.config.test_run import TrexTrafficGeneratorConfig
+from framework.exception import SSHTimeoutError
from framework.parser import TextParser
from framework.remote_session.blocking_app import BlockingApp
from framework.remote_session.python_shell import PythonShell
@@ -220,7 +221,9 @@ def _create_packet_stream(self, packet: Packet) -> None:
]
self._shell.send_command("\n".join(packet_stream))
- def _send_traffic_and_get_stats(self, duration: float, send_mpps: float | None = None) -> str:
+ def _send_traffic_and_get_stats(
+ self, duration: float, send_mpps: float | None = None, retry_attempts: int = 5
+ ) -> str:
"""Send traffic and get TG Rx stats.
Sends traffic from the TRex client's ports for the given duration.
@@ -230,15 +233,31 @@ def _send_traffic_and_get_stats(self, duration: float, send_mpps: float | None =
Args:
duration: The traffic generation duration.
send_mpps: The millions of packets per second for TRex to send from each port.
+ retry_attempts: The number of times to retry this command on failure.
+
+ Raises:
+ SSHTimeoutError: If TRex fails to send traffic in the allotted attempts.
"""
- if send_mpps:
- self._shell.send_command(f"""{self.stl_client_name}.start(ports=[0, 1],
- mult = '{send_mpps}mpps',
- duration = {duration})""")
- else:
- self._shell.send_command(f"""{self.stl_client_name}.start(ports=[0, 1],
- mult = '100%',
- duration = {duration})""")
+ result = "link is DOWN"
+ attempt = 0
+
+ while "link is DOWN" in result and attempt <= retry_attempts:
+ if attempt > 0:
+ self._logger.info(
+ f"Generate traffic command failed (attempt {attempt} out of {retry_attempts})"
+ )
+ time.sleep(0.25)
+ elif attempt == retry_attempts:
+ raise SSHTimeoutError("Failed to generate traffic on TREX traffic generator")
+ if send_mpps:
+ result = self._shell.send_command(f"""{self.stl_client_name}.start(ports=[0, 1],
+ mult = '{send_mpps}mpps',
+ duration = {duration})""")
+ else:
+ result = self._shell.send_command(f"""{self.stl_client_name}.start(ports=[0, 1],
+ mult = '100%',
+ duration = {duration})""")
+ attempt += 1
time.sleep(duration)
--
2.50.1
^ permalink raw reply related
* Re: [PATCH dpdk v5] net/tap: use offsets provided by rte_net_get_ptype
From: Stephen Hemminger @ 2026-05-18 17:52 UTC (permalink / raw)
To: Robin Jarry; +Cc: dev
In-Reply-To: <20260518082659.13036-2-rjarry@redhat.com>
On Mon, 18 May 2026 10:27:00 +0200
Robin Jarry <rjarry@redhat.com> wrote:
> Instead of guessing what are the proper header lengths, pass
> a rte_net_hdr_lens struct to rte_net_get_ptype and use it to get the
> proper header lengths/offsets in tap_verify_csum.
>
> This allows supporting stacked VLAN/QinQ tags and IPv6 extensions.
>
> Signed-off-by: Robin Jarry <rjarry@redhat.com>
Still needs some polishing around Ipv6 ext headers
per AI review...
[PATCH dpdk v5] net/tap: use offsets provided by rte_net_get_ptype
The refactor is mechanically correct. rte_net_get_ptype() with a
non-NULL rte_net_hdr_lens fills l2_len walking the full VLAN/QinQ
stack (up to RTE_NET_VLAN_MAX_DEPTH), and for IPv4 sets
l3_len = rte_ipv4_hdr_len(ip4h). Replacing the local l2_len/l3_len
arithmetic with hlen->* preserves the existing bounds checks and
genuinely fixes the stacked-VLAN case (the old code only handled
single VLAN +4 or QinQ +8). The unnecessary cast of l4_hdr to
struct rte_udp_hdr * was correctly dropped. The {0} initializer on
the rte_net_hdr_lens stack variable is appropriate since
rte_net_get_ptype only writes the fields it parses.
Info:
- The commit message says "This allows supporting stacked
VLAN/QinQ tags and IPv6 extensions." The first half is true.
The IPv6-extensions half is not: the else branch that catches
RTE_PTYPE_L3_IPV6_EXT and returns is unchanged, and the
accompanying comment "IPv6 extensions are not supported." in
that branch is also unchanged. For an IPv6 packet with
extension headers, rte_net_get_ptype() will set
RTE_PTYPE_L3_IPV6_EXT (not RTE_PTYPE_L3_IPV6), so the
else-if branch is skipped and tap_verify_csum() returns
without verifying L3 or L4 checksums. The hlen now carries
the correct l3_len including extensions, so this is a
prerequisite for IPv6-ext support but not the feature itself.
Suggest rewording to "This is a prerequisite for IPv6
extension header support" or dropping that half of the
sentence.
- drivers/net/tap/rte_eth_tap.c:549: minor scoping observation
only (no action needed) -- the hlen struct is filled
unconditionally on every received packet whereas previously
rte_net_get_ptype() was called with NULL when offload check
was disabled. The cost is a handful of stack writes per
packet; consistent with how other PMDs handle this.
^ permalink raw reply
* Re: [PATCH v16 00/11] net/sxe2: fix logic errors and address feedback
From: Stephen Hemminger @ 2026-05-18 17:25 UTC (permalink / raw)
To: liujie5; +Cc: dev
In-Reply-To: <20260518091405.3295896-1-liujie5@linkdatatechnology.com>
On Mon, 18 May 2026 17:13:54 +0800
liujie5@linkdatatechnology.com wrote:
> From: Jie Liu <liujie5@linkdatatechnology.com>
>
> This patch set addresses the feedback received on the v10 submission
> for the sxe2 PMD. The primary focus is on fixing vector path selection,
> ensuring memory safety during mbuf initialization, and cleaning up
> redundant logic in the configuration functions.
>
> v16 Changes:
> - Fixed vector Rx burst function being overwritten by scalar selection.
> - Refactored Rx/Tx mode set functions to seed flags from caps first,
> eliminating tautological checks.
> - Added memset for mbuf_def in vector init to avoid uninitialized reads.
> - Converted pci_map_addr_info to designated initializers.
> - Removed dead Windows-only code in meson.build.
> - Added NULL checks for mbuf free for driver-wide consistency.
> - Updated burst_mode_get to accurately report AVX paths.
> - Adjusted SXE2_ETH_OVERHEAD to match actual VLAN capabilities.
>
> Jie Liu (11):
> mailmap: add Jie Liu
> doc: add sxe2 guide and release notes
> common/sxe2: add sxe2 basic structures
> drivers: add base driver skeleton
> drivers: add base driver probe skeleton
> drivers: support PCI BAR mapping
> common/sxe2: add ioctl interface for DMA map and unmap
> net/sxe2: support queue setup and control
> drivers: add data path for Rx and Tx
> net/sxe2: add vectorized Rx and Tx
> net/sxe2: implement Tx done cleanup
>
> .mailmap | 1 +
> doc/guides/nics/features/sxe2.ini | 29 +
> doc/guides/nics/index.rst | 1 +
> doc/guides/nics/sxe2.rst | 34 +
> doc/guides/rel_notes/release_26_07.rst | 4 +
> drivers/common/sxe2/meson.build | 15 +
> drivers/common/sxe2/sxe2_common.c | 683 +++++++++++++
> drivers/common/sxe2/sxe2_common.h | 85 ++
> drivers/common/sxe2/sxe2_common_log.h | 81 ++
> drivers/common/sxe2/sxe2_host_regs.h | 707 +++++++++++++
> drivers/common/sxe2/sxe2_internal_ver.h | 33 +
> drivers/common/sxe2/sxe2_ioctl_chnl.c | 325 ++++++
> drivers/common/sxe2/sxe2_ioctl_chnl.h | 130 +++
> drivers/common/sxe2/sxe2_ioctl_chnl_func.h | 62 ++
> drivers/common/sxe2/sxe2_osal.h | 153 +++
> drivers/meson.build | 1 +
> drivers/net/meson.build | 1 +
> drivers/net/sxe2/meson.build | 32 +
> drivers/net/sxe2/sxe2_cmd_chnl.c | 323 ++++++
> drivers/net/sxe2/sxe2_cmd_chnl.h | 37 +
> drivers/net/sxe2/sxe2_drv_cmd.h | 388 ++++++++
> drivers/net/sxe2/sxe2_ethdev.c | 968 ++++++++++++++++++
> drivers/net/sxe2/sxe2_ethdev.h | 318 ++++++
> drivers/net/sxe2/sxe2_irq.h | 48 +
> drivers/net/sxe2/sxe2_queue.c | 66 ++
> drivers/net/sxe2/sxe2_queue.h | 195 ++++
> drivers/net/sxe2/sxe2_rx.c | 559 +++++++++++
> drivers/net/sxe2/sxe2_rx.h | 34 +
> drivers/net/sxe2/sxe2_tx.c | 420 ++++++++
> drivers/net/sxe2/sxe2_tx.h | 32 +
> drivers/net/sxe2/sxe2_txrx.c | 362 +++++++
> drivers/net/sxe2/sxe2_txrx.h | 23 +
> drivers/net/sxe2/sxe2_txrx_common.h | 540 ++++++++++
> drivers/net/sxe2/sxe2_txrx_poll.c | 1044 ++++++++++++++++++++
> drivers/net/sxe2/sxe2_txrx_poll.h | 20 +
> drivers/net/sxe2/sxe2_txrx_vec.c | 201 ++++
> drivers/net/sxe2/sxe2_txrx_vec.h | 73 ++
> drivers/net/sxe2/sxe2_txrx_vec_common.h | 235 +++++
> drivers/net/sxe2/sxe2_txrx_vec_sse.c | 549 ++++++++++
> drivers/net/sxe2/sxe2_vsi.c | 214 ++++
> drivers/net/sxe2/sxe2_vsi.h | 204 ++++
> 41 files changed, 9230 insertions(+)
> create mode 100644 doc/guides/nics/features/sxe2.ini
> create mode 100644 doc/guides/nics/sxe2.rst
> create mode 100644 drivers/common/sxe2/meson.build
> create mode 100644 drivers/common/sxe2/sxe2_common.c
> create mode 100644 drivers/common/sxe2/sxe2_common.h
> create mode 100644 drivers/common/sxe2/sxe2_common_log.h
> create mode 100644 drivers/common/sxe2/sxe2_host_regs.h
> create mode 100644 drivers/common/sxe2/sxe2_internal_ver.h
> create mode 100644 drivers/common/sxe2/sxe2_ioctl_chnl.c
> create mode 100644 drivers/common/sxe2/sxe2_ioctl_chnl.h
> create mode 100644 drivers/common/sxe2/sxe2_ioctl_chnl_func.h
> create mode 100644 drivers/common/sxe2/sxe2_osal.h
> create mode 100644 drivers/net/sxe2/meson.build
> create mode 100644 drivers/net/sxe2/sxe2_cmd_chnl.c
> create mode 100644 drivers/net/sxe2/sxe2_cmd_chnl.h
> create mode 100644 drivers/net/sxe2/sxe2_drv_cmd.h
> create mode 100644 drivers/net/sxe2/sxe2_ethdev.c
> create mode 100644 drivers/net/sxe2/sxe2_ethdev.h
> create mode 100644 drivers/net/sxe2/sxe2_irq.h
> create mode 100644 drivers/net/sxe2/sxe2_queue.c
> create mode 100644 drivers/net/sxe2/sxe2_queue.h
> create mode 100644 drivers/net/sxe2/sxe2_rx.c
> create mode 100644 drivers/net/sxe2/sxe2_rx.h
> create mode 100644 drivers/net/sxe2/sxe2_tx.c
> create mode 100644 drivers/net/sxe2/sxe2_tx.h
> create mode 100644 drivers/net/sxe2/sxe2_txrx.c
> create mode 100644 drivers/net/sxe2/sxe2_txrx.h
> create mode 100644 drivers/net/sxe2/sxe2_txrx_common.h
> create mode 100644 drivers/net/sxe2/sxe2_txrx_poll.c
> create mode 100644 drivers/net/sxe2/sxe2_txrx_poll.h
> create mode 100644 drivers/net/sxe2/sxe2_txrx_vec.c
> create mode 100644 drivers/net/sxe2/sxe2_txrx_vec.h
> create mode 100644 drivers/net/sxe2/sxe2_txrx_vec_common.h
> create mode 100644 drivers/net/sxe2/sxe2_txrx_vec_sse.c
> create mode 100644 drivers/net/sxe2/sxe2_vsi.c
> create mode 100644 drivers/net/sxe2/sxe2_vsi.h
>
Getting much closer to merge, still time for 26.07.
Review of [PATCH v16 00/11] sxe2 PMD
====================================
Overall comments
----------------
All Error/Warning-level issues from the v15 review are addressed
in v16:
- sxe2_tx_done_cleanup(): tx_queue NULL check now runs before
dereferencing txq->vsi->adapter.
- Vector mode probe is still primary-only, but secondaries now
inherit the decision via adapter->q_ctxt.{rx,tx}_mode_flags
rather than silently falling back to scalar.
- BITS_TO_uint32_t macro renamed to BITS_TO_U32.
- Dead "goto l_end; l_end:" pattern in sxe2_rx_mode_func_set
removed in favour of "return;".
- sxe2_drv_dev_handshake function name correctly spelled in
the patch that introduces it.
Two new concerns and one carryover:
Patch 09/11: drivers: add data path for Rx and Tx
-------------------------------------------------
Warning: asymmetric secondary-process fallback in
sxe2_tx_mode_func_set() versus sxe2_rx_mode_func_set().
Rx (correct):
if (rte_eal_process_type() == RTE_PROC_PRIMARY) {
...
adapter->q_ctxt.rx_mode_flags = rx_mode_flags;
} else {
rx_mode_flags = adapter->q_ctxt.rx_mode_flags;
}
Tx (overrides primary's decision):
if (rte_eal_process_type() == RTE_PROC_PRIMARY) {
...
adapter->q_ctxt.tx_mode_flags = tx_mode_flags;
} else {
tx_mode_flags = adapter->q_ctxt.tx_mode_flags;
if (tx_mode_flags == 0)
tx_mode_flags = SXE2_TX_MODE_SIMPLE_BATCH;
}
If the primary genuinely settled on tx_mode_flags = 0 (no
vector, no simple-batch — i.e. the full sxe2_tx_pkts /
sxe2_tx_pkts_prepare path), the secondary will pick
sxe2_tx_pkts_simple instead. Primary and secondary then run
different burst functions against the same queue, with different
offload-handling semantics. This breaks the multi-process
contract.
If the fallback is meant to handle "secondary attached before
primary configured anything", that case should be detected
explicitly (e.g. a separate "configured" flag). Otherwise just
mirror the Rx side: trust whatever the primary put in shared
memory.
Warning: SXE2_{RX,TX}_MODE_VEC_SSE bit is never set.
sxe2_{rx,tx}_vec_support_check() always seeds *vec_flags with
either SXE2_*_MODE_VEC_SIMPLE or SXE2_*_MODE_VEC_OFFLOAD, both
of which are in SXE2_*_MODE_VEC_SET_MASK. In the mode-set
function:
tx_mode_flags = vec_flags;
#ifdef RTE_ARCH_X86
if ((tx_mode_flags & SXE2_TX_MODE_VEC_SET_MASK) == 0)
tx_mode_flags |= SXE2_TX_MODE_VEC_SSE;
#endif
the condition is already false because vec_flags set at least
the SIMPLE bit, so VEC_SSE is never OR'd in. The Rx side has
the identical pattern, with the additional dead duplicate
"rte_vect_get_max_simd_bitwidth() >= RTE_VECT_SIMD_128" check
that the surrounding if already verified. Either the
*_vec_support_check() helpers should seed only the OFFLOAD bit
and let the mode-set function pick SSE/AVX2/AVX512 from CPU
features, or the dead conditional should be removed.
As-is, the bursts dev->tx_pkt_burst = sxe2_tx_pkts_vec_sse and
dev->rx_pkt_burst = sxe2_rx_pkts_scattered_vec_sse_offload get
selected purely on the SET_MASK presence — the SSE-specific bit
never participates. This works on x86 (it's gated by
#ifdef RTE_ARCH_X86), but the VEC_SSE / VEC_AVX2 / VEC_AVX512 /
VEC_NEON bit definitions are then misleading: no code path
actually distinguishes them.
Series hygiene
--------------
Info: log strings "Open fd=%d to handshark with kernel" and
"Failed to handshark, ..." are introduced in patch 03 with the
typo, and corrected to "handshake" in patch 05. The function
itself is correctly named sxe2_drv_dev_handshake throughout,
but the messages inside it disagree with the function name in
patches 03 and 04. Two options: fold the typo fix into patch
03, or just write the log strings correctly the first time.
Info: dead "if (ret != 0) PMD_LOG_ERR(...)" check after two
void-returning sxe2_{rx,tx}_mode_func_set() calls was added in
patch 08 and removed in patch 09 of v15; in v16 the same pattern
appears in patch 08 (added) and patch 09 (removed). Same
within-series churn as before — please collapse.
^ permalink raw reply
* Re: [PATCH v13 0/5] Support add/remove memory region and get-max-slots
From: Stephen Hemminger @ 2026-05-18 17:13 UTC (permalink / raw)
To: pravin.bathija; +Cc: dev, fengchengwen, maxime.coquelin, thomas
In-Reply-To: <20260514020157.1937404-1-pravin.bathija@dell.com>
On Thu, 14 May 2026 02:01:52 +0000
<pravin.bathija@dell.com> wrote:
> From: Pravin M Bathija <pravin.bathija@dell.com>
>
> This is version v13 of the patchset and it incorporates the
> recommendations made by Fengcheng Wen.
>
> Changes made to patch 3/5 and 4/5
> * Relocated function remove_guest_pages from patch 3/5 to 4/5.
> * Renamed VhostUserSingleMemReg to VhostUserMemRegMsg and memory_single
> to memreg.
>
> This implementation has been extensively tested by doing Read/Write I/O
> from multiple instances of fio + libblkio (front-end) talking to
> spdk/dpdk (back-end) based drives. Tested with qemu front-end talking to
> dpdk testpmd (back-end) performing add/removal of memory regions. Also
> tested post-copy live migration after doing add_memory_region.
>
> Version Log:
> Version v13 (Current version): Incorporate code review suggestions from
> Fengcheng Wen as described above.
> Version v12: Incorporate code review suggestions from Maxime Coquelin
> and ai-code-review.
> Changes made to patch 3/5
> Refactored async_dma_map() to delegate to async_dma_map_region(),
> eliminating code duplication between the two functions.
> Restored original comments in async_dma_map_region() explaining why
> ENODEV and EINVAL errors are ignored (these were stripped in v10)
> Reverted unnecessary changes to vhost_user_postcopy_register() --
> removed the host_user_addr == 0 checks and reg_msg_index indirection
> that were added in v10, since this function is only called from
> vhost_user_set_mem_table() where regions are always contiguous.
>
> Version v11: Incorporate code review suggestions from Stephen Hemminger.
> Change made to patch 4/5
> Fix incomplete cleanup in vhost_user_add_mem_reg() when
> vhost_user_mmap_region() fails after the mmap succeeds (e.g.
> add_guest_pages() realloc failure) realloc failure). The error path now
> calls remove_guest_pages() and free_mem_region() to undo the mapping
> and stale guest-page entries, preventing a leaked mmap and slot reuse
> corruption. The plain close(fd) path is kept for pre-mmap failures.
>
> Version v10: Incorporate code review suggestions from Stephen Hemminger.
> Change made to patch 4/5
> Moved dev_invalidate_vrings after free_mem_region, array compaction, and
> nregions decrement. This ensures translate_ring_addresses only sees
> surviving memory regions, preventing vring pointers from resolving into
> a region that is about to be unmapped.
>
> Version v9: Incorporate code review suggestions from Stephen Hemminger.
> Changes made to patch 3/5
> Restored max_guest_pages initial value to hardcoded 8 instead of
> VHOST_MEMORY_MAX_NREGIONS, matching upstream semantics.
> Changes made to patch 4/5
> Added close(reg->fd) and reg->fd = -1 before goto close_msg_fds in the
> mmap failure path to fix fd leak after fd was moved from ctx->fds[0].
> Converted dev_invalidate_vrings from a plain function to a macro +
> implementation function pair, accepting message ID as a parameter so
> the static_assert reports the correct handler at each call site.
> Updated dev_invalidate_vrings call in add_mem_reg to pass
> VHOST_USER_ADD_MEM_REG as message ID.
> Updated dev_invalidate_vrings call in rem_mem_reg to pass
> VHOST_USER_REM_MEM_REG as message ID.
>
> Version v8: Incorporate code review suggestions from Stephen Hemminger.
> rewrite async_dma_map_region function to iterate guest pages by host
> address range matching
> change function dev_invalidate_vrings to accept a double pointer to
> propagate pointer updates
> new function remove_guest_pages was added
> add_mem_reg error path was narrowed to only clean up the single failed
> region instead of destroting all existing regions
>
> Version v7: Incorporate code review suggestions from Maxime Coquelin.
> Add debug messages to vhost_postcopy_register function.
>
> Version v6: Added the enablement of this feature as a final patch in
> this patch-set and other code optimizations as suggested by Maxime
> Coquelin.
>
> Version v5: removed the patch that increased the number of memory regions
> from 8 to 128. This will be submitted as a separate feature at a later
> point after incorporating additional optimizations. Also includes code
> optimizations as suggested by Feng Cheng Wen.
>
> Version v4: code optimizations as suggested by Feng Cheng Wen.
>
> Version v3: code optimizations as suggested by Maxime Coquelin
> and Thomas Monjalon.
>
> Version v2: code optimizations as suggested by Maxime Coquelin.
>
> Version v1: Initial patch set.
>
> Pravin M Bathija (5):
> vhost: add user to mailmap and define to vhost hdr
> vhost_user: header defines for add/rem mem region
> vhost_user: support function defines for back-end
> vhost_user: Function defs for add/rem mem regions
> vhost_user: enable configure memory slots
>
> .mailmap | 1 +
> lib/vhost/rte_vhost.h | 4 +
> lib/vhost/vhost_user.c | 418 +++++++++++++++++++++++++++++++++++------
> lib/vhost/vhost_user.h | 10 +
> 4 files changed, 371 insertions(+), 62 deletions(-)
>
Some useful AI feedback
Review of [PATCH v13 0-5/5] vhost: configure memory slots support
Author: Pravin M Bathija <pravin.bathija@dell.com>
This revision addresses essentially every correctness issue raised in
the v7-v12 reviews:
- ctx->fds[0] = -1 ownership transfer is now done before mmap, and
the mmap-failure path closes reg->fd explicitly when mmap never
set reg->mmap_addr.
- _dev_invalidate_vrings now takes struct virtio_net **pdev and
writes back *pdev = dev at the end, so a numa_realloc inside
translate_ring_addresses propagates correctly. Both call sites
refresh "dev = *pdev;" afterwards.
- The dev_invalidate_vrings() macro now takes the message id and
uses static_assert(id ## _LOCK_ALL_QPS, ...), matching the
existing VHOST_USER_ASSERT_LOCK pattern. Works for both
VHOST_USER_ADD_MEM_REG and VHOST_USER_REM_MEM_REG call sites.
- Overlap check in vhost_user_add_mem_reg uses guest address
space (guest_user_addr, size / userspace_addr, memory_size),
no longer mmap_size.
- free_new_region undoes only the failed region: async DMA unmap,
remove_guest_pages, free_mem_region(reg), nregions--.
- async_dma_map_region iterates dev->nr_guest_pages and filters
by [reg_start, reg_end), eliminating the prior reg_size
underflow loop.
- The regions array is kept contiguous via memmove on REM_MEM_REG,
so existing iterators that walk mem->nregions remain correct.
- max_guest_pages is back to 8 in vhost_user_initialize_memory.
One protocol-level issue remains worth raising.
Patch 4/5 -- vhost_user: Function defs for add/rem mem regions
--------------------------------------------------------------------
Warning: ADD_MEM_REG does not send the host_user_addr reply
Per the vhost-user spec for VHOST_USER_ADD_MEM_REG, the back-end
is expected to reply with the same message format and the
userspace_addr field replaced by the host userspace address that
the region was mapped into. The handler returns
RTE_VHOST_MSG_RESULT_OK with no reply constructed, so the
dispatcher does not call send_vhost_reply().
For postcopy migration this matters in particular: the original
vhost_user_postcopy_register() does two things -- exchange the
host_user_addr with the front-end and wait for an ack, then
register the regions with userfaultfd. The patch only does the
userfaultfd registration via vhost_user_postcopy_region_register().
The in-code comment notes the payload-layout mismatch with
vhost_user_postcopy_register() but stops there.
Without the address reply, QEMU will not know the back-end's
mapping for regions added via ADD_MEM_REG, so the userfaultfd
handling on the QEMU side cannot resolve faults in those
regions. Postcopy migration combined with the
CONFIGURE_MEM_SLOTS feature will not work.
Suggested fix: construct a memreg-payload reply with
region->userspace_addr replaced by reg->host_user_addr and
return RTE_VHOST_MSG_RESULT_REPLY. At minimum, refuse
ADD_MEM_REG when dev->postcopy_listening is set, so that the
combination fails cleanly rather than silently mis-mapping.
Info: vhost_user_rem_mem_reg does not validate ctx->fd_num
The handler is registered with accepts_fd = true and does not
call validate_msg_fds(). The trailing close_msg_fds(ctx) cleans
up whatever fds were passed, so this is not a leak, but a
malformed message with an unexpected fd count is silently
accepted. The other accepts_fd handlers in this file validate
fd_num explicitly.
Info: vhost_user_get_max_mem_slots cast is unnecessary
ctx->msg.payload.u64 = (uint64_t)max_mem_slots;
max_mem_slots is uint32_t and the assignment widens
automatically; the cast can be dropped. Minor.
Reviewed-by would be appropriate once the postcopy reply is
addressed (or the combination is rejected). The rest of the
series looks correct.
^ permalink raw reply
* Re: [PATCH] eal: silence -Wconstant-logical-operand in RTE_IS_POWER_OF_2
From: Stephen Hemminger @ 2026-05-18 17:10 UTC (permalink / raw)
To: Bruce Richardson
Cc: dev, stable, Jack Bond-Preston, Gavin Hu, Honnappa Nagarahalli,
Pablo de Lara
In-Reply-To: <agtEqEv2fB7R7_Aa@bricha3-mobl1.ger.corp.intel.com>
On Mon, 18 May 2026 17:56:08 +0100
Bruce Richardson <bruce.richardson@intel.com> wrote:
> I'd also suggest changing the second part of the && to match, changing the
> "!" for explicit "== 0":
>
> #define RTE_IS_POWER_OF_2(n) ((n) != 0 && (((n) - 1) & (n)) == 0)
Was heading for the smallest possible part
^ permalink raw reply
* Re: [PATCH] eal: silence -Wconstant-logical-operand in RTE_IS_POWER_OF_2
From: Bruce Richardson @ 2026-05-18 16:56 UTC (permalink / raw)
To: Stephen Hemminger
Cc: dev, stable, Jack Bond-Preston, Gavin Hu, Honnappa Nagarahalli,
Pablo de Lara
In-Reply-To: <20260518163401.580696-1-stephen@networkplumber.org>
On Mon, May 18, 2026 at 09:33:31AM -0700, Stephen Hemminger wrote:
> Newer GCC warns when a non-boolean constant is an operand of &&, which
> trips whenever RTE_IS_POWER_OF_2 is used in a static_assert with a
> power-of-two literal. Make the zero check explicit.
>
> Fixes: 7c872b96983a ("hash: validate hash bucket entries while compiling")
> Cc: stable@dpdk.org
>
> Signed-off-by: Stephen Hemminger <stephen@networkplumber.org>
Acked-by: Bruce Richardson <bruce.richardson@intel.com>
One further suggestion below, since you are changing this.
> ---
> lib/eal/include/rte_bitops.h | 2 +-
> 1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/lib/eal/include/rte_bitops.h b/lib/eal/include/rte_bitops.h
> index aa6ac73abb..d2719ecd5e 100644
> --- a/lib/eal/include/rte_bitops.h
> +++ b/lib/eal/include/rte_bitops.h
> @@ -1299,7 +1299,7 @@ rte_fls_u64(uint64_t x)
> /**
> * Macro to return 1 if n is a power of 2, 0 otherwise
> */
> -#define RTE_IS_POWER_OF_2(n) ((n) && !(((n) - 1) & (n)))
> +#define RTE_IS_POWER_OF_2(n) ((n) != 0 && !(((n) - 1) & (n)))
>
I'd also suggest changing the second part of the && to match, changing the
"!" for explicit "== 0":
#define RTE_IS_POWER_OF_2(n) ((n) != 0 && (((n) - 1) & (n)) == 0)
> /**
> * Returns true if n is a power of 2
> --
> 2.53.0
>
^ permalink raw reply
* [RFC] eal: enable thread safety analysis for recursive lock
From: Stephen Hemminger @ 2026-05-18 16:34 UTC (permalink / raw)
To: dev; +Cc: Stephen Hemminger
The recursive spinlock API was annotated with
__rte_no_thread_safety_analysis on each definition, with no capability
annotations on any declaration. As a result, callers received no
static checking: fields marked __rte_guarded_by() on a recursive
spinlock were not verified, and rte_spinlock_recursive_t was not
recognized as a capability at all.
Signed-off-by: Stephen Hemminger <stephen@networkplumber.org>
---
lib/eal/include/generic/rte_spinlock.h | 28 +++++++++++++++++++++++++-
1 file changed, 27 insertions(+), 1 deletion(-)
diff --git a/lib/eal/include/generic/rte_spinlock.h b/lib/eal/include/generic/rte_spinlock.h
index 5d810b682a..e489f846f6 100644
--- a/lib/eal/include/generic/rte_spinlock.h
+++ b/lib/eal/include/generic/rte_spinlock.h
@@ -195,7 +195,7 @@ rte_spinlock_trylock_tm(rte_spinlock_t *sl)
/**
* The rte_spinlock_recursive_t type.
*/
-typedef struct {
+typedef struct __rte_capability("recursive_spinlock") {
rte_spinlock_t sl; /**< the actual spinlock */
RTE_ATOMIC(int) user; /**< core id using lock, -1 for unused */
int count; /**< count of time this lock has been called */
@@ -219,6 +219,32 @@ static inline void rte_spinlock_recursive_init(rte_spinlock_recursive_t *slr)
slr->count = 0;
}
+/*
+ * TSA models the recursive spinlock as a single capability: callers
+ * get the same __rte_guarded_by() checking as the non-recursive API.
+ * Nested acquires by the owner are invisible to the analyzer — TSA
+ * has no model for re-entrance, so mismatched recursive lock/unlock
+ * counts remain a runtime concern.
+ *
+ * The definitions use __rte_no_thread_safety_analysis because the
+ * inner rte_spinlock_lock() is conditional on the owner check, which
+ * TSA cannot reason about. Annotations on the declarations are what
+ * callers see.
+ */
+
+static inline void
+rte_spinlock_recursive_lock(rte_spinlock_recursive_t *slr)
+ __rte_acquire_capability(slr);
+
+static inline void
+rte_spinlock_recursive_unlock(rte_spinlock_recursive_t *slr)
+ __rte_release_capability(slr);
+
+__rte_warn_unused_result
+static inline int
+rte_spinlock_recursive_trylock(rte_spinlock_recursive_t *slr)
+ __rte_try_acquire_capability(true, slr);
+
/**
* Take the recursive spinlock.
*
--
2.53.0
^ permalink raw reply related
* [PATCH] eal: silence -Wconstant-logical-operand in RTE_IS_POWER_OF_2
From: Stephen Hemminger @ 2026-05-18 16:33 UTC (permalink / raw)
To: dev
Cc: Stephen Hemminger, stable, Jack Bond-Preston, Gavin Hu,
Honnappa Nagarahalli, Pablo de Lara
Newer GCC warns when a non-boolean constant is an operand of &&, which
trips whenever RTE_IS_POWER_OF_2 is used in a static_assert with a
power-of-two literal. Make the zero check explicit.
Fixes: 7c872b96983a ("hash: validate hash bucket entries while compiling")
Cc: stable@dpdk.org
Signed-off-by: Stephen Hemminger <stephen@networkplumber.org>
---
lib/eal/include/rte_bitops.h | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/lib/eal/include/rte_bitops.h b/lib/eal/include/rte_bitops.h
index aa6ac73abb..d2719ecd5e 100644
--- a/lib/eal/include/rte_bitops.h
+++ b/lib/eal/include/rte_bitops.h
@@ -1299,7 +1299,7 @@ rte_fls_u64(uint64_t x)
/**
* Macro to return 1 if n is a power of 2, 0 otherwise
*/
-#define RTE_IS_POWER_OF_2(n) ((n) && !(((n) - 1) & (n)))
+#define RTE_IS_POWER_OF_2(n) ((n) != 0 && !(((n) - 1) & (n)))
/**
* Returns true if n is a power of 2
--
2.53.0
^ permalink raw reply related
* Re: [PATCH v2 1/2] spinlock: remove volatile qualifier
From: Stephen Hemminger @ 2026-05-18 16:28 UTC (permalink / raw)
To: Thomas Monjalon
Cc: dev, Konstantin Ananyev, Bruce Richardson, Robin Jarry, stable
In-Reply-To: <20260518150819.3332628-1-thomas@monjalon.net>
On Mon, 18 May 2026 17:07:35 +0200
Thomas Monjalon <thomas@monjalon.net> wrote:
> When compiling with C++20 standard requirement (default in GCC 16),
> the increment and decrement of volatile variables are rejected:
>
> rte_spinlock.h:241:14: error:
> '++' expression of 'volatile'-qualified type is deprecated
> rte_spinlock.h:252:21: error:
> '--' expression of 'volatile'-qualified type is deprecated
> rte_spinlock.h:278:14: error:
> '++' expression of 'volatile'-qualified type is deprecated
>
> The count field of rte_spinlock_recursive_t
> does not need the volatile qualifier
> because it is only accessed by the thread holding the lock,
> which already provides the necessary memory ordering.
>
> The user field can be accessed outside of the lock,
> so it must handled as a C11 atomic variable.
>
> Fixes: af75078fece3 ("first public release")
> Cc: stable@dpdk.org
>
> Signed-off-by: Thomas Monjalon <thomas@monjalon.net>
I would also suggest renaming 'user' to 'owner' to follow common
practice in other places. It also provides an intentional API
break if somebody is misguided enough to do direct access to the
structure field.
^ permalink raw reply
* Re: [PATCH v2 1/2] spinlock: remove volatile qualifier
From: Stephen Hemminger @ 2026-05-18 16:26 UTC (permalink / raw)
To: Robin Jarry
Cc: Thomas Monjalon, dev, Konstantin Ananyev, Bruce Richardson,
stable
In-Reply-To: <DILWG1XCA7NV.33OR3FAYCVVI0@redhat.com>
On Mon, 18 May 2026 17:14:54 +0200
"Robin Jarry" <rjarry@redhat.com> wrote:
> Hey Thomas,
>
> Thomas Monjalon, May 18, 2026 at 17:07:
> > When compiling with C++20 standard requirement (default in GCC 16),
> > the increment and decrement of volatile variables are rejected:
> >
> > rte_spinlock.h:241:14: error:
> > '++' expression of 'volatile'-qualified type is deprecated
> > rte_spinlock.h:252:21: error:
> > '--' expression of 'volatile'-qualified type is deprecated
> > rte_spinlock.h:278:14: error:
> > '++' expression of 'volatile'-qualified type is deprecated
> >
> > The count field of rte_spinlock_recursive_t
> > does not need the volatile qualifier
> > because it is only accessed by the thread holding the lock,
> > which already provides the necessary memory ordering.
> >
> > The user field can be accessed outside of the lock,
> > so it must handled as a C11 atomic variable.
> >
> > Fixes: af75078fece3 ("first public release")
> > Cc: stable@dpdk.org
> >
> > Signed-off-by: Thomas Monjalon <thomas@monjalon.net>
> > ---
> > v1: drop volatile keyword
> > v2: make user an atomic variable
> > ---
> > lib/eal/include/generic/rte_spinlock.h | 17 ++++++++---------
> > 1 file changed, 8 insertions(+), 9 deletions(-)
> >
> > diff --git a/lib/eal/include/generic/rte_spinlock.h b/lib/eal/include/generic/rte_spinlock.h
> > index c907d4e45c..5d810b682a 100644
> > --- a/lib/eal/include/generic/rte_spinlock.h
> > +++ b/lib/eal/include/generic/rte_spinlock.h
> > @@ -197,8 +197,8 @@ rte_spinlock_trylock_tm(rte_spinlock_t *sl)
> > */
> > typedef struct {
> > rte_spinlock_t sl; /**< the actual spinlock */
> > - volatile int user; /**< core id using lock, -1 for unused */
> > - volatile int count; /**< count of time this lock has been called */
> > + RTE_ATOMIC(int) user; /**< core id using lock, -1 for unused */
> > + int count; /**< count of time this lock has been called */
> > } rte_spinlock_recursive_t;
> >
> > /**
> > @@ -215,7 +215,7 @@ typedef struct {
> > static inline void rte_spinlock_recursive_init(rte_spinlock_recursive_t *slr)
> > {
> > rte_spinlock_init(&slr->sl);
> > - slr->user = -1;
> > + rte_atomic_store_explicit(&slr->user, -1, rte_memory_order_relaxed);
> > slr->count = 0;
> > }
> >
> > @@ -230,9 +230,9 @@ static inline void rte_spinlock_recursive_lock(rte_spinlock_recursive_t *slr)
> > {
> > int id = rte_gettid();
> >
> > - if (slr->user != id) {
> > + if (rte_atomic_load_explicit(&slr->user, rte_memory_order_relaxed) != id) {
>
> This needs to be rte_memory_order_acquire
No it does not. There is no dependency here.
The acquire is inside the spinlock.
>
> > rte_spinlock_lock(&slr->sl);
> > - slr->user = id;
> > + rte_atomic_store_explicit(&slr->user, id, rte_memory_order_relaxed);
>
> And rte_memory_order_release
Ditto.
>
> > }
> > slr->count++;
> > }
> > @@ -246,10 +246,9 @@ static inline void rte_spinlock_recursive_unlock(rte_spinlock_recursive_t *slr)
> > __rte_no_thread_safety_analysis
> > {
> > if (--(slr->count) == 0) {
>
> This code is completely broken. Any thread can unlock without any check.
I would make an RTE_ASSERT() that id matched current thread id.
Since caller holds lock, no atomic required for that.
^ permalink raw reply
* Re: [PATCH v2 1/2] spinlock: remove volatile qualifier
From: Bruce Richardson @ 2026-05-18 15:28 UTC (permalink / raw)
To: Thomas Monjalon
Cc: Robin Jarry, dev, Stephen Hemminger, Konstantin Ananyev, stable
In-Reply-To: <kagkiNyiQQ67llvhX0AB8g@monjalon.net>
On Mon, May 18, 2026 at 05:25:43PM +0200, Thomas Monjalon wrote:
> 18/05/2026 17:14, Robin Jarry:
> > Hey Thomas,
> >
> > Thomas Monjalon, May 18, 2026 at 17:07:
<snip>
>
> [...]
> > > @@ -246,10 +246,9 @@ static inline void rte_spinlock_recursive_unlock(rte_spinlock_recursive_t *slr)
> > > __rte_no_thread_safety_analysis
> > > {
> > > if (--(slr->count) == 0) {
> >
> > This code is completely broken. Any thread can unlock without any check.
>
> Maybe, but I don't intend to fix recursive unlock in this patch.
> The subject is "remove volatile qualifier" (and unblock GCC 16).
>
As with regular mutexes, threads should not go around unlocking when not
holding the lock. Therefore, I think this is fine. [With regular spinlocks
we can't check since we don't track threads, therefore I think we just need
to assume a well-behaved app.]
/Bruce
^ permalink raw reply
* Re: [PATCH v2 1/2] spinlock: remove volatile qualifier
From: Thomas Monjalon @ 2026-05-18 15:25 UTC (permalink / raw)
To: Robin Jarry
Cc: dev, Stephen Hemminger, Konstantin Ananyev, Bruce Richardson,
stable
In-Reply-To: <DILWG1XCA7NV.33OR3FAYCVVI0@redhat.com>
18/05/2026 17:14, Robin Jarry:
> Hey Thomas,
>
> Thomas Monjalon, May 18, 2026 at 17:07:
> > When compiling with C++20 standard requirement (default in GCC 16),
> > the increment and decrement of volatile variables are rejected:
> >
> > rte_spinlock.h:241:14: error:
> > '++' expression of 'volatile'-qualified type is deprecated
> > rte_spinlock.h:252:21: error:
> > '--' expression of 'volatile'-qualified type is deprecated
> > rte_spinlock.h:278:14: error:
> > '++' expression of 'volatile'-qualified type is deprecated
> >
> > The count field of rte_spinlock_recursive_t
> > does not need the volatile qualifier
> > because it is only accessed by the thread holding the lock,
> > which already provides the necessary memory ordering.
> >
> > The user field can be accessed outside of the lock,
> > so it must handled as a C11 atomic variable.
> >
> > Fixes: af75078fece3 ("first public release")
> > Cc: stable@dpdk.org
> >
> > Signed-off-by: Thomas Monjalon <thomas@monjalon.net>
> > ---
> > v1: drop volatile keyword
> > v2: make user an atomic variable
> > ---
> > typedef struct {
> > rte_spinlock_t sl; /**< the actual spinlock */
> > - volatile int user; /**< core id using lock, -1 for unused */
> > - volatile int count; /**< count of time this lock has been called */
> > + RTE_ATOMIC(int) user; /**< core id using lock, -1 for unused */
> > + int count; /**< count of time this lock has been called */
> > } rte_spinlock_recursive_t;
[...]
> > @@ -230,9 +230,9 @@ static inline void rte_spinlock_recursive_lock(rte_spinlock_recursive_t *slr)
> > {
> > int id = rte_gettid();
> >
> > - if (slr->user != id) {
> > + if (rte_atomic_load_explicit(&slr->user, rte_memory_order_relaxed) != id) {
>
> This needs to be rte_memory_order_acquire
The memory ordering is managed with the sl variable.
The variable user has just to be atomic, so relaxed should be enough,
am I wrong?
[...]
> > @@ -246,10 +246,9 @@ static inline void rte_spinlock_recursive_unlock(rte_spinlock_recursive_t *slr)
> > __rte_no_thread_safety_analysis
> > {
> > if (--(slr->count) == 0) {
>
> This code is completely broken. Any thread can unlock without any check.
Maybe, but I don't intend to fix recursive unlock in this patch.
The subject is "remove volatile qualifier" (and unblock GCC 16).
^ permalink raw reply
* Re: [PATCH v2 1/2] spinlock: remove volatile qualifier
From: Robin Jarry @ 2026-05-18 15:14 UTC (permalink / raw)
To: Thomas Monjalon, dev
Cc: Stephen Hemminger, Konstantin Ananyev, Bruce Richardson, stable
In-Reply-To: <20260518150819.3332628-1-thomas@monjalon.net>
Hey Thomas,
Thomas Monjalon, May 18, 2026 at 17:07:
> When compiling with C++20 standard requirement (default in GCC 16),
> the increment and decrement of volatile variables are rejected:
>
> rte_spinlock.h:241:14: error:
> '++' expression of 'volatile'-qualified type is deprecated
> rte_spinlock.h:252:21: error:
> '--' expression of 'volatile'-qualified type is deprecated
> rte_spinlock.h:278:14: error:
> '++' expression of 'volatile'-qualified type is deprecated
>
> The count field of rte_spinlock_recursive_t
> does not need the volatile qualifier
> because it is only accessed by the thread holding the lock,
> which already provides the necessary memory ordering.
>
> The user field can be accessed outside of the lock,
> so it must handled as a C11 atomic variable.
>
> Fixes: af75078fece3 ("first public release")
> Cc: stable@dpdk.org
>
> Signed-off-by: Thomas Monjalon <thomas@monjalon.net>
> ---
> v1: drop volatile keyword
> v2: make user an atomic variable
> ---
> lib/eal/include/generic/rte_spinlock.h | 17 ++++++++---------
> 1 file changed, 8 insertions(+), 9 deletions(-)
>
> diff --git a/lib/eal/include/generic/rte_spinlock.h b/lib/eal/include/generic/rte_spinlock.h
> index c907d4e45c..5d810b682a 100644
> --- a/lib/eal/include/generic/rte_spinlock.h
> +++ b/lib/eal/include/generic/rte_spinlock.h
> @@ -197,8 +197,8 @@ rte_spinlock_trylock_tm(rte_spinlock_t *sl)
> */
> typedef struct {
> rte_spinlock_t sl; /**< the actual spinlock */
> - volatile int user; /**< core id using lock, -1 for unused */
> - volatile int count; /**< count of time this lock has been called */
> + RTE_ATOMIC(int) user; /**< core id using lock, -1 for unused */
> + int count; /**< count of time this lock has been called */
> } rte_spinlock_recursive_t;
>
> /**
> @@ -215,7 +215,7 @@ typedef struct {
> static inline void rte_spinlock_recursive_init(rte_spinlock_recursive_t *slr)
> {
> rte_spinlock_init(&slr->sl);
> - slr->user = -1;
> + rte_atomic_store_explicit(&slr->user, -1, rte_memory_order_relaxed);
> slr->count = 0;
> }
>
> @@ -230,9 +230,9 @@ static inline void rte_spinlock_recursive_lock(rte_spinlock_recursive_t *slr)
> {
> int id = rte_gettid();
>
> - if (slr->user != id) {
> + if (rte_atomic_load_explicit(&slr->user, rte_memory_order_relaxed) != id) {
This needs to be rte_memory_order_acquire
> rte_spinlock_lock(&slr->sl);
> - slr->user = id;
> + rte_atomic_store_explicit(&slr->user, id, rte_memory_order_relaxed);
And rte_memory_order_release
> }
> slr->count++;
> }
> @@ -246,10 +246,9 @@ static inline void rte_spinlock_recursive_unlock(rte_spinlock_recursive_t *slr)
> __rte_no_thread_safety_analysis
> {
> if (--(slr->count) == 0) {
This code is completely broken. Any thread can unlock without any check.
I think it should be:
diff --git a/lib/eal/include/generic/rte_spinlock.h b/lib/eal/include/generic/rte_spinlock.h
index c907d4e45c39..b1058e4f8b4f 100644
--- a/lib/eal/include/generic/rte_spinlock.h
+++ b/lib/eal/include/generic/rte_spinlock.h
@@ -245,11 +247,17 @@ static inline void rte_spinlock_recursive_lock(rte_spinlock_recursive_t *slr)
static inline void rte_spinlock_recursive_unlock(rte_spinlock_recursive_t *slr)
__rte_no_thread_safety_analysis
{
- if (--(slr->count) == 0) {
- slr->user = -1;
- rte_spinlock_unlock(&slr->sl);
- }
+ int id = rte_gettid();
+ if (rte_atomic_load_explicit(&slr->user, rte_memory_order_acquire) == id) {
+ RTE_ASSERT(slr->count > 0);
+ if (--(slr->count) == 0) {
+ rte_atomic_store_explicit(&slr->user, -1, rte_memory_order_release);
+ rte_spinlock_unlock(&slr->sl);
+ }
+ } else {
+ RTE_ASSERT(id != -1 && "unlocked from another thread");
+ }
}
(not tested)
> - slr->user = -1;
> + rte_atomic_store_explicit(&slr->user, -1, rte_memory_order_relaxed);
> rte_spinlock_unlock(&slr->sl);
> }
> -
> }
>
> /**
> @@ -266,10 +265,10 @@ static inline int rte_spinlock_recursive_trylock(rte_spinlock_recursive_t *slr)
> {
> int id = rte_gettid();
>
> - if (slr->user != id) {
> + if (rte_atomic_load_explicit(&slr->user, rte_memory_order_relaxed) != id) {
rte_memory_order_acquire
> if (rte_spinlock_trylock(&slr->sl) == 0)
> return 0;
> - slr->user = id;
> + rte_atomic_store_explicit(&slr->user, id, rte_memory_order_relaxed);
rte_memory_order_release
> }
> slr->count++;
> return 1;
--
Robin
> Monitored by the American Human Association.
^ permalink raw reply related
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