[PATCH v1 2/3] raw/ntb: add AMD NTB support
Bruce Richardson
bruce.richardson at intel.com
Fri Sep 25 12:22:17 CEST 2026
On Sun, Aug 23, 2026 at 07:36:38PM +0530, Raghavendra Ningoji wrote:
> Add support for the NTB endpoints integrated in AMD EPYC Embedded
> "Turin", "Genoa" and "Siena" processors to the raw/ntb driver.
>
> The AMD NTB uses a primary/secondary topology: one endpoint enumerates
> as the primary (device ID 0x14c0) and the other as the secondary
> (device ID 0x14c3). The hardware exposes two memory windows (BAR23 and
> BAR45), 16 doorbells and a single shared 16-register scratchpad bank.
> The scratchpad bank is split into two disjoint 8-register sets, one per
> side, so the driver uses a packed handshake layout that fits in 8
> registers, plugged in through the framework's dev_handshake and
> read_peer_config hooks. A vendor-specific MSI-X interrupt handler is
> provided through the interrupt_handler hook.
>
> AMD NTB uses an outbound translation window: a write to a BARxx memory
> window offset is forwarded to (xlat_base | offset) in the peer's memory
> rather than (xlat_base + offset). For that to be correct the
> translation base must be aligned to a power of two >= the window length
> so that no offset bit collides with a set bit in the base; the XLAT
> register also requires at least 4K alignment. Report this requirement
> to applications through a new mw_addr_align field in struct
> ntb_dev_info, reject a misaligned base in amd_ntb_mw_set_trans, and
> honour the field when reserving the memzone in the ntb example.
>
> On the secondary side the device's own PCIe link status does not
> reflect the true inter-host link, so the link speed and width are read
> from the upstream switch port via sysfs.
>
> Signed-off-by: Raghavendra Ningoji <raghavendra.ningoji at amd.com>
> ---
> drivers/raw/ntb/meson.build | 3 +-
> drivers/raw/ntb/ntb.c | 21 +
> drivers/raw/ntb/ntb_hw_amd.c | 709 ++++++++++++++++++++++++++++++++++
> drivers/raw/ntb/ntb_hw_amd.h | 114 ++++++
> drivers/raw/ntb/rte_pmd_ntb.h | 10 +
> examples/ntb/ntb_fwd.c | 22 +-
> usertools/dpdk-devbind.py | 4 +-
> 7 files changed, 879 insertions(+), 4 deletions(-)
> create mode 100644 drivers/raw/ntb/ntb_hw_amd.c
> create mode 100644 drivers/raw/ntb/ntb_hw_amd.h
>
Reviewing changes to common code only, please see inline below. I think
there is quite a bit of complexity introduced by the alignment constraints
which could do with being simplified.
Thanks,
/Bruce
> diff --git a/drivers/raw/ntb/meson.build b/drivers/raw/ntb/meson.build
> index 9096f2b25a..d7a8f2d1ed 100644
> --- a/drivers/raw/ntb/meson.build
> +++ b/drivers/raw/ntb/meson.build
> @@ -3,5 +3,6 @@
>
> deps += ['rawdev', 'mbuf', 'mempool', 'pci', 'bus_pci']
> sources = files('ntb.c',
> - 'ntb_hw_intel.c')
> + 'ntb_hw_intel.c',
> + 'ntb_hw_amd.c')
Very minor nit, but consider putting the ")" on the next line and putting a
comma after the 'ntb_hw_amd.c' entry (since meson allows a trailing comma).
This means that new entries can be added without having to modify any
existing lines.
> headers = files('rte_pmd_ntb.h')
> diff --git a/drivers/raw/ntb/ntb.c b/drivers/raw/ntb/ntb.c
> index 3a6a299081..b87141e4f4 100644
> --- a/drivers/raw/ntb/ntb.c
> +++ b/drivers/raw/ntb/ntb.c
> @@ -20,12 +20,15 @@
> #include <rte_rawdev_pmd.h>
>
> #include "ntb_hw_intel.h"
> +#include "ntb_hw_amd.h"
> #include "rte_pmd_ntb.h"
> #include "ntb.h"
>
> static const struct rte_pci_id pci_id_ntb_map[] = {
> { RTE_PCI_DEVICE(NTB_INTEL_VENDOR_ID, NTB_INTEL_DEV_ID_B2B_SKX) },
> { RTE_PCI_DEVICE(NTB_INTEL_VENDOR_ID, NTB_INTEL_DEV_ID_B2B_ICX) },
> + { RTE_PCI_DEVICE(NTB_AMD_VENDOR_ID, NTB_AMD_DEV_ID_PRI) },
> + { RTE_PCI_DEVICE(NTB_AMD_VENDOR_ID, NTB_AMD_DEV_ID_SEC) },
> { .vendor_id = 0, /* sentinel */ },
> };
>
> @@ -846,6 +849,20 @@ ntb_dev_info_get(struct rte_rawdev *dev, rte_rawdev_obj_t dev_info,
> info->mw_size_align = (uint8_t)(hw->pci_dev->id.vendor_id ==
> NTB_INTEL_VENDOR_ID);
>
> + /**
> + * AMD NTB uses an outbound translation window: writes to a BARxx
> + * memory window are forwarded to the peer via the XLAT registers,
> + * whose base must be 4K aligned. If the mw memzone base is not
> + * aligned, the low bits are dropped and all window writes land at
> + * the wrong offset. Report the required alignment so the memzone is
> + * reserved correctly. Intel uses mw_size_align (a superset), so this
> + * only matters for non-Intel vendors.
> + */
Consider shortening the coment here. Also see other feedback below
regarding this field.
> + if (hw->pci_dev->id.vendor_id == NTB_AMD_VENDOR_ID)
> + info->mw_addr_align = RTE_PGSIZE_4K;
> + else
> + info->mw_addr_align = 0;
> +
> if (!hw->queue_size || !hw->queue_pairs) {
> NTB_LOG(ERR, "No queue size and queue num assigned.");
> return -EAGAIN;
> @@ -1406,6 +1423,10 @@ ntb_init_hw(struct rte_rawdev *dev, struct rte_pci_device *pci_dev)
> case NTB_INTEL_DEV_ID_B2B_ICX:
> hw->ntb_ops = &intel_ntb_ops;
> break;
> + case NTB_AMD_DEV_ID_PRI:
> + case NTB_AMD_DEV_ID_SEC:
> + hw->ntb_ops = &amd_ntb_ops;
> + break;
> default:
> NTB_LOG(ERR, "Not supported device.");
> return -EINVAL;
> diff --git a/drivers/raw/ntb/ntb_hw_amd.c b/drivers/raw/ntb/ntb_hw_amd.c
> new file mode 100644
> index 0000000000..9861dcee57
> --- /dev/null
> +++ b/drivers/raw/ntb/ntb_hw_amd.c
<snip>
> diff --git a/drivers/raw/ntb/rte_pmd_ntb.h b/drivers/raw/ntb/rte_pmd_ntb.h
> index 76da3be026..59a2ad6849 100644
> --- a/drivers/raw/ntb/rte_pmd_ntb.h
> +++ b/drivers/raw/ntb/rte_pmd_ntb.h
> @@ -27,6 +27,16 @@ struct ntb_dev_info {
> uint8_t mw_size_align;
> uint8_t mw_cnt;
> uint64_t *mw_size;
> + /**< Minimum alignment (bytes) required for the mw translation base
> + * address, and a flag that the base must additionally be aligned to a
> + * power of two >= the mw length. 0 means no extra alignment beyond
> + * cache line. AMD NTB uses an outbound translation window that forms
> + * the target as (xlat_base | offset) instead of (xlat_base + offset),
> + * so the base must be size-aligned to avoid offset bits colliding with
> + * base bits; it also requires at least 4K alignment for the XLAT
> + * register.
> + */
> + uint64_t mw_addr_align;
Again, shorten the comment here to just a line or two.
Also, in terms of how it is used, remove the special case for 0 == cache
aligned, and instead change code assignment above to be cache aligned by
default. This means that all uses of this value in apps don't need to have a
special-case for it - they just align the memory allocation to what is
provided, be it 64-bytes or 4k.
> };
>
> struct ntb_dev_config {
> diff --git a/examples/ntb/ntb_fwd.c b/examples/ntb/ntb_fwd.c
> index 33f3c1ef17..7cc4e22147 100644
> --- a/examples/ntb/ntb_fwd.c
> +++ b/examples/ntb/ntb_fwd.c
> @@ -1146,8 +1146,10 @@ ntb_mbuf_pool_create(uint16_t mbuf_seg_size, uint32_t nb_mbuf,
> if (!left_sz)
> break;
> snprintf(mz_name, sizeof(mz_name), "ntb_mw_%d", mz_id);
> - align = ntb_info.mw_size_align ? ntb_info.mw_size[mz_id] :
> - RTE_CACHE_LINE_SIZE;
> + if (ntb_info.mw_size_align)
> + align = ntb_info.mw_size[mz_id];
> + else
> + align = RTE_CACHE_LINE_SIZE;
Again, see above comment. If we remove zero as a possible value here, you
can just use ntb_info.mw_size_align directly without branching in app code.
> /* Reserve ntb header space on memzone 0. */
> max_mz_len = mz_id ? ntb_info.mw_size[mz_id] :
> ntb_info.mw_size[mz_id] - ntb_info.ntb_hdr_size;
> @@ -1155,6 +1157,22 @@ ntb_mbuf_pool_create(uint16_t mbuf_seg_size, uint32_t nb_mbuf,
> (max_mz_len / total_elt_sz * total_elt_sz);
> if (!mz_len)
> continue;
> + /*
> + * Some NTB hardware (e.g. AMD) uses an outbound translation
> + * window that forms the target as (xlat_base | offset) rather
> + * than (xlat_base + offset). For that to be correct the memzone
> + * base must be aligned to a power of two >= its length, so that
> + * no offset bit collides with a set bit in the base address.
> + * Honour that requirement when the driver reports mw_addr_align.
> + */
> + if (ntb_info.mw_addr_align) {
> + uint64_t pow2_align = rte_align64pow2(mz_len);
> +
> + if (pow2_align > align)
> + align = pow2_align;
> + if (ntb_info.mw_addr_align > align)
> + align = ntb_info.mw_addr_align;
> + }
This is really complex to have in the app, and is hard for the user to
understand and work with too. I would suggest that, rather than trying to
expose this via a single addr_align value - which it turns out isn't
actually the alignment needed - you add a separate API called
"get_mem_align" or something similar, and then hide the complexity of this
calculation in the driver. Then you can drop the mw_addr_align in the info
struct.
> mz = rte_memzone_reserve_aligned(mz_name, mz_len, socket_id,
> RTE_MEMZONE_IOVA_CONTIG, align);
> if (mz == NULL) {
> diff --git a/usertools/dpdk-devbind.py b/usertools/dpdk-devbind.py
> index e72f238aba..cf5747b003 100755
> --- a/usertools/dpdk-devbind.py
> +++ b/usertools/dpdk-devbind.py
> @@ -78,6 +78,8 @@
> 'SVendor': None, 'SDevice': None}
> intel_ntb_icx = {'Class': '06', 'Vendor': '8086', 'Device': '347e',
> 'SVendor': None, 'SDevice': None}
> +amd_ntb = {'Class': '06', 'Vendor': '1022', 'Device': '14c0,14c3',
> + 'SVendor': None, 'SDevice': None}
>
> cnxk_sso = {'Class': '08', 'Vendor': '177d', 'Device': 'a0f9,a0fa',
> 'SVendor': None, 'SDevice': None}
> @@ -105,7 +107,7 @@
> regex_devices = [cn9k_ree]
> ml_devices = [cnxk_ml]
> misc_devices = [cnxk_bphy, cnxk_bphy_cgx, cnxk_inl_dev,
> - intel_ntb_skx, intel_ntb_icx,
> + intel_ntb_skx, intel_ntb_icx, amd_ntb,
> virtio_blk]
>
> # global dict ethernet devices present. Dictionary indexed by PCI address.
> --
> 2.34.1
>
More information about the dev
mailing list