* [PATCH 0/8] net/nfb: rework to real multiport
@ 2026-01-15 15:16 spinler
2026-01-15 15:16 ` [PATCH 1/8] net/nfb: prepare for indirect queue mapping scheme spinler
` (17 more replies)
0 siblings, 18 replies; 131+ messages in thread
From: spinler @ 2026-01-15 15:16 UTC (permalink / raw)
To: dev; +Cc: Martin Spinler
From: Martin Spinler <spinler@cesnet.cz>
This series implements real multiport for better user experience.
The existing driver creates one ethdev/port for one PCI device.
As the CESNET-NDK based cards aren't capable to represent each
Ethernet port by own PCI device, new driver implementation
processes real port configuration from firmware/card and switches
from rte_eth_dev_pci_generic_probe to multiple rte_eth_dev_create calls.
---
Depends-on: series-37064 ("net/nfb: code cleanup")
Martin Spinler (8):
net/nfb: prepare for indirect queue mapping scheme
net/nfb: create ethdev for every eth port/channel
net/nfb: add vdev as alternative device probe method
net/nfb: add device argument "port" to limit used ports
net/nfb: init only MACs associated with device
net/nfb: add compatible cards to driver PCI ID table
net/nfb: report firmware version
doc/nfb: cleanup and update guide
doc/guides/nics/features/nfb.ini | 4 +
doc/guides/nics/nfb.rst | 187 +++++++--------
drivers/net/nfb/meson.build | 1 +
drivers/net/nfb/nfb.h | 48 +++-
drivers/net/nfb/nfb_ethdev.c | 399 +++++++++++++++++++++++--------
drivers/net/nfb/nfb_rx.c | 21 +-
drivers/net/nfb/nfb_tx.c | 21 +-
drivers/net/nfb/nfb_vdev.c | 97 ++++++++
8 files changed, 551 insertions(+), 227 deletions(-)
create mode 100644 drivers/net/nfb/nfb_vdev.c
--
2.52.0
^ permalink raw reply [flat|nested] 131+ messages in thread
* [PATCH 1/8] net/nfb: prepare for indirect queue mapping scheme
2026-01-15 15:16 [PATCH 0/8] net/nfb: rework to real multiport spinler
@ 2026-01-15 15:16 ` spinler
2026-01-16 17:34 ` Stephen Hemminger
2026-01-15 15:16 ` [PATCH 2/8] net/nfb: create ethdev for every eth port/channel spinler
` (16 subsequent siblings)
17 siblings, 1 reply; 131+ messages in thread
From: spinler @ 2026-01-15 15:16 UTC (permalink / raw)
To: dev; +Cc: Martin Spinler
From: Martin Spinler <spinler@cesnet.cz>
This prepares queue mapping for port-aware driver implementation.
Signed-off-by: Martin Spinler <spinler@cesnet.cz>
---
drivers/net/nfb/nfb.h | 4 ++++
drivers/net/nfb/nfb_ethdev.c | 21 +++++++++++++++++++++
drivers/net/nfb/nfb_rx.c | 21 ++++++++++++---------
drivers/net/nfb/nfb_tx.c | 21 ++++++++++++---------
4 files changed, 49 insertions(+), 18 deletions(-)
diff --git a/drivers/net/nfb/nfb.h b/drivers/net/nfb/nfb.h
index d85c5fba62..ef41d100ac 100644
--- a/drivers/net/nfb/nfb.h
+++ b/drivers/net/nfb/nfb.h
@@ -47,6 +47,10 @@ struct pmd_internals {
uint16_t max_txmac;
struct nc_rxmac *rxmac[RTE_MAX_NC_RXMAC];
struct nc_txmac *txmac[RTE_MAX_NC_TXMAC];
+
+ int *queue_map_rx;
+ int *queue_map_tx;
+
struct nfb_device *nfb;
};
diff --git a/drivers/net/nfb/nfb_ethdev.c b/drivers/net/nfb/nfb_ethdev.c
index 74728f91dd..247f78fe24 100644
--- a/drivers/net/nfb/nfb_ethdev.c
+++ b/drivers/net/nfb/nfb_ethdev.c
@@ -510,6 +510,7 @@ static const struct eth_dev_ops ops = {
static int
nfb_eth_dev_init(struct rte_eth_dev *dev)
{
+ int i;
int ret;
uint32_t mac_count;
struct rte_eth_dev_data *data = dev->data;
@@ -567,6 +568,23 @@ nfb_eth_dev_init(struct rte_eth_dev *dev)
dev->rx_pkt_burst = nfb_eth_ndp_rx;
dev->tx_pkt_burst = nfb_eth_ndp_tx;
+ internals->queue_map_rx = rte_malloc("NFB queue map",
+ sizeof(*internals->queue_map_rx) *
+ (priv->max_rx_queues + priv->max_tx_queues), 0);
+ if (internals->queue_map_rx == NULL) {
+ ret = -ENOMEM;
+ goto err_alloc_queue_map;
+ }
+ internals->queue_map_tx = internals->queue_map_rx + priv->max_rx_queues;
+
+ /* default queue mapping is 1:1 */
+ for (i = 0; i < priv->max_rx_queues; i++) {
+ internals->queue_map_rx[i] = i;
+ }
+ for (i = 0; i < priv->max_tx_queues; i++) {
+ internals->queue_map_tx[i] = i;
+ }
+
/* Set function callbacks for Ethernet API */
dev->dev_ops = &ops;
@@ -605,6 +623,8 @@ nfb_eth_dev_init(struct rte_eth_dev *dev)
return 0;
err_malloc_mac_addrs:
+ rte_free(internals->queue_map_rx);
+err_alloc_queue_map:
nfb_nc_rxmac_deinit(internals->rxmac, internals->max_rxmac);
nfb_nc_txmac_deinit(internals->txmac, internals->max_txmac);
nfb_close(internals->nfb);
@@ -635,6 +655,7 @@ nfb_eth_dev_uninit(struct rte_eth_dev *dev)
nfb_nc_txmac_deinit(internals->txmac, internals->max_txmac);
nfb_close(internals->nfb);
+ rte_free(internals->queue_map_rx);
rte_free(internals);
NFB_LOG(INFO, "NFB device (" PCI_PRI_FMT ") successfully uninitialized",
diff --git a/drivers/net/nfb/nfb_rx.c b/drivers/net/nfb/nfb_rx.c
index 413d275853..0881d8b6b8 100644
--- a/drivers/net/nfb/nfb_rx.c
+++ b/drivers/net/nfb/nfb_rx.c
@@ -63,6 +63,7 @@ nfb_eth_rx_queue_setup(struct rte_eth_dev *dev,
struct pmd_internals *internals = dev->process_private;
struct ndp_rx_queue *rxq;
+ int nfb_qid;
int ret;
rxq = rte_zmalloc_socket("ndp rx queue",
@@ -77,17 +78,19 @@ nfb_eth_rx_queue_setup(struct rte_eth_dev *dev,
rxq->flags = 0;
- ret = nfb_eth_rx_queue_init(internals->nfb,
- rx_queue_id,
- dev->data->port_id,
- mb_pool,
- rxq);
+ /* nfb queue id doesn't neccessary corresponds to txq_id */
+ nfb_qid = internals->queue_map_rx[rx_queue_id];
- if (ret == 0)
- dev->data->rx_queues[rx_queue_id] = rxq;
- else
- rte_free(rxq);
+ ret = nfb_eth_rx_queue_init(internals->nfb, nfb_qid,
+ dev->data->port_id, mb_pool, rxq);
+ if (ret)
+ goto err_queue_init;
+
+ dev->data->rx_queues[rx_queue_id] = rxq;
+ return 0;
+err_queue_init:
+ rte_free(rxq);
return ret;
}
diff --git a/drivers/net/nfb/nfb_tx.c b/drivers/net/nfb/nfb_tx.c
index 1f997ce22f..e24db8939c 100644
--- a/drivers/net/nfb/nfb_tx.c
+++ b/drivers/net/nfb/nfb_tx.c
@@ -55,10 +55,10 @@ nfb_eth_tx_queue_setup(struct rte_eth_dev *dev,
{
struct pmd_internals *internals = dev->process_private;
int ret;
+ int nfb_qid;
struct ndp_tx_queue *txq;
- txq = rte_zmalloc_socket("ndp tx queue",
- sizeof(struct ndp_tx_queue),
+ txq = rte_zmalloc_socket("ndp tx queue", sizeof(struct ndp_tx_queue),
RTE_CACHE_LINE_SIZE, socket_id);
if (txq == NULL) {
@@ -67,15 +67,18 @@ nfb_eth_tx_queue_setup(struct rte_eth_dev *dev,
return -ENOMEM;
}
- ret = nfb_eth_tx_queue_init(internals->nfb,
- tx_queue_id,
- txq);
+ /* nfb queue id doesn't neccessary corresponds to tx_queue_id */
+ nfb_qid = internals->queue_map_tx[tx_queue_id];
- if (ret == 0)
- dev->data->tx_queues[tx_queue_id] = txq;
- else
- rte_free(txq);
+ ret = nfb_eth_tx_queue_init(internals->nfb, nfb_qid, txq);
+ if (ret)
+ goto err_queue_init;
+
+ dev->data->tx_queues[tx_queue_id] = txq;
+ return 0;
+err_queue_init:
+ rte_free(txq);
return ret;
}
--
2.52.0
^ permalink raw reply related [flat|nested] 131+ messages in thread
* [PATCH 2/8] net/nfb: create ethdev for every eth port/channel
2026-01-15 15:16 [PATCH 0/8] net/nfb: rework to real multiport spinler
2026-01-15 15:16 ` [PATCH 1/8] net/nfb: prepare for indirect queue mapping scheme spinler
@ 2026-01-15 15:16 ` spinler
2026-01-15 15:16 ` [PATCH 3/8] net/nfb: add vdev as alternative device probe method spinler
` (15 subsequent siblings)
17 siblings, 0 replies; 131+ messages in thread
From: spinler @ 2026-01-15 15:16 UTC (permalink / raw)
To: dev; +Cc: Martin Spinler
From: Martin Spinler <spinler@cesnet.cz>
The existing driver creates one ethdev/port for one PCI device.
As the CESNET-NDK-based cards are mostly unable to represent
each physical port with one PCI device, this atypical behaviour
often limits the user.
The nfb-framework comes with generic port representation API,
which provides information about ports and their mapping to firmware
resource (physical ports, DMA queues, processing pipelines).
This driver design switches from one rte_eth_dev_pci_generic_probe call
to multiple rte_eth_dev_create calls, while parses the firmware mapping
informations.
Signed-off-by: Martin Spinler <spinler@cesnet.cz>
---
drivers/net/nfb/nfb.h | 22 +++++
drivers/net/nfb/nfb_ethdev.c | 175 ++++++++++++++++++++++++++---------
2 files changed, 152 insertions(+), 45 deletions(-)
diff --git a/drivers/net/nfb/nfb.h b/drivers/net/nfb/nfb.h
index ef41d100ac..9c0d1a06f8 100644
--- a/drivers/net/nfb/nfb.h
+++ b/drivers/net/nfb/nfb.h
@@ -11,6 +11,7 @@
#include <nfb/ndp.h>
#include <netcope/rxmac.h>
#include <netcope/txmac.h>
+#include <netcope/info.h>
extern int nfb_logtype;
#define RTE_LOGTYPE_NFB nfb_logtype
@@ -51,6 +52,9 @@ struct pmd_internals {
int *queue_map_rx;
int *queue_map_tx;
+ TAILQ_ENTRY(pmd_internals) eth_dev_list;
+ struct rte_eth_dev *eth_dev;
+
struct nfb_device *nfb;
};
@@ -59,4 +63,22 @@ struct pmd_priv {
int max_tx_queues;
};
+struct nfb_init_params {
+ const char *path;
+ const char *args;
+ int nfb_id;
+
+ struct nc_ifc_map_info map_info;
+ struct nc_ifc_info *ifc_info;
+
+ char name[RTE_DEV_NAME_MAX_LEN];
+};
+
+
+int
+nfb_eth_common_probe(struct rte_device *device,
+ ethdev_bus_specific_init specific_init, void *specific_device,
+ struct nfb_init_params *params, int ep_index);
+int nfb_eth_common_remove(struct rte_device *dev);
+
#endif /* _NFB_H_ */
diff --git a/drivers/net/nfb/nfb_ethdev.c b/drivers/net/nfb/nfb_ethdev.c
index 247f78fe24..4a6648059e 100644
--- a/drivers/net/nfb/nfb_ethdev.c
+++ b/drivers/net/nfb/nfb_ethdev.c
@@ -4,6 +4,9 @@
* All rights reserved.
*/
+#include <sys/queue.h>
+#include <rte_tailq.h>
+
#include <nfb/nfb.h>
#include <nfb/ndp.h>
#include <netcope/rxmac.h>
@@ -19,6 +22,13 @@
#include "nfb.h"
+/* The TAILQ entries are used for cleanup of allocated resources
+ * in situations, where dev_close is not called.
+ */
+TAILQ_HEAD(pmd_internals_head, pmd_internals);
+static struct pmd_internals_head nfb_eth_dev_list =
+ TAILQ_HEAD_INITIALIZER(nfb_eth_dev_list);
+
static int nfb_eth_dev_uninit(struct rte_eth_dev *dev);
/**
@@ -508,22 +518,18 @@ static const struct eth_dev_ops ops = {
* 0 on success, a negative errno value otherwise.
*/
static int
-nfb_eth_dev_init(struct rte_eth_dev *dev)
+nfb_eth_dev_init(struct rte_eth_dev *dev, void *init_data)
{
int i;
+ int cnt;
int ret;
- uint32_t mac_count;
struct rte_eth_dev_data *data = dev->data;
struct pmd_internals *internals;
+ struct nfb_init_params *params = init_data;
+ struct nc_ifc_info *ifc = params->ifc_info;
+ struct nc_ifc_map_info *mi = ¶ms->map_info;
struct pmd_priv *priv = data->dev_private;
- struct rte_pci_device *pci_dev = RTE_ETH_DEV_TO_PCI(dev);
- struct rte_pci_addr *pci_addr = &pci_dev->addr;
struct rte_ether_addr eth_addr_init;
- char nfb_dev[PATH_MAX];
-
- NFB_LOG(INFO, "Initializing NFB device (" PCI_PRI_FMT ")",
- pci_addr->domain, pci_addr->bus, pci_addr->devid,
- pci_addr->function);
internals = rte_zmalloc_socket("nfb_internals",
sizeof(struct pmd_internals), RTE_CACHE_LINE_SIZE,
@@ -535,27 +541,13 @@ nfb_eth_dev_init(struct rte_eth_dev *dev)
dev->process_private = internals;
- snprintf(nfb_dev, sizeof(nfb_dev),
- "/dev/nfb/by-pci-slot/" PCI_PRI_FMT,
- pci_addr->domain, pci_addr->bus, pci_addr->devid,
- pci_addr->function);
-
- /*
- * Get number of available DMA RX and TX queues, which is maximum
- * number of queues that can be created and store it in private device
- * data structure.
- */
- internals->nfb = nfb_open(nfb_dev);
+ /* Open device handle */
+ internals->nfb = nfb_open(params->path);
if (internals->nfb == NULL) {
- NFB_LOG(ERR, "nfb_open(): failed to open %s", nfb_dev);
+ NFB_LOG(ERR, "nfb_open(): failed to open %s", params->path);
ret = -EINVAL;
goto err_nfb_open;
}
- priv->max_rx_queues = ndp_get_rx_queue_available_count(internals->nfb);
- priv->max_tx_queues = ndp_get_tx_queue_available_count(internals->nfb);
-
- NFB_LOG(INFO, "Available NDP queues RX: %u TX: %u",
- data->nb_rx_queues, data->nb_tx_queues);
nfb_nc_rxmac_init(internals->nfb,
internals->rxmac,
@@ -568,6 +560,10 @@ nfb_eth_dev_init(struct rte_eth_dev *dev)
dev->rx_pkt_burst = nfb_eth_ndp_rx;
dev->tx_pkt_burst = nfb_eth_ndp_tx;
+ /* Get number of available DMA RX and TX queues */
+ priv->max_rx_queues = ifc->rxq_cnt;
+ priv->max_tx_queues = ifc->txq_cnt;
+
internals->queue_map_rx = rte_malloc("NFB queue map",
sizeof(*internals->queue_map_rx) *
(priv->max_rx_queues + priv->max_tx_queues), 0);
@@ -577,12 +573,18 @@ nfb_eth_dev_init(struct rte_eth_dev *dev)
}
internals->queue_map_tx = internals->queue_map_rx + priv->max_rx_queues;
- /* default queue mapping is 1:1 */
- for (i = 0; i < priv->max_rx_queues; i++) {
- internals->queue_map_rx[i] = i;
+ cnt = 0;
+ for (i = 0; i < mi->rxq_cnt; i++) {
+ if (mi->rxq[i].ifc == ifc->id) {
+ internals->queue_map_rx[cnt++] = mi->rxq[i].id;
+ }
}
- for (i = 0; i < priv->max_tx_queues; i++) {
- internals->queue_map_tx[i] = i;
+
+ cnt = 0;
+ for (i = 0; i < mi->txq_cnt; i++) {
+ if (mi->txq[i].ifc == ifc->id) {
+ internals->queue_map_tx[cnt++] = mi->txq[i].id;
+ }
}
/* Set function callbacks for Ethernet API */
@@ -593,9 +595,9 @@ nfb_eth_dev_init(struct rte_eth_dev *dev)
if (rte_eal_process_type() == RTE_PROC_PRIMARY) {
/* Allocate space for MAC addresses */
- mac_count = nfb_eth_get_max_mac_address_count(dev);
+ cnt = nfb_eth_get_max_mac_address_count(dev);
data->mac_addrs = rte_zmalloc(data->name,
- sizeof(struct rte_ether_addr) * mac_count, RTE_CACHE_LINE_SIZE);
+ sizeof(struct rte_ether_addr) * cnt, RTE_CACHE_LINE_SIZE);
if (data->mac_addrs == NULL) {
NFB_LOG(ERR, "Could not alloc space for MAC address");
ret = -ENOMEM;
@@ -616,10 +618,6 @@ nfb_eth_dev_init(struct rte_eth_dev *dev)
data->dev_flags |= RTE_ETH_DEV_AUTOFILL_QUEUE_XSTATS;
}
- NFB_LOG(INFO, "NFB device (" PCI_PRI_FMT ") successfully initialized",
- pci_addr->domain, pci_addr->bus, pci_addr->devid,
- pci_addr->function);
-
return 0;
err_malloc_mac_addrs:
@@ -647,10 +645,10 @@ nfb_eth_dev_init(struct rte_eth_dev *dev)
static int
nfb_eth_dev_uninit(struct rte_eth_dev *dev)
{
- struct rte_pci_device *pci_dev = RTE_ETH_DEV_TO_PCI(dev);
- struct rte_pci_addr *pci_addr = &pci_dev->addr;
struct pmd_internals *internals = dev->process_private;
+ TAILQ_REMOVE(&nfb_eth_dev_list, internals, eth_dev_list);
+
nfb_nc_rxmac_deinit(internals->rxmac, internals->max_rxmac);
nfb_nc_txmac_deinit(internals->txmac, internals->max_txmac);
nfb_close(internals->nfb);
@@ -658,9 +656,59 @@ nfb_eth_dev_uninit(struct rte_eth_dev *dev)
rte_free(internals->queue_map_rx);
rte_free(internals);
- NFB_LOG(INFO, "NFB device (" PCI_PRI_FMT ") successfully uninitialized",
- pci_addr->domain, pci_addr->bus, pci_addr->devid,
- pci_addr->function);
+ return 0;
+}
+
+int
+nfb_eth_common_probe(struct rte_device *device,
+ ethdev_bus_specific_init specific_init, void *specific_device,
+ struct nfb_init_params *params, int ep_index)
+{
+ int i;
+ int ret;
+ int basename_len;
+
+ struct nc_ifc_info *ifc;
+ struct nfb_device *nfb_dev;
+ struct rte_eth_dev *eth_dev;
+ struct pmd_internals *p;
+
+ basename_len = strlen(params->name);
+
+ nfb_dev = nfb_open(params->path);
+ if (nfb_dev == NULL) {
+ NFB_LOG(ERR, "nfb_open(): failed to open %s", params->path);
+ return -EINVAL;
+ }
+
+ nc_ifc_map_info_create_ordinary(nfb_dev, ¶ms->map_info);
+
+ for (i = 0; i < params->map_info.ifc_cnt; i++) {
+ ifc = params->ifc_info = ¶ms->map_info.ifc[i];
+
+ /* Skip interfaces which doesn't belong to this PCI device */
+ if ((ep_index != -1 && ifc->ep != ep_index) ||
+ (ifc->flags & NC_IFC_INFO_FLAG_ACTIVE) == 0)
+ continue;
+
+ snprintf(params->name + basename_len, sizeof(params->name) - basename_len,
+ "_eth%d", params->ifc_info->id);
+
+ ret = rte_eth_dev_create(device, params->name,
+ sizeof(struct pmd_priv),
+ specific_init, specific_device,
+ nfb_eth_dev_init, params);
+
+ if (ret == 0) {
+ eth_dev = rte_eth_dev_get_by_name(params->name);
+ p = eth_dev->process_private;
+ p->eth_dev = eth_dev;
+ TAILQ_INSERT_TAIL(&nfb_eth_dev_list, p, eth_dev_list);
+ }
+ }
+
+ nc_map_info_destroy(¶ms->map_info);
+ nfb_close(nfb_dev);
return 0;
}
@@ -691,8 +739,45 @@ static int
nfb_eth_pci_probe(struct rte_pci_driver *pci_drv __rte_unused,
struct rte_pci_device *pci_dev)
{
- return rte_eth_dev_pci_generic_probe(pci_dev,
- sizeof(struct pmd_priv), nfb_eth_dev_init);
+ int ret;
+ char path[PATH_MAX];
+
+ struct nc_composed_device_info comp_dev_info;
+ struct nfb_init_params params = {0};
+
+ rte_pci_device_name(&pci_dev->addr, params.name, sizeof(params.name));
+
+ /* NFB device can be composed from multiple PCI devices,
+ * find the base char device ID for the current PCI device */
+ ret = nc_get_composed_device_info_by_pci(NULL, params.name, &comp_dev_info);
+ if (ret) {
+ NFB_LOG(ERR, "Could not find NFB device for %s", params.name);
+ return -ENODEV;
+ }
+
+ ret = snprintf(path, sizeof(path), NFB_BASE_DEV_PATH "%d", comp_dev_info.nfb_id);
+ RTE_ASSERT(ret > 0 && ret < sizeof(path));
+
+ params.args = pci_dev->device.devargs ? pci_dev->device.devargs->args : NULL;
+ params.path = path;
+ params.nfb_id = comp_dev_info.nfb_id;
+
+ return nfb_eth_common_probe(&pci_dev->device, eth_dev_pci_specific_init, pci_dev, ¶ms,
+ comp_dev_info.ep_index);
+}
+
+int
+nfb_eth_common_remove(struct rte_device *dev)
+{
+ struct pmd_internals *entry, *temp;
+
+ RTE_TAILQ_FOREACH_SAFE(entry, &nfb_eth_dev_list, eth_dev_list, temp) {
+ if (dev == entry->eth_dev->device) {
+ TAILQ_REMOVE(&nfb_eth_dev_list, entry, eth_dev_list);
+ rte_eth_dev_destroy(entry->eth_dev, nfb_eth_dev_uninit);
+ }
+ }
+ return 0;
}
/**
@@ -709,7 +794,7 @@ nfb_eth_pci_probe(struct rte_pci_driver *pci_drv __rte_unused,
static int
nfb_eth_pci_remove(struct rte_pci_device *pci_dev)
{
- return rte_eth_dev_pci_generic_remove(pci_dev, nfb_eth_dev_uninit);
+ return nfb_eth_common_remove(&pci_dev->device);
}
static struct rte_pci_driver nfb_eth_driver = {
--
2.52.0
^ permalink raw reply related [flat|nested] 131+ messages in thread
* [PATCH 3/8] net/nfb: add vdev as alternative device probe method
2026-01-15 15:16 [PATCH 0/8] net/nfb: rework to real multiport spinler
2026-01-15 15:16 ` [PATCH 1/8] net/nfb: prepare for indirect queue mapping scheme spinler
2026-01-15 15:16 ` [PATCH 2/8] net/nfb: create ethdev for every eth port/channel spinler
@ 2026-01-15 15:16 ` spinler
2026-01-15 15:16 ` [PATCH 4/8] net/nfb: add device argument "port" to limit used ports spinler
` (14 subsequent siblings)
17 siblings, 0 replies; 131+ messages in thread
From: spinler @ 2026-01-15 15:16 UTC (permalink / raw)
To: dev; +Cc: Martin Spinler
From: Martin Spinler <spinler@cesnet.cz>
VDEV are useful for NFB devices not represented by PCI device,
e.g. virtual or simulated devices.
Signed-off-by: Martin Spinler <spinler@cesnet.cz>
---
drivers/net/nfb/meson.build | 1 +
drivers/net/nfb/nfb_vdev.c | 96 +++++++++++++++++++++++++++++++++++++
2 files changed, 97 insertions(+)
create mode 100644 drivers/net/nfb/nfb_vdev.c
diff --git a/drivers/net/nfb/meson.build b/drivers/net/nfb/meson.build
index d7a255c928..9e458dfb4a 100644
--- a/drivers/net/nfb/meson.build
+++ b/drivers/net/nfb/meson.build
@@ -16,6 +16,7 @@ ext_deps += dep
sources = files(
'nfb_ethdev.c',
+ 'nfb_vdev.c',
'nfb_rx.c',
'nfb_rxmode.c',
'nfb_stats.c',
diff --git a/drivers/net/nfb/nfb_vdev.c b/drivers/net/nfb/nfb_vdev.c
new file mode 100644
index 0000000000..0cbb34f00a
--- /dev/null
+++ b/drivers/net/nfb/nfb_vdev.c
@@ -0,0 +1,96 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2019 Cesnet
+ */
+
+#include <rte_kvargs.h>
+#include <ethdev_vdev.h>
+
+#include "nfb.h"
+
+#define VDEV_NFB_DRIVER net_vdev_nfb
+#define VDEV_NFB_ARG_DEV "dev"
+
+
+static int
+vdev_nfb_vdev_probe(struct rte_vdev_device *dev)
+{
+ int i;
+ int ret = 0;
+
+ struct nfb_init_params params;
+
+ const char *vdev_name = rte_vdev_device_name(dev);
+ const char *vdev_args = rte_vdev_device_args(dev);
+ char *dev_params, *dev_params_mod;
+ struct rte_kvargs *kvargs;
+
+ kvargs = rte_kvargs_parse(vdev_args, NULL);
+ if (kvargs == NULL) {
+ NFB_LOG(ERR, "Failed to parse device arguments %s", vdev_args);
+ ret = -EINVAL;
+ goto err_parse_args;
+ }
+
+ dev_params = strdup(vdev_args);
+ if (dev_params == NULL) {
+ ret = -ENOMEM;
+ goto err_strdup_params;
+ }
+
+ params.args = dev_params;
+ params.path = nfb_default_dev_path();
+ params.nfb_id = 0;
+
+ dev_params_mod = dev_params;
+
+ dev_params_mod[0] = 0;
+
+ /* Parse parameters for virtual device */
+ for (i = 0; i != (signed) kvargs->count; ++i) {
+ const struct rte_kvargs_pair *pair = &kvargs->pairs[i];
+
+ if (!strcmp(pair->key, VDEV_NFB_ARG_DEV)) {
+ params.path = pair->value;
+ } else {
+ dev_params_mod += sprintf(dev_params_mod, "%s%s=%s",
+ dev_params_mod == dev_params ? "" : ",",
+ pair->key, pair->value);
+ }
+ }
+
+ strcpy(params.name, vdev_name);
+
+ ret = nfb_eth_common_probe(&dev->device, NULL, NULL, ¶ms, -1);
+ if (ret)
+ goto err_nfb_common_probe;
+
+ free(dev_params);
+ rte_kvargs_free(kvargs);
+
+ return ret;
+
+err_nfb_common_probe:
+ free(dev_params);
+err_strdup_params:
+ rte_kvargs_free(kvargs);
+err_parse_args:
+ return ret;
+}
+
+static int
+vdev_nfb_vdev_remove(struct rte_vdev_device *dev)
+{
+ return nfb_eth_common_remove(&dev->device);
+}
+
+/** Virtual device descriptor. */
+static struct rte_vdev_driver vdev_nfb_vdev = {
+ .probe = vdev_nfb_vdev_probe,
+ .remove = vdev_nfb_vdev_remove,
+};
+
+RTE_PMD_REGISTER_VDEV(VDEV_NFB_DRIVER, vdev_nfb_vdev);
+RTE_PMD_REGISTER_ALIAS(VDEV_NFB_DRIVER, eth_vdev_nfb);
+RTE_PMD_REGISTER_PARAM_STRING(net_vdev_nfb,
+ VDEV_NFB_ARG_DEV "=<string>"
+ );
--
2.52.0
^ permalink raw reply related [flat|nested] 131+ messages in thread
* [PATCH 4/8] net/nfb: add device argument "port" to limit used ports
2026-01-15 15:16 [PATCH 0/8] net/nfb: rework to real multiport spinler
` (2 preceding siblings ...)
2026-01-15 15:16 ` [PATCH 3/8] net/nfb: add vdev as alternative device probe method spinler
@ 2026-01-15 15:16 ` spinler
2026-01-16 17:36 ` Stephen Hemminger
` (2 more replies)
2026-01-15 15:16 ` [PATCH 5/8] net/nfb: init only MACs associated with device spinler
` (13 subsequent siblings)
17 siblings, 3 replies; 131+ messages in thread
From: spinler @ 2026-01-15 15:16 UTC (permalink / raw)
To: dev; +Cc: Martin Spinler
From: Martin Spinler <spinler@cesnet.cz>
NFB devices does not use separate PCI device for each port; one PCI device
contains handles for all ports instead. For some application this approach
can be limiting.
The "port" argument can be used to select only desired ports.
It can be used multiple times. When is not used at all, the driver
selects all ports.
Signed-off-by: Martin Spinler <spinler@cesnet.cz>
---
drivers/net/nfb/nfb.h | 10 ++++++++
drivers/net/nfb/nfb_ethdev.c | 49 +++++++++++++++++++++++++++++++++++-
drivers/net/nfb/nfb_vdev.c | 3 ++-
3 files changed, 60 insertions(+), 2 deletions(-)
diff --git a/drivers/net/nfb/nfb.h b/drivers/net/nfb/nfb.h
index 9c0d1a06f8..86fcc1862c 100644
--- a/drivers/net/nfb/nfb.h
+++ b/drivers/net/nfb/nfb.h
@@ -42,6 +42,16 @@ extern int nfb_logtype;
#define RTE_NFB_DRIVER_NAME net_nfb
+/* Device arguments */
+#define NFB_ARG_PORT "port"
+
+#define NFB_COMMON_ARGS \
+ NFB_ARG_PORT"=<number>"
+
+static const char * const VALID_KEYS[] = {
+ NFB_ARG_PORT,
+ NULL
+};
struct pmd_internals {
uint16_t max_rxmac;
diff --git a/drivers/net/nfb/nfb_ethdev.c b/drivers/net/nfb/nfb_ethdev.c
index 4a6648059e..7f0303e389 100644
--- a/drivers/net/nfb/nfb_ethdev.c
+++ b/drivers/net/nfb/nfb_ethdev.c
@@ -659,13 +659,31 @@ nfb_eth_dev_uninit(struct rte_eth_dev *dev)
return 0;
}
+static int fill_port_mask(const char *key __rte_unused, const char *value, void *opaque)
+{
+ int ret = 0;
+ char *end = NULL;
+ uint64_t *port_mask = opaque;
+ int port;
+
+ port = strtol(value, &end, 16);
+ if ((value[0] == '\0') || (end == NULL) || (*end != '\0'))
+ ret = -1;
+
+ if (ret != 0 || port >= 64 || port < 0)
+ return -1;
+
+ *port_mask |= (1ull << port);
+ return 0;
+}
+
int
nfb_eth_common_probe(struct rte_device *device,
ethdev_bus_specific_init specific_init, void *specific_device,
struct nfb_init_params *params, int ep_index)
{
int i;
- int ret;
+ int ret = 0;
int basename_len;
struct nc_ifc_info *ifc;
@@ -673,6 +691,9 @@ nfb_eth_common_probe(struct rte_device *device,
struct rte_eth_dev *eth_dev;
struct pmd_internals *p;
+ struct rte_kvargs *kvlist;
+ uint64_t port_mask = 0xFFFFFFFFFFFFFFFFull;
+
basename_len = strlen(params->name);
nfb_dev = nfb_open(params->path);
@@ -683,6 +704,26 @@ nfb_eth_common_probe(struct rte_device *device,
nc_ifc_map_info_create_ordinary(nfb_dev, ¶ms->map_info);
+ if (params->args != NULL && strlen(params->args) > 0) {
+ kvlist = rte_kvargs_parse(params->args, VALID_KEYS);
+ if (kvlist == NULL) {
+ NFB_LOG(ERR, "Failed to parse device arguments %s", params->args);
+ return -EINVAL;
+ }
+ if (rte_kvargs_count(kvlist, NFB_ARG_PORT)) {
+ port_mask = 0;
+ if (rte_kvargs_process(kvlist, NFB_ARG_PORT, fill_port_mask, (void*) &port_mask))
+ ret = -1;
+ if (ret || port_mask >= (1ull << (params->map_info.ifc_cnt)))
+ port_mask = 0;
+ }
+ rte_kvargs_free(kvlist);
+ if (port_mask == 0) {
+ NFB_LOG(ERR, "Failed to parse device port argument");
+ return -EINVAL;
+ }
+ }
+
for (i = 0; i < params->map_info.ifc_cnt; i++) {
ifc = params->ifc_info = ¶ms->map_info.ifc[i];
@@ -691,6 +732,9 @@ nfb_eth_common_probe(struct rte_device *device,
(ifc->flags & NC_IFC_INFO_FLAG_ACTIVE) == 0)
continue;
+ if ((port_mask & (1ull << i)) == 0)
+ continue;
+
snprintf(params->name + basename_len, sizeof(params->name) - basename_len,
"_eth%d", params->ifc_info->id);
@@ -807,3 +851,6 @@ RTE_PMD_REGISTER_PCI(RTE_NFB_DRIVER_NAME, nfb_eth_driver);
RTE_PMD_REGISTER_PCI_TABLE(RTE_NFB_DRIVER_NAME, nfb_pci_id_table);
RTE_PMD_REGISTER_KMOD_DEP(RTE_NFB_DRIVER_NAME, "* nfb");
RTE_LOG_REGISTER_DEFAULT(nfb_logtype, NOTICE);
+RTE_PMD_REGISTER_PARAM_STRING(RTE_NFB_DRIVER_NAME,
+ NFB_COMMON_ARGS
+ );
diff --git a/drivers/net/nfb/nfb_vdev.c b/drivers/net/nfb/nfb_vdev.c
index 0cbb34f00a..f5c8b3edb0 100644
--- a/drivers/net/nfb/nfb_vdev.c
+++ b/drivers/net/nfb/nfb_vdev.c
@@ -92,5 +92,6 @@ static struct rte_vdev_driver vdev_nfb_vdev = {
RTE_PMD_REGISTER_VDEV(VDEV_NFB_DRIVER, vdev_nfb_vdev);
RTE_PMD_REGISTER_ALIAS(VDEV_NFB_DRIVER, eth_vdev_nfb);
RTE_PMD_REGISTER_PARAM_STRING(net_vdev_nfb,
- VDEV_NFB_ARG_DEV "=<string>"
+ VDEV_NFB_ARG_DEV "=<string> "
+ NFB_COMMON_ARGS
);
--
2.52.0
^ permalink raw reply related [flat|nested] 131+ messages in thread
* [PATCH 5/8] net/nfb: init only MACs associated with device
2026-01-15 15:16 [PATCH 0/8] net/nfb: rework to real multiport spinler
` (3 preceding siblings ...)
2026-01-15 15:16 ` [PATCH 4/8] net/nfb: add device argument "port" to limit used ports spinler
@ 2026-01-15 15:16 ` spinler
2026-01-15 15:16 ` [PATCH 6/8] net/nfb: add compatible cards to driver PCI ID table spinler
` (12 subsequent siblings)
17 siblings, 0 replies; 131+ messages in thread
From: spinler @ 2026-01-15 15:16 UTC (permalink / raw)
To: dev; +Cc: Martin Spinler
From: Martin Spinler <spinler@cesnet.cz>
Use informations obtained from the new manner of creating NFB ethdevs
and initialize only RxMACs / TxMACs associated with actual ethdev.
This enables separated management and configuration of interface ports.
Signed-off-by: Martin Spinler <spinler@cesnet.cz>
---
drivers/net/nfb/nfb.h | 9 +--
drivers/net/nfb/nfb_ethdev.c | 136 +++++++++++++++++++----------------
2 files changed, 75 insertions(+), 70 deletions(-)
diff --git a/drivers/net/nfb/nfb.h b/drivers/net/nfb/nfb.h
index 86fcc1862c..8a4e79a479 100644
--- a/drivers/net/nfb/nfb.h
+++ b/drivers/net/nfb/nfb.h
@@ -36,10 +36,6 @@ extern int nfb_logtype;
#define RTE_ETH_NDP_MAX_RX_QUEUES 32
#define RTE_ETH_NDP_MAX_TX_QUEUES 32
-/* Max index of rx/tx dmas */
-#define RTE_MAX_NC_RXMAC 256
-#define RTE_MAX_NC_TXMAC 256
-
#define RTE_NFB_DRIVER_NAME net_nfb
/* Device arguments */
@@ -56,9 +52,8 @@ static const char * const VALID_KEYS[] = {
struct pmd_internals {
uint16_t max_rxmac;
uint16_t max_txmac;
- struct nc_rxmac *rxmac[RTE_MAX_NC_RXMAC];
- struct nc_txmac *txmac[RTE_MAX_NC_TXMAC];
-
+ struct nc_rxmac **rxmac;
+ struct nc_txmac **txmac;
int *queue_map_rx;
int *queue_map_tx;
diff --git a/drivers/net/nfb/nfb_ethdev.c b/drivers/net/nfb/nfb_ethdev.c
index 7f0303e389..53925bef9e 100644
--- a/drivers/net/nfb/nfb_ethdev.c
+++ b/drivers/net/nfb/nfb_ethdev.c
@@ -39,81 +39,95 @@ static const struct rte_ether_addr eth_addr = {
};
/**
- * Open all RX DMA queues
+ * Open RX MAC components associated with current ifc
*
- * @param dev
- * Pointer to nfb device.
- * @param[out] rxmac
- * Pointer to output array of nc_rxmac
- * @param[out] max_rxmac
- * Pointer to output max index of rxmac
+ * @param priv
+ * Pointer to driver private structure
+ * @param params
+ * Pointer to init parameters structure
*/
-static void
-nfb_nc_rxmac_init(struct nfb_device *nfb,
- struct nc_rxmac *rxmac[RTE_MAX_NC_RXMAC],
- uint16_t *max_rxmac)
+static int
+nfb_nc_rxmac_init(struct pmd_internals *priv, struct nfb_init_params *params)
{
- *max_rxmac = 0;
- while ((rxmac[*max_rxmac] = nc_rxmac_open_index(nfb, *max_rxmac)))
- ++(*max_rxmac);
+ int i, j;
+ struct nc_ifc_info *ifc = params->ifc_info;
+ struct nc_ifc_map_info *mi = ¶ms->map_info;
+
+ priv->rxmac = rte_zmalloc("NFB RxMAC", sizeof(*priv->rxmac) * ifc->eth_cnt, 0);
+ if (priv->rxmac == NULL)
+ return -ENOMEM;
+
+ for (i = 0, j = 0; i < mi->eth_cnt && j < ifc->eth_cnt; i++) {
+ if (mi->eth[i].ifc != ifc->id)
+ continue;
+ priv->rxmac[j] = nc_rxmac_open(priv->nfb, mi->eth[i].node_rxmac);
+ if (priv->rxmac[j])
+ j++;
+ }
+
+ priv->max_rxmac = j;
+ return 0;
}
/**
- * Open all TX DMA queues
+ * Open TX MAC components associated with current ifc
*
- * @param dev
- * Pointer to nfb device.
- * @param[out] txmac
- * Pointer to output array of nc_txmac
- * @param[out] max_rxmac
- * Pointer to output max index of txmac
+ * @param priv
+ * Pointer to driver private structure
+ * @param params
+ * Pointer to init parameters structure
*/
-static void
-nfb_nc_txmac_init(struct nfb_device *nfb,
- struct nc_txmac *txmac[RTE_MAX_NC_TXMAC],
- uint16_t *max_txmac)
+static int
+nfb_nc_txmac_init(struct pmd_internals *priv, struct nfb_init_params *params)
{
- *max_txmac = 0;
- while ((txmac[*max_txmac] = nc_txmac_open_index(nfb, *max_txmac)))
- ++(*max_txmac);
+
+ int i, j;
+ struct nc_ifc_info *ifc = params->ifc_info;
+ struct nc_ifc_map_info *mi = ¶ms->map_info;
+
+ priv->txmac = rte_zmalloc("NFB TxMAC", sizeof(*priv->txmac) * ifc->eth_cnt, 0);
+ if (priv->txmac == NULL)
+ return -ENOMEM;
+
+ for (i = 0, j = 0; i < mi->eth_cnt && j < ifc->eth_cnt; i++) {
+ if (mi->eth[i].ifc != ifc->id)
+ continue;
+ priv->txmac[j] = nc_txmac_open(priv->nfb, mi->eth[i].node_txmac);
+ if (priv->txmac[j])
+ j++;
+ }
+
+ priv->max_txmac = j;
+ return 0;
}
/**
- * Close all RX DMA queues
- *
- * @param rxmac
- * Pointer to array of nc_rxmac
- * @param max_rxmac
- * Maximum index of rxmac
+ * Close all RX MAC components
+ * @param priv
+ * Pointer to driver private structure
*/
static void
-nfb_nc_rxmac_deinit(struct nc_rxmac *rxmac[RTE_MAX_NC_RXMAC],
- uint16_t max_rxmac)
+nfb_nc_rxmac_deinit(struct pmd_internals *priv)
{
uint16_t i;
- for (i = 0; i < max_rxmac; i++) {
- nc_rxmac_close(rxmac[i]);
- rxmac[i] = NULL;
- }
+ for (i = 0; i < priv->max_rxmac; i++)
+ nc_rxmac_close(priv->rxmac[i]);
+
+ rte_free(priv->rxmac);
}
/**
- * Close all TX DMA queues
- *
- * @param txmac
- * Pointer to array of nc_txmac
- * @param max_txmac
- * Maximum index of txmac
+ * Close all TX MAC components
+ * @param priv
+ * Pointer to driver private structure
*/
static void
-nfb_nc_txmac_deinit(struct nc_txmac *txmac[RTE_MAX_NC_TXMAC],
- uint16_t max_txmac)
+nfb_nc_txmac_deinit(struct pmd_internals *priv)
{
uint16_t i;
- for (i = 0; i < max_txmac; i++) {
- nc_txmac_close(txmac[i]);
- txmac[i] = NULL;
- }
+ for (i = 0; i < priv->max_txmac; i++)
+ nc_txmac_close(priv->txmac[i]);
+ rte_free(priv->txmac);
}
/**
@@ -330,7 +344,7 @@ nfb_eth_link_update(struct rte_eth_dev *dev,
link.link_duplex = RTE_ETH_LINK_FULL_DUPLEX;
link.link_autoneg = RTE_ETH_LINK_SPEED_FIXED;
- if (internals->rxmac[0] != NULL) {
+ if (internals->max_rxmac) {
nc_rxmac_read_status(internals->rxmac[0], &status);
switch (status.speed) {
@@ -549,12 +563,8 @@ nfb_eth_dev_init(struct rte_eth_dev *dev, void *init_data)
goto err_nfb_open;
}
- nfb_nc_rxmac_init(internals->nfb,
- internals->rxmac,
- &internals->max_rxmac);
- nfb_nc_txmac_init(internals->nfb,
- internals->txmac,
- &internals->max_txmac);
+ nfb_nc_rxmac_init(internals, params);
+ nfb_nc_txmac_init(internals, params);
/* Set rx, tx burst functions */
dev->rx_pkt_burst = nfb_eth_ndp_rx;
@@ -623,8 +633,8 @@ nfb_eth_dev_init(struct rte_eth_dev *dev, void *init_data)
err_malloc_mac_addrs:
rte_free(internals->queue_map_rx);
err_alloc_queue_map:
- nfb_nc_rxmac_deinit(internals->rxmac, internals->max_rxmac);
- nfb_nc_txmac_deinit(internals->txmac, internals->max_txmac);
+ nfb_nc_rxmac_deinit(internals);
+ nfb_nc_txmac_deinit(internals);
nfb_close(internals->nfb);
err_nfb_open:
@@ -649,8 +659,8 @@ nfb_eth_dev_uninit(struct rte_eth_dev *dev)
TAILQ_REMOVE(&nfb_eth_dev_list, internals, eth_dev_list);
- nfb_nc_rxmac_deinit(internals->rxmac, internals->max_rxmac);
- nfb_nc_txmac_deinit(internals->txmac, internals->max_txmac);
+ nfb_nc_rxmac_deinit(internals);
+ nfb_nc_txmac_deinit(internals);
nfb_close(internals->nfb);
rte_free(internals->queue_map_rx);
--
2.52.0
^ permalink raw reply related [flat|nested] 131+ messages in thread
* [PATCH 6/8] net/nfb: add compatible cards to driver PCI ID table
2026-01-15 15:16 [PATCH 0/8] net/nfb: rework to real multiport spinler
` (4 preceding siblings ...)
2026-01-15 15:16 ` [PATCH 5/8] net/nfb: init only MACs associated with device spinler
@ 2026-01-15 15:16 ` spinler
2026-01-15 15:16 ` [PATCH 7/8] net/nfb: report firmware version spinler
` (11 subsequent siblings)
17 siblings, 0 replies; 131+ messages in thread
From: spinler @ 2026-01-15 15:16 UTC (permalink / raw)
To: dev; +Cc: Martin Spinler
From: Martin Spinler <spinler@cesnet.cz>
The nfb driver is compatible with:
- NFB-200G2QL card secondary PCI device
- FB2CGHH card
- COMBO-400G1 card
- Common CESNET-NDK cards
Signed-off-by: Martin Spinler <spinler@cesnet.cz>
---
drivers/net/nfb/nfb.h | 5 +++++
drivers/net/nfb/nfb_ethdev.c | 4 ++++
2 files changed, 9 insertions(+)
diff --git a/drivers/net/nfb/nfb.h b/drivers/net/nfb/nfb.h
index 8a4e79a479..2586975f7c 100644
--- a/drivers/net/nfb/nfb.h
+++ b/drivers/net/nfb/nfb.h
@@ -24,13 +24,18 @@ extern int nfb_logtype;
/* PCI Vendor ID */
#define PCI_VENDOR_ID_NETCOPE 0x1b26
#define PCI_VENDOR_ID_SILICOM 0x1c2c
+#define PCI_VENDOR_ID_CESNET 0x18ec
/* PCI Device IDs */
#define PCI_DEVICE_ID_NFB_40G2 0xcb80
#define PCI_DEVICE_ID_NFB_100G2 0xc2c1
#define PCI_DEVICE_ID_NFB_200G2QL 0xc250
+#define PCI_DEVICE_ID_NFB_200G2QL_E1 0xc251
#define PCI_DEVICE_ID_FB2CGG3 0x00d0
+#define PCI_DEVICE_ID_FB2CGHH 0x00d2
#define PCI_DEVICE_ID_FB2CGG3D 0xc240
+#define PCI_DEVICE_ID_COMBO400G1 0xc400
+#define PCI_DEVICE_ID_CESNET_NDK_COMMON 0xc000
/* Max index of ndp rx/tx queues */
#define RTE_ETH_NDP_MAX_RX_QUEUES 32
diff --git a/drivers/net/nfb/nfb_ethdev.c b/drivers/net/nfb/nfb_ethdev.c
index 53925bef9e..ea16708618 100644
--- a/drivers/net/nfb/nfb_ethdev.c
+++ b/drivers/net/nfb/nfb_ethdev.c
@@ -771,8 +771,12 @@ static const struct rte_pci_id nfb_pci_id_table[] = {
{ RTE_PCI_DEVICE(PCI_VENDOR_ID_NETCOPE, PCI_DEVICE_ID_NFB_40G2) },
{ RTE_PCI_DEVICE(PCI_VENDOR_ID_NETCOPE, PCI_DEVICE_ID_NFB_100G2) },
{ RTE_PCI_DEVICE(PCI_VENDOR_ID_NETCOPE, PCI_DEVICE_ID_NFB_200G2QL) },
+ { RTE_PCI_DEVICE(PCI_VENDOR_ID_NETCOPE, PCI_DEVICE_ID_NFB_200G2QL_E1) },
{ RTE_PCI_DEVICE(PCI_VENDOR_ID_SILICOM, PCI_DEVICE_ID_FB2CGG3) },
+ { RTE_PCI_DEVICE(PCI_VENDOR_ID_SILICOM, PCI_DEVICE_ID_FB2CGHH) },
{ RTE_PCI_DEVICE(PCI_VENDOR_ID_SILICOM, PCI_DEVICE_ID_FB2CGG3D) },
+ { RTE_PCI_DEVICE(PCI_VENDOR_ID_CESNET, PCI_DEVICE_ID_COMBO400G1) },
+ { RTE_PCI_DEVICE(PCI_VENDOR_ID_CESNET, PCI_DEVICE_ID_CESNET_NDK_COMMON) },
{ .vendor_id = 0, }
};
--
2.52.0
^ permalink raw reply related [flat|nested] 131+ messages in thread
* [PATCH 7/8] net/nfb: report firmware version
2026-01-15 15:16 [PATCH 0/8] net/nfb: rework to real multiport spinler
` (5 preceding siblings ...)
2026-01-15 15:16 ` [PATCH 6/8] net/nfb: add compatible cards to driver PCI ID table spinler
@ 2026-01-15 15:16 ` spinler
2026-01-15 15:16 ` [PATCH 8/8] doc/nfb: cleanup and update guide spinler
` (10 subsequent siblings)
17 siblings, 0 replies; 131+ messages in thread
From: spinler @ 2026-01-15 15:16 UTC (permalink / raw)
To: dev; +Cc: Martin Spinler
From: Martin Spinler <spinler@cesnet.cz>
Signed-off-by: Martin Spinler <spinler@cesnet.cz>
---
drivers/net/nfb/nfb_ethdev.c | 26 ++++++++++++++++++++++++++
1 file changed, 26 insertions(+)
diff --git a/drivers/net/nfb/nfb_ethdev.c b/drivers/net/nfb/nfb_ethdev.c
index ea16708618..4e0e28cce6 100644
--- a/drivers/net/nfb/nfb_ethdev.c
+++ b/drivers/net/nfb/nfb_ethdev.c
@@ -494,6 +494,31 @@ nfb_eth_mac_addr_remove(struct rte_eth_dev *dev, uint32_t index)
nc_rxmac_set_mac(internals->rxmac[i], index, 0, 0);
}
+static int
+nfb_eth_fw_version_get(struct rte_eth_dev *dev, char *fw_version,
+ size_t fw_size)
+{
+ int ret;
+ const char *proj_name, *proj_vers;
+ struct pmd_internals *priv = (struct pmd_internals *)
+ dev->process_private;
+
+ proj_name = nc_info_get_fw_project_name(priv->nfb, NULL);
+ proj_vers = nc_info_get_fw_project_version(priv->nfb, NULL);
+
+ if (proj_name == NULL)
+ proj_name = "";
+ if (proj_vers == NULL)
+ proj_vers = "";
+
+ ret = snprintf(fw_version, fw_size, "%s;%s", proj_name, proj_vers);
+
+ if (ret >= (signed)fw_size)
+ return strlen(proj_name) + 1 + strlen(proj_vers) + 1;
+
+ return 0;
+}
+
static const struct eth_dev_ops ops = {
.dev_start = nfb_eth_dev_start,
.dev_stop = nfb_eth_dev_stop,
@@ -520,6 +545,7 @@ static const struct eth_dev_ops ops = {
.mac_addr_set = nfb_eth_mac_addr_set,
.mac_addr_add = nfb_eth_mac_addr_add,
.mac_addr_remove = nfb_eth_mac_addr_remove,
+ .fw_version_get = nfb_eth_fw_version_get,
};
/**
--
2.52.0
^ permalink raw reply related [flat|nested] 131+ messages in thread
* [PATCH 8/8] doc/nfb: cleanup and update guide
2026-01-15 15:16 [PATCH 0/8] net/nfb: rework to real multiport spinler
` (6 preceding siblings ...)
2026-01-15 15:16 ` [PATCH 7/8] net/nfb: report firmware version spinler
@ 2026-01-15 15:16 ` spinler
2026-01-16 5:50 ` [PATCH 0/8] net/nfb: rework to real multiport Stephen Hemminger
` (9 subsequent siblings)
17 siblings, 0 replies; 131+ messages in thread
From: spinler @ 2026-01-15 15:16 UTC (permalink / raw)
To: dev; +Cc: Martin Spinler
From: Martin Spinler <spinler@cesnet.cz>
Signed-off-by: Martin Spinler <spinler@cesnet.cz>
---
doc/guides/nics/features/nfb.ini | 4 +
doc/guides/nics/nfb.rst | 187 ++++++++++++++-----------------
2 files changed, 91 insertions(+), 100 deletions(-)
diff --git a/doc/guides/nics/features/nfb.ini b/doc/guides/nics/features/nfb.ini
index c77b592789..7ac89fc15f 100644
--- a/doc/guides/nics/features/nfb.ini
+++ b/doc/guides/nics/features/nfb.ini
@@ -9,9 +9,13 @@ Link status = Y
Queue start/stop = Y
Promiscuous mode = Y
Allmulticast mode = Y
+Unicast MAC filter = Y
+Timestamp offload = P
Basic stats = Y
Extended stats = Y
Stats per queue = Y
+FW version = Y
+Multiprocess aware = Y
Linux = Y
x86-64 = Y
Usage doc = Y
diff --git a/doc/guides/nics/nfb.rst b/doc/guides/nics/nfb.rst
index a9b4049654..144bc897aa 100644
--- a/doc/guides/nics/nfb.rst
+++ b/doc/guides/nics/nfb.rst
@@ -2,137 +2,124 @@
Copyright 2019 Cesnet
Copyright 2019 Netcope Technologies
-NFB poll mode driver library
-=================================
+NFB Poll Mode Driver
+====================
+
+The NFB PMD implements support for the FPGA-based
+programmable NICs running `CESNET-NDK <https://www.liberouter.org/ndk/>`_
+based firmware (formerly known as the NetCOPE platform). The CESNET Network Development Kit offers
+wide spectrum of supported cards, for example:
+N6010, FB2CGG3 (Silicom Denmark),
+IA-420F, IA-440i (BittWare),
+AGI-FH400G (ReflexCES),
+and `many more <https://github.com/CESNET/ndk-fpga/tree/devel/cards>`_.
+
+The CESNET-NDK framework is open source and
+can be found on `CESNET-NDK <https://github.com/CESNET/ndk-fpga>`_.
+Ready-to-use demo firmwares can be found on the `DYNANIC page <https://dyna-nic.com/try-now/>`_.
+
+Software compatibility and firmware for
+`historical cards <https://www.liberouter.org/technologies/cards/>`_
+are left unmaintained.
+
+Software prerequisites
+----------------------
-The NFB poll mode driver library implements support for the Netcope
-FPGA Boards (**NFB-40G2, NFB-100G2, NFB-200G2QL**) and Silicom **FB2CGG3** card,
-FPGA-based programmable NICs. The NFB PMD uses interface provided by the libnfb
-library to communicate with these cards over the nfb layer.
+This PMD requires a Linux kernel module, which is responsible for initialization and
+allocation of resources needed for the nfb layer function.
+Communication between PMD and kernel modules is mediated by the libnfb library.
+The kernel module and library are not part of DPDK and must be installed
+separately. Dependencies can be found on GitHub:
+`nfb-framework <https://github.com/CESNET/ndk-sw>`_ as source code,
+or for RPM-based distributions, the prebuilt `nfb-framework` package on
+`Fedora Copr <https://copr.fedorainfracloud.org/coprs/g/CESNET/nfb-framework/>`_.
-More information about the
-`NFB cards <https://www.liberouter.org/technologies/cards/>`_
-and used technology
-(`Network Development Kit <https://www.liberouter.org/ndk/>`_)
-can be found on the `Liberouter website <http://www.liberouter.org/>`_.
+Before starting the DPDK, make sure that the kernel module is loaded (`sudo modprobe nfb`)
+and the card is running the CESNET-NDK based firmware (`nfb-info -l`).
.. note::
- Currently the driver is supported only on x86_64 architectures.
- Only x86_64 versions of the external libraries are provided.
-
-Prerequisites
--------------
-
-This PMD requires kernel modules which are responsible for initialization and
-allocation of resources needed for nfb layer function.
-Communication between PMD and kernel modules is mediated by libnfb library.
-These kernel modules and library are not part of DPDK and must be installed
-separately:
-
-* **libnfb library**
+ Currently, the driver is supported only on x86_64 architectures.
- The library provides API for initialization of nfb transfers, receiving and
- transmitting data segments.
+NFB card architecture
+---------------------
-* **Kernel modules**
+Ethernet Ports
+~~~~~~~~~~~~~~
- * nfb
+The NFB cards are multi-port multi-queue cards, where (generally) data from any
+Ethernet port may be sent by the firmware to any queue.
- Kernel modules manage initialization of hardware, allocation and
- sharing of resources for user space applications.
+The cards were historically represented in DPDK as a single port.
+Currently each Ethernet channel is represented as one DPDK port.
-Dependencies can be found here:
-`Netcope common <https://github.com/CESNET/ndk-sw>`_.
+.. note::
-Versions of the packages
-~~~~~~~~~~~~~~~~~~~~~~~~
+ Normally, one port corresponds to one channel, but ports can often be configured in a separate
+ manner. For example one 100G port can be used as 4x25G or 4x10G independent Ethernet channels.
-The minimum version of the provided packages:
+By default, all ports are initialized and used for the allowed PCI device.
+When this behaviour is limiting (e.g., for multiple instances of DPDK app on different ports
+of the same PCI device), ports can be specified by the `port` item in the `allow` argument:
-* for DPDK from 19.05
+.. code-block:: console
-Configuration
--------------
+ -a 0000:01:00.0,port=0,port=3
-Timestamps
+PCIe slots
+~~~~~~~~~~
-The PMD supports hardware timestamps of frame receipt on physical network interface. In order to use
-the timestamps, the hardware timestamping unit must be enabled (follow the documentation of the NFB
-products). The standard `RTE_ETH_RX_OFFLOAD_TIMESTAMP` flag can be used for this feature.
+Some cards employ more than one PCIe device for better data throughput. This can be achieved by
+slot bifurcation (only a minor improvement) or by an add-on cable connected to another PCIe slot.
+Both improvements can work together, as is, for example, in the case of the AGI-FH400G card.
-When the timestamps are enabled, a timestamp validity flag is set in the MBUFs
-containing received frames and timestamp is inserted into the `rte_mbuf` struct.
+Because primary and secondary slot(s) can be attached to different NUMA nodes
+(also applies for bifurcation on some HW), the data structures need to be correctly allocated.
+(Device-aware allocation matters also on IOMMU-enabled systems.)
+The firmware already provides DMA queue to PCI device mapping. The DPDK application just needs to
+use all PCI devices, otherwise some queues will not be available; provide all PCI endpoints
+listed in the `nfb-info -v` in the `allow` argument.
-The timestamp is an `uint64_t` field and holds the number of nanoseconds
-elapsed since 1.1.1970 00:00:00 UTC.
+.. note::
+ For cards where the number of Ethernet ports is less than the number of PCI devices
+ (e.g., AGI-FH400G: 1 port, up to 4 PCI devices), the virtual DPDK ports are
+ created to achieve the best NUMA-aware throughput
+ (virtual ports lack a of lot of configuration features).
-Using the NFB PMD
-----------------------
+Features
+--------
-Kernel modules have to be loaded before running the DPDK application.
-NFB card architecture
----------------------
-The NFB cards are multi-port multi-queue cards, where (generally) data from any
-Ethernet port may be sent to any queue.
-They are represented in DPDK as a single port.
-NFB-200G2QL card employs an add-on cable which allows to connect it to two
-physical PCI-E slots at the same time (see the diagram below).
-This is done to allow 200 Gbps of traffic to be transferred through the PCI-E
-bus (note that a single PCI-E 3.0 x16 slot provides only 125 Gbps theoretical
-throughput).
-Although each slot may be connected to a different CPU and therefore to a different
-NUMA node, the card is represented as a single port in DPDK. To work with data
-from the individual queues on the right NUMA node, connection of NUMA nodes on
-first and last queue (each NUMA node has half of the queues) need to be checked.
+Timestamps
+~~~~~~~~~~
-Limitations
------------
+The PMD supports hardware timestamps of frame receipt on physical network interface.
+In order to use the timestamps, the hardware timestamping unit must be enabled
+(follow the documentation of the NFB products).
+The standard `RTE_ETH_RX_OFFLOAD_TIMESTAMP` flag can be used for this feature.
-Driver is usable only on Linux architecture, namely on CentOS.
+When the timestamps are enabled, a timestamp validity flag is set in the MBUFs
+containing received frames and timestamp is inserted into the `rte_mbuf` struct.
-Since a card is always represented as a single port, but can be connected to two
-NUMA nodes, there is need for manual check where master/slave is connected.
+The timestamp is an `uint64_t` field and holds the number of nanoseconds
+elapsed since 1.1.1970 00:00:00 UTC.
-Example of usage
-----------------
+Simulation
+~~~~~~~~~~
-Read packets from 0. and 1. receive queue and write them to 0. and 1.
-transmit queue:
+The CESNET-NDK framework offers the possibility of simulating the firmware together with DPDK.
+This allows for easy debugging of a packet flow behaviour with a specific firmware configuration.
+The DPDK NFB driver can be connected to the simulator (Questa/ModelSim/nvc) via a virtual device:
.. code-block:: console
- ./<build_dir>/app/dpdk-testpmd -l 0-3 -n 2 \
- -- --port-topology=chained --rxq=2 --txq=2 --nb-cores=2 -i -a
-
-Example output:
-
-.. code-block:: console
+ dpdk-testpmd
+ --vdev=eth_vdev_nfb,dev=libnfb-ext-grpc.so:grpc+dma_vas:localhost:50051,queue_driver=native
+ --iova-mode=va -- -i`
- [...]
- EAL: PCI device 0000:06:00.0 on NUMA socket -1
- EAL: probe driver: 1b26:c1c1 net_nfb
- PMD: Initializing NFB device (0000:06:00.0)
- PMD: Available DMA queues RX: 8 TX: 8
- PMD: NFB device (0000:06:00.0) successfully initialized
- Interactive-mode selected
- Auto-start selected
- Configuring Port 0 (socket 0)
- Port 0: 00:11:17:00:00:00
- Checking link statuses...
- Port 0 Link Up - speed 10000 Mbps - full-duplex
- Done
- Start automatic packet forwarding
- io packet forwarding - CRC stripping disabled - packets/burst=32
- nb forwarding cores=2 - nb forwarding ports=1
- RX queues=2 - RX desc=128 - RX free threshold=0
- RX threshold registers: pthresh=0 hthresh=0 wthresh=0
- TX queues=2 - TX desc=512 - TX free threshold=0
- TX threshold registers: pthresh=0 hthresh=0 wthresh=0
- TX RS bit threshold=0 - TXQ flags=0x0
- testpmd>
+More info about the simulation can be found int the CESNET-NDK `documentation
+<https://cesnet.github.io/ndk-fpga/devel/ndk_apps/minimal/tests/cocotb/readme.html>`_.
--
2.52.0
^ permalink raw reply related [flat|nested] 131+ messages in thread
* Re: [PATCH 0/8] net/nfb: rework to real multiport
2026-01-15 15:16 [PATCH 0/8] net/nfb: rework to real multiport spinler
` (7 preceding siblings ...)
2026-01-15 15:16 ` [PATCH 8/8] doc/nfb: cleanup and update guide spinler
@ 2026-01-16 5:50 ` Stephen Hemminger
2026-01-16 16:44 ` [PATCH v2 " spinler
` (8 subsequent siblings)
17 siblings, 0 replies; 131+ messages in thread
From: Stephen Hemminger @ 2026-01-16 5:50 UTC (permalink / raw)
To: spinler; +Cc: dev
On Thu, 15 Jan 2026 16:16:48 +0100
spinler@cesnet.cz wrote:
> From: Martin Spinler <spinler@cesnet.cz>
>
> This series implements real multiport for better user experience.
>
> The existing driver creates one ethdev/port for one PCI device.
> As the CESNET-NDK based cards aren't capable to represent each
> Ethernet port by own PCI device, new driver implementation
> processes real port configuration from firmware/card and switches
> from rte_eth_dev_pci_generic_probe to multiple rte_eth_dev_create calls.
>
> ---
> Depends-on: series-37064 ("net/nfb: code cleanup")
>
> Martin Spinler (8):
> net/nfb: prepare for indirect queue mapping scheme
> net/nfb: create ethdev for every eth port/channel
> net/nfb: add vdev as alternative device probe method
> net/nfb: add device argument "port" to limit used ports
> net/nfb: init only MACs associated with device
> net/nfb: add compatible cards to driver PCI ID table
> net/nfb: report firmware version
> doc/nfb: cleanup and update guide
>
> doc/guides/nics/features/nfb.ini | 4 +
> doc/guides/nics/nfb.rst | 187 +++++++--------
> drivers/net/nfb/meson.build | 1 +
> drivers/net/nfb/nfb.h | 48 +++-
> drivers/net/nfb/nfb_ethdev.c | 399 +++++++++++++++++++++++--------
> drivers/net/nfb/nfb_rx.c | 21 +-
> drivers/net/nfb/nfb_tx.c | 21 +-
> drivers/net/nfb/nfb_vdev.c | 97 ++++++++
> 8 files changed, 551 insertions(+), 227 deletions(-)
> create mode 100644 drivers/net/nfb/nfb_vdev.c
>
> --
> 2.52.0
AI had some good feedback on this patch series.
Some of this noise though.
# DPDK NFB Multiport Patch Series Review
**Series:** `[PATCH 1-8/8]` NFB multiport support
**Author:** Martin Spinler <spinler@cesnet.cz>
**Date:** Thu, 15 Jan 2026
---
## Executive Summary
This patch series adds multiport support to the NFB driver, changing from a single ethdev per PCI device to one ethdev per Ethernet channel. The series is generally well-structured and follows DPDK conventions, but has several issues requiring attention before merging.
---
## Patch-by-Patch Review
### Patch 1/8: `net/nfb: prepare for indirect queue mapping scheme`
**Subject Line:** ✅ OK (52 chars, correct prefix, lowercase)
**Commit Message:**
- ⚠️ **WARNING:** Body is minimal - could use more context explaining *why* queue mapping needs to change
- ✅ Signed-off-by present
**Code Issues:**
| Line | Severity | Issue |
|------|----------|-------|
| 113-118 | ⚠️ Warning | Unnecessary braces for single-statement `for` loops |
| 161 | ⚠️ Warning | Comment typo: "neccessary" → "necessary" |
| 205 | ⚠️ Warning | Same typo: "neccessary" → "necessary" |
```c
// Current (unnecessary braces):
for (i = 0; i < priv->max_rx_queues; i++) {
internals->queue_map_rx[i] = i;
}
// Preferred:
for (i = 0; i < priv->max_rx_queues; i++)
internals->queue_map_rx[i] = i;
```
**Style:** ✅ OK - uses `rte_malloc`/`rte_free` appropriately for driver code
---
### Patch 2/8: `net/nfb: create ethdev for every eth port/channel`
**Subject Line:** ✅ OK (49 chars)
**Commit Message:**
- ✅ Good explanation of the change rationale
- ⚠️ **WARNING:** Line 297 "informations" → "information" (grammar)
- ✅ Signed-off-by present
**Code Issues:**
| Line | Severity | Issue |
|------|----------|-------|
| 341-342 | ⚠️ Warning | Double blank line after struct definition |
| 373 | ℹ️ Info | Consider using `TAILQ_HEAD_INITIALIZER` inline instead of separate declaration |
| 545-547 | ⚠️ Warning | Comment says "doesn't belong" but code checks `ep != ep_index` which is confusing |
**Structural Issues:**
- ✅ Good use of TAILQ for device list management
- ✅ Proper error cleanup with `goto` labels
- ⚠️ **WARNING:** `nfb_eth_common_probe()` declared in header but missing `__rte_internal` tag for internal API
---
### Patch 3/8: `net/nfb: add vdev as alternative device probe method`
**Subject Line:** ✅ OK (50 chars)
**Commit Message:**
- ✅ Clear explanation
- ✅ Signed-off-by present
**Code Issues:**
| Line | Severity | Issue |
|------|----------|-------|
| 719 | ❌ Error | Copyright year "2019" should be updated to current year (2026) for new file |
| 729-730 | ℹ️ Info | Double blank line |
| 766 | ⚠️ Warning | Cast `(signed)` is unusual - prefer explicit `int` comparison |
| 778 | ⚠️ Warning | `strcpy()` is unsafe - use `strlcpy()` or `snprintf()` |
```c
// Current (unsafe):
strcpy(params.name, vdev_name);
// Preferred:
strlcpy(params.name, vdev_name, sizeof(params.name));
// or
snprintf(params.name, sizeof(params.name), "%s", vdev_name);
```
| Line | Severity | Issue |
|------|----------|-------|
| 751, 784 | ⚠️ Warning | Using libc `strdup()`/`free()` - should use `rte_strdup()`/`rte_free()` for consistency |
**License:** ✅ SPDX-License-Identifier present and correct (BSD-3-Clause)
---
### Patch 4/8: `net/nfb: add device argument "port" to limit used ports`
**Subject Line:** ✅ OK (52 chars)
**Commit Message:**
- ⚠️ **WARNING:** Line 876 - "NFB devices does not" → "NFB devices do not" (grammar)
- ✅ Signed-off-by present
**Code Issues:**
| Line | Severity | Issue |
|------|----------|-------|
| 905-908 | ⚠️ Warning | `static const char * const` array defined in header - should be in .c file to avoid multiple definitions |
| 927 | ⚠️ Warning | Using hex base (16) for port parsing but doc says `port=<number>` - inconsistent |
| 971 | ⚠️ Warning | Missing space after cast: `(void*)` → `(void *)` |
```c
// Current:
if (rte_kvargs_process(kvlist, NFB_ARG_PORT, fill_port_mask, (void*) &port_mask))
// Preferred:
if (rte_kvargs_process(kvlist, NFB_ARG_PORT, fill_port_mask, (void *)&port_mask))
```
---
### Patch 5/8: `net/nfb: init only MACs associated with device`
**Subject Line:** ✅ OK (46 chars)
**Commit Message:**
- ⚠️ **WARNING:** "informations" → "information" (grammar)
- ✅ Signed-off-by present
**Code Issues:**
| Line | Severity | Issue |
|------|----------|-------|
| 1147-1148, 1155-1161, 1200-1206 | ⚠️ Warning | Unnecessary braces around single `if` body |
| 1192 | ℹ️ Info | Extra blank line at start of function body |
**Good practices observed:**
- ✅ Dynamic allocation of MAC arrays based on actual count
- ✅ Proper cleanup with `rte_free()`
---
### Patch 6/8: `net/nfb: add compatible cards to driver PCI ID table`
**Subject Line:** ✅ OK (52 chars)
**Commit Message:**
- ✅ Clear list of added cards
- ✅ Signed-off-by present
**Code Issues:**
| Line | Severity | Issue |
|------|----------|-------|
| 1421-1423 | ℹ️ Info | Inconsistent spacing in `RTE_PCI_DEVICE` calls - some have 1 space, some have 2 |
```c
// Inconsistent:
{ RTE_PCI_DEVICE(PCI_VENDOR_ID_SILICOM, PCI_DEVICE_ID_FB2CGHH) },
{ RTE_PCI_DEVICE(PCI_VENDOR_ID_CESNET, PCI_DEVICE_ID_COMBO400G1) }, // 2 spaces
// Should be consistent throughout
```
**Documentation:** ⚠️ **WARNING** - New device IDs should be mentioned in release notes
---
### Patch 7/8: `net/nfb: report firmware version`
**Subject Line:** ✅ OK (33 chars)
**Commit Message:**
- ❌ **ERROR:** Missing commit body - should explain what firmware version info is reported and why
- ✅ Signed-off-by present
**Code Issues:**
| Line | Severity | Issue |
|------|----------|-------|
| 1508-1509 | ⚠️ Warning | Unnecessary cast from `void *` - `dev->process_private` is already `void *` |
| 1521 | ⚠️ Warning | Cast `(signed)` is unusual - prefer `(int)fw_size` or comparison with explicit types |
```c
// Current (unnecessary cast):
struct pmd_internals *priv = (struct pmd_internals *)
dev->process_private;
// Preferred:
struct pmd_internals *priv = dev->process_private;
```
**Documentation:** ✅ Features matrix updated in patch 8
---
### Patch 8/8: `doc/nfb: cleanup and update guide`
**Subject Line:** ✅ OK (31 chars)
**Commit Message:**
- ❌ **ERROR:** Missing commit body - should summarize what's changed in docs
- ✅ Signed-off-by present
**Documentation Issues:**
| Line | Severity | Issue |
|------|----------|-------|
| 1674-1675 | ❌ Error | URL uses `http://` - should be `https://` per DPDK guidelines |
| 1761 | ⚠️ Warning | Typo: "lack a of lot" → "lack a lot" |
| 1767-1770 | ℹ️ Info | Extra blank lines in Features section |
| 1827 | ⚠️ Warning | Stray backtick at end: `-- -i\`` → `-- -i` |
```rst
// Current (line 1674):
`Liberouter website <http://www.liberouter.org/>`_.
// Should be https
```
**Features Matrix:**
- ✅ Added: Unicast MAC filter, Timestamp offload (P), FW version, Multiprocess aware
- ⚠️ Should verify these actually work as documented
---
## Summary of Issues
### Errors (Must Fix)
| Patch | Issue |
|-------|-------|
| 3/8 | Copyright year 2019 in new file should be 2026 |
| 7/8 | Missing commit body |
| 8/8 | Missing commit body |
| 8/8 | URL uses `http://` instead of `https://` |
### Warnings (Should Fix)
| Patch | Issue |
|-------|-------|
| 1/8 | Typo: "neccessary" (2 occurrences) |
| 2/8 | Grammar: "informations" |
| 2/8 | Missing `__rte_internal` on internal API |
| 3/8 | Unsafe `strcpy()` - use `strlcpy()` |
| 3/8 | libc `strdup()`/`free()` instead of `rte_*` variants |
| 4/8 | Grammar: "does not" → "do not" |
| 4/8 | `static const` array in header file |
| 4/8 | Missing space after cast |
| 5/8 | Grammar: "informations" |
| 6/8 | New device IDs need release notes |
| 7/8, 8/8 | Unnecessary casts from `void *` |
| 8/8 | Typo: "lack a of lot" |
| 8/8 | Stray backtick in command example |
### Info (Consider)
| Patch | Issue |
|-------|-------|
| 1/8, 5/8 | Unnecessary braces for single statements |
| 3/8, 4/8 | Double blank lines |
| 6/8 | Inconsistent spacing in macro calls |
---
## Missing Items
1. **Release Notes:** No `doc/guides/rel_notes/` entry for this significant driver change
2. **Cc: stable@dpdk.org:** Not applicable (new feature, not a fix)
3. **Cover Letter:** Series lacks a cover letter explaining the overall motivation and design
---
## Recommendations
1. Add a cover letter (patch 0/8) explaining the multiport architecture changes
2. Update release notes for the new multiport support and new device IDs
3. Fix all typos and grammar issues in commit messages
4. Add commit bodies to patches 7/8 and 8/8
5. Update copyright year in new files
6. Replace unsafe string functions with safe variants
7. Consider using `rte_strdup()` instead of libc `strdup()` for consistency
---
## Verdict
**Needs Revision (v2)**
The patch series is fundamentally sound and addresses a real limitation of the existing driver. However, there are several documentation and style issues that should be addressed before merging. The most critical issues are the missing commit bodies, documentation URL protocol, and the unsafe `strcpy()` usage.
^ permalink raw reply [flat|nested] 131+ messages in thread
* [PATCH v2 0/8] net/nfb: rework to real multiport
2026-01-15 15:16 [PATCH 0/8] net/nfb: rework to real multiport spinler
` (8 preceding siblings ...)
2026-01-16 5:50 ` [PATCH 0/8] net/nfb: rework to real multiport Stephen Hemminger
@ 2026-01-16 16:44 ` spinler
2026-01-16 16:44 ` [PATCH v2 1/8] net/nfb: prepare for indirect queue mapping scheme spinler
` (8 more replies)
2026-01-21 17:03 ` [PATCH v3 " spinler
` (7 subsequent siblings)
17 siblings, 9 replies; 131+ messages in thread
From: spinler @ 2026-01-16 16:44 UTC (permalink / raw)
To: dev; +Cc: Martin Spinler
From: Martin Spinler <spinler@cesnet.cz>
This series implements real multiport for better user experience.
The existing driver creates one ethdev/port for one PCI device.
As the CESNET-NDK based cards aren't capable to represent each
Ethernet port by own PCI device, new driver implementation
processes real port configuration from firmware/card and switches
from rte_eth_dev_pci_generic_probe to multiple rte_eth_dev_create calls.
---
v2:
* Changed strcpy to strlcpy.
* Moved static array VALID_KEYS from header file.
* Fixed hex base number parsing for port argument.
* The strlcpy is used instead of strcpy.
* Used __rte_internal tag for nfb_eth_common_{probe,remove}.
* Fixed coding style and spelling issues.
Depends-on: series-37064 ("net/nfb: code cleanup")
Martin Spinler (8):
net/nfb: prepare for indirect queue mapping scheme
net/nfb: create ethdev for every eth port/channel
net/nfb: add vdev as alternative device probe method
net/nfb: add device argument "port" to limit used ports
net/nfb: init only MACs associated with device
net/nfb: add compatible cards to driver PCI ID table
net/nfb: report firmware version
doc/nfb: cleanup and update guide
doc/guides/nics/features/nfb.ini | 4 +
doc/guides/nics/nfb.rst | 187 +++++++-------
drivers/net/nfb/meson.build | 1 +
drivers/net/nfb/nfb.h | 46 +++-
drivers/net/nfb/nfb_ethdev.c | 407 +++++++++++++++++++++++--------
drivers/net/nfb/nfb_rx.c | 21 +-
drivers/net/nfb/nfb_tx.c | 21 +-
drivers/net/nfb/nfb_vdev.c | 96 ++++++++
8 files changed, 554 insertions(+), 229 deletions(-)
create mode 100644 drivers/net/nfb/nfb_vdev.c
--
2.52.0
^ permalink raw reply [flat|nested] 131+ messages in thread
* [PATCH v2 1/8] net/nfb: prepare for indirect queue mapping scheme
2026-01-16 16:44 ` [PATCH v2 " spinler
@ 2026-01-16 16:44 ` spinler
2026-01-16 16:44 ` [PATCH v2 2/8] net/nfb: create ethdev for every eth port/channel spinler
` (7 subsequent siblings)
8 siblings, 0 replies; 131+ messages in thread
From: spinler @ 2026-01-16 16:44 UTC (permalink / raw)
To: dev; +Cc: Martin Spinler
From: Martin Spinler <spinler@cesnet.cz>
This prepares queue mapping for port-aware driver implementation.
Signed-off-by: Martin Spinler <spinler@cesnet.cz>
---
drivers/net/nfb/nfb.h | 4 ++++
drivers/net/nfb/nfb_ethdev.c | 19 +++++++++++++++++++
drivers/net/nfb/nfb_rx.c | 21 ++++++++++++---------
drivers/net/nfb/nfb_tx.c | 21 ++++++++++++---------
4 files changed, 47 insertions(+), 18 deletions(-)
diff --git a/drivers/net/nfb/nfb.h b/drivers/net/nfb/nfb.h
index 09d4b7da5f..0361859fc1 100644
--- a/drivers/net/nfb/nfb.h
+++ b/drivers/net/nfb/nfb.h
@@ -47,6 +47,10 @@ struct pmd_internals {
uint16_t max_txmac;
struct nc_rxmac *rxmac[RTE_MAX_NC_RXMAC];
struct nc_txmac *txmac[RTE_MAX_NC_TXMAC];
+
+ int *queue_map_rx;
+ int *queue_map_tx;
+
struct nfb_device *nfb;
};
diff --git a/drivers/net/nfb/nfb_ethdev.c b/drivers/net/nfb/nfb_ethdev.c
index 59d9987789..238a3004c5 100644
--- a/drivers/net/nfb/nfb_ethdev.c
+++ b/drivers/net/nfb/nfb_ethdev.c
@@ -509,6 +509,7 @@ static const struct eth_dev_ops ops = {
static int
nfb_eth_dev_init(struct rte_eth_dev *dev)
{
+ int i;
int ret;
uint32_t mac_count;
struct rte_eth_dev_data *data = dev->data;
@@ -566,6 +567,21 @@ nfb_eth_dev_init(struct rte_eth_dev *dev)
dev->rx_pkt_burst = nfb_eth_ndp_rx;
dev->tx_pkt_burst = nfb_eth_ndp_tx;
+ internals->queue_map_rx = rte_malloc("NFB queue map",
+ sizeof(*internals->queue_map_rx) *
+ (priv->max_rx_queues + priv->max_tx_queues), 0);
+ if (internals->queue_map_rx == NULL) {
+ ret = -ENOMEM;
+ goto err_alloc_queue_map;
+ }
+ internals->queue_map_tx = internals->queue_map_rx + priv->max_rx_queues;
+
+ /* default queue mapping is 1:1 */
+ for (i = 0; i < priv->max_rx_queues; i++)
+ internals->queue_map_rx[i] = i;
+ for (i = 0; i < priv->max_tx_queues; i++)
+ internals->queue_map_tx[i] = i;
+
/* Set function callbacks for Ethernet API */
dev->dev_ops = &ops;
@@ -604,6 +620,8 @@ nfb_eth_dev_init(struct rte_eth_dev *dev)
return 0;
err_malloc_mac_addrs:
+ rte_free(internals->queue_map_rx);
+err_alloc_queue_map:
nfb_nc_rxmac_deinit(internals->rxmac, internals->max_rxmac);
nfb_nc_txmac_deinit(internals->txmac, internals->max_txmac);
nfb_close(internals->nfb);
@@ -634,6 +652,7 @@ nfb_eth_dev_uninit(struct rte_eth_dev *dev)
nfb_nc_txmac_deinit(internals->txmac, internals->max_txmac);
nfb_close(internals->nfb);
+ rte_free(internals->queue_map_rx);
rte_free(internals);
NFB_LOG(INFO, "NFB device (" PCI_PRI_FMT ") successfully uninitialized",
diff --git a/drivers/net/nfb/nfb_rx.c b/drivers/net/nfb/nfb_rx.c
index 413d275853..dba5755bdc 100644
--- a/drivers/net/nfb/nfb_rx.c
+++ b/drivers/net/nfb/nfb_rx.c
@@ -63,6 +63,7 @@ nfb_eth_rx_queue_setup(struct rte_eth_dev *dev,
struct pmd_internals *internals = dev->process_private;
struct ndp_rx_queue *rxq;
+ int nfb_qid;
int ret;
rxq = rte_zmalloc_socket("ndp rx queue",
@@ -77,17 +78,19 @@ nfb_eth_rx_queue_setup(struct rte_eth_dev *dev,
rxq->flags = 0;
- ret = nfb_eth_rx_queue_init(internals->nfb,
- rx_queue_id,
- dev->data->port_id,
- mb_pool,
- rxq);
+ /* nfb queue id doesn't necessary corresponds to txq_id */
+ nfb_qid = internals->queue_map_rx[rx_queue_id];
- if (ret == 0)
- dev->data->rx_queues[rx_queue_id] = rxq;
- else
- rte_free(rxq);
+ ret = nfb_eth_rx_queue_init(internals->nfb, nfb_qid,
+ dev->data->port_id, mb_pool, rxq);
+ if (ret)
+ goto err_queue_init;
+
+ dev->data->rx_queues[rx_queue_id] = rxq;
+ return 0;
+err_queue_init:
+ rte_free(rxq);
return ret;
}
diff --git a/drivers/net/nfb/nfb_tx.c b/drivers/net/nfb/nfb_tx.c
index 1f997ce22f..8ea62ec62c 100644
--- a/drivers/net/nfb/nfb_tx.c
+++ b/drivers/net/nfb/nfb_tx.c
@@ -55,10 +55,10 @@ nfb_eth_tx_queue_setup(struct rte_eth_dev *dev,
{
struct pmd_internals *internals = dev->process_private;
int ret;
+ int nfb_qid;
struct ndp_tx_queue *txq;
- txq = rte_zmalloc_socket("ndp tx queue",
- sizeof(struct ndp_tx_queue),
+ txq = rte_zmalloc_socket("ndp tx queue", sizeof(struct ndp_tx_queue),
RTE_CACHE_LINE_SIZE, socket_id);
if (txq == NULL) {
@@ -67,15 +67,18 @@ nfb_eth_tx_queue_setup(struct rte_eth_dev *dev,
return -ENOMEM;
}
- ret = nfb_eth_tx_queue_init(internals->nfb,
- tx_queue_id,
- txq);
+ /* nfb queue id doesn't necessary corresponds to tx_queue_id */
+ nfb_qid = internals->queue_map_tx[tx_queue_id];
- if (ret == 0)
- dev->data->tx_queues[tx_queue_id] = txq;
- else
- rte_free(txq);
+ ret = nfb_eth_tx_queue_init(internals->nfb, nfb_qid, txq);
+ if (ret)
+ goto err_queue_init;
+
+ dev->data->tx_queues[tx_queue_id] = txq;
+ return 0;
+err_queue_init:
+ rte_free(txq);
return ret;
}
--
2.52.0
^ permalink raw reply related [flat|nested] 131+ messages in thread
* [PATCH v2 2/8] net/nfb: create ethdev for every eth port/channel
2026-01-16 16:44 ` [PATCH v2 " spinler
2026-01-16 16:44 ` [PATCH v2 1/8] net/nfb: prepare for indirect queue mapping scheme spinler
@ 2026-01-16 16:44 ` spinler
2026-01-16 16:44 ` [PATCH v2 3/8] net/nfb: add vdev as alternative device probe method spinler
` (6 subsequent siblings)
8 siblings, 0 replies; 131+ messages in thread
From: spinler @ 2026-01-16 16:44 UTC (permalink / raw)
To: dev; +Cc: Martin Spinler
From: Martin Spinler <spinler@cesnet.cz>
The existing driver creates one ethdev/port for one PCI device.
As the CESNET-NDK-based cards are mostly unable to represent
each physical port with one PCI device, this atypical behaviour
often limits the user.
The nfb-framework comes with generic port representation API,
which provides information about ports and their mapping to firmware
resource (physical ports, DMA queues, processing pipelines).
This driver design switches from one rte_eth_dev_pci_generic_probe call
to multiple rte_eth_dev_create calls, while parses the firmware mapping
information.
Signed-off-by: Martin Spinler <spinler@cesnet.cz>
---
drivers/net/nfb/nfb.h | 24 +++++
drivers/net/nfb/nfb_ethdev.c | 180 ++++++++++++++++++++++++++---------
2 files changed, 159 insertions(+), 45 deletions(-)
diff --git a/drivers/net/nfb/nfb.h b/drivers/net/nfb/nfb.h
index 0361859fc1..bd381d4320 100644
--- a/drivers/net/nfb/nfb.h
+++ b/drivers/net/nfb/nfb.h
@@ -11,6 +11,7 @@
#include <nfb/ndp.h>
#include <netcope/rxmac.h>
#include <netcope/txmac.h>
+#include <netcope/info.h>
extern int nfb_logtype;
#define RTE_LOGTYPE_NFB nfb_logtype
@@ -51,6 +52,9 @@ struct pmd_internals {
int *queue_map_rx;
int *queue_map_tx;
+ TAILQ_ENTRY(pmd_internals) eth_dev_list;
+ struct rte_eth_dev *eth_dev;
+
struct nfb_device *nfb;
};
@@ -59,4 +63,24 @@ struct pmd_priv {
uint16_t max_tx_queues;
};
+struct nfb_init_params {
+ const char *path;
+ const char *args;
+ int nfb_id;
+
+ struct nc_ifc_map_info map_info;
+ struct nc_ifc_info *ifc_info;
+
+ char name[RTE_DEV_NAME_MAX_LEN];
+};
+
+
+__rte_internal
+int
+nfb_eth_common_probe(struct rte_device *device,
+ ethdev_bus_specific_init specific_init, void *specific_device,
+ struct nfb_init_params *params, int ep_index);
+__rte_internal
+int nfb_eth_common_remove(struct rte_device *dev);
+
#endif /* _NFB_H_ */
diff --git a/drivers/net/nfb/nfb_ethdev.c b/drivers/net/nfb/nfb_ethdev.c
index 238a3004c5..2afd2a42db 100644
--- a/drivers/net/nfb/nfb_ethdev.c
+++ b/drivers/net/nfb/nfb_ethdev.c
@@ -4,6 +4,10 @@
* All rights reserved.
*/
+#include <sys/queue.h>
+#include <eal_export.h>
+#include <rte_tailq.h>
+
#include <nfb/nfb.h>
#include <nfb/ndp.h>
#include <netcope/rxmac.h>
@@ -18,6 +22,13 @@
#include "nfb_rxmode.h"
#include "nfb.h"
+/* The TAILQ entries are used for cleanup of allocated resources
+ * in situations, where dev_close is not called.
+ */
+TAILQ_HEAD(pmd_internals_head, pmd_internals);
+static struct pmd_internals_head nfb_eth_dev_list =
+ TAILQ_HEAD_INITIALIZER(nfb_eth_dev_list);
+
static int nfb_eth_dev_uninit(struct rte_eth_dev *dev);
/**
@@ -507,22 +518,18 @@ static const struct eth_dev_ops ops = {
* 0 on success, a negative errno value otherwise.
*/
static int
-nfb_eth_dev_init(struct rte_eth_dev *dev)
+nfb_eth_dev_init(struct rte_eth_dev *dev, void *init_data)
{
int i;
+ int cnt;
int ret;
- uint32_t mac_count;
struct rte_eth_dev_data *data = dev->data;
struct pmd_internals *internals;
+ struct nfb_init_params *params = init_data;
+ struct nc_ifc_info *ifc = params->ifc_info;
+ struct nc_ifc_map_info *mi = ¶ms->map_info;
struct pmd_priv *priv = data->dev_private;
- struct rte_pci_device *pci_dev = RTE_ETH_DEV_TO_PCI(dev);
- struct rte_pci_addr *pci_addr = &pci_dev->addr;
struct rte_ether_addr eth_addr_init;
- char nfb_dev[PATH_MAX];
-
- NFB_LOG(INFO, "Initializing NFB device (" PCI_PRI_FMT ")",
- pci_addr->domain, pci_addr->bus, pci_addr->devid,
- pci_addr->function);
internals = rte_zmalloc_socket("nfb_internals",
sizeof(struct pmd_internals), RTE_CACHE_LINE_SIZE,
@@ -534,27 +541,13 @@ nfb_eth_dev_init(struct rte_eth_dev *dev)
dev->process_private = internals;
- snprintf(nfb_dev, sizeof(nfb_dev),
- "/dev/nfb/by-pci-slot/" PCI_PRI_FMT,
- pci_addr->domain, pci_addr->bus, pci_addr->devid,
- pci_addr->function);
-
- /*
- * Get number of available DMA RX and TX queues, which is maximum
- * number of queues that can be created and store it in private device
- * data structure.
- */
- internals->nfb = nfb_open(nfb_dev);
+ /* Open device handle */
+ internals->nfb = nfb_open(params->path);
if (internals->nfb == NULL) {
- NFB_LOG(ERR, "nfb_open(): failed to open %s", nfb_dev);
+ NFB_LOG(ERR, "nfb_open(): failed to open %s", params->path);
ret = -EINVAL;
goto err_nfb_open;
}
- priv->max_rx_queues = ndp_get_rx_queue_available_count(internals->nfb);
- priv->max_tx_queues = ndp_get_tx_queue_available_count(internals->nfb);
-
- NFB_LOG(INFO, "Available NDP queues RX: %u TX: %u",
- priv->max_rx_queues, priv->max_tx_queues);
nfb_nc_rxmac_init(internals->nfb,
internals->rxmac,
@@ -567,6 +560,10 @@ nfb_eth_dev_init(struct rte_eth_dev *dev)
dev->rx_pkt_burst = nfb_eth_ndp_rx;
dev->tx_pkt_burst = nfb_eth_ndp_tx;
+ /* Get number of available DMA RX and TX queues */
+ priv->max_rx_queues = ifc->rxq_cnt;
+ priv->max_tx_queues = ifc->txq_cnt;
+
internals->queue_map_rx = rte_malloc("NFB queue map",
sizeof(*internals->queue_map_rx) *
(priv->max_rx_queues + priv->max_tx_queues), 0);
@@ -576,11 +573,17 @@ nfb_eth_dev_init(struct rte_eth_dev *dev)
}
internals->queue_map_tx = internals->queue_map_rx + priv->max_rx_queues;
- /* default queue mapping is 1:1 */
- for (i = 0; i < priv->max_rx_queues; i++)
- internals->queue_map_rx[i] = i;
- for (i = 0; i < priv->max_tx_queues; i++)
- internals->queue_map_tx[i] = i;
+ cnt = 0;
+ for (i = 0; i < mi->rxq_cnt; i++) {
+ if (mi->rxq[i].ifc == ifc->id)
+ internals->queue_map_rx[cnt++] = mi->rxq[i].id;
+ }
+
+ cnt = 0;
+ for (i = 0; i < mi->txq_cnt; i++) {
+ if (mi->txq[i].ifc == ifc->id)
+ internals->queue_map_tx[cnt++] = mi->txq[i].id;
+ }
/* Set function callbacks for Ethernet API */
dev->dev_ops = &ops;
@@ -590,9 +593,9 @@ nfb_eth_dev_init(struct rte_eth_dev *dev)
if (rte_eal_process_type() == RTE_PROC_PRIMARY) {
/* Allocate space for MAC addresses */
- mac_count = nfb_eth_get_max_mac_address_count(dev);
+ cnt = nfb_eth_get_max_mac_address_count(dev);
data->mac_addrs = rte_zmalloc(data->name,
- sizeof(struct rte_ether_addr) * mac_count, RTE_CACHE_LINE_SIZE);
+ sizeof(struct rte_ether_addr) * cnt, RTE_CACHE_LINE_SIZE);
if (data->mac_addrs == NULL) {
NFB_LOG(ERR, "Could not alloc space for MAC address");
ret = -ENOMEM;
@@ -613,10 +616,6 @@ nfb_eth_dev_init(struct rte_eth_dev *dev)
data->dev_flags |= RTE_ETH_DEV_AUTOFILL_QUEUE_XSTATS;
}
- NFB_LOG(INFO, "NFB device (" PCI_PRI_FMT ") successfully initialized",
- pci_addr->domain, pci_addr->bus, pci_addr->devid,
- pci_addr->function);
-
return 0;
err_malloc_mac_addrs:
@@ -644,10 +643,10 @@ nfb_eth_dev_init(struct rte_eth_dev *dev)
static int
nfb_eth_dev_uninit(struct rte_eth_dev *dev)
{
- struct rte_pci_device *pci_dev = RTE_ETH_DEV_TO_PCI(dev);
- struct rte_pci_addr *pci_addr = &pci_dev->addr;
struct pmd_internals *internals = dev->process_private;
+ TAILQ_REMOVE(&nfb_eth_dev_list, internals, eth_dev_list);
+
nfb_nc_rxmac_deinit(internals->rxmac, internals->max_rxmac);
nfb_nc_txmac_deinit(internals->txmac, internals->max_txmac);
nfb_close(internals->nfb);
@@ -655,9 +654,61 @@ nfb_eth_dev_uninit(struct rte_eth_dev *dev)
rte_free(internals->queue_map_rx);
rte_free(internals);
- NFB_LOG(INFO, "NFB device (" PCI_PRI_FMT ") successfully uninitialized",
- pci_addr->domain, pci_addr->bus, pci_addr->devid,
- pci_addr->function);
+ return 0;
+}
+
+RTE_EXPORT_INTERNAL_SYMBOL(nfb_eth_common_probe)
+int
+nfb_eth_common_probe(struct rte_device *device,
+ ethdev_bus_specific_init specific_init, void *specific_device,
+ struct nfb_init_params *params, int ep_index)
+{
+ int i;
+ int ret;
+ int basename_len;
+
+ struct nc_ifc_info *ifc;
+ struct nfb_device *nfb_dev;
+ struct rte_eth_dev *eth_dev;
+ struct pmd_internals *p;
+
+ basename_len = strlen(params->name);
+
+ nfb_dev = nfb_open(params->path);
+ if (nfb_dev == NULL) {
+ NFB_LOG(ERR, "nfb_open(): failed to open %s", params->path);
+ return -EINVAL;
+ }
+
+ nc_ifc_map_info_create_ordinary(nfb_dev, ¶ms->map_info);
+
+ for (i = 0; i < params->map_info.ifc_cnt; i++) {
+ ifc = ¶ms->map_info.ifc[i];
+ params->ifc_info = ifc;
+
+ /* Skip interfaces which doesn't belong to this PCI device */
+ if ((ep_index != -1 && ifc->ep != ep_index) ||
+ (ifc->flags & NC_IFC_INFO_FLAG_ACTIVE) == 0)
+ continue;
+
+ snprintf(params->name + basename_len, sizeof(params->name) - basename_len,
+ "_eth%d", params->ifc_info->id);
+
+ ret = rte_eth_dev_create(device, params->name,
+ sizeof(struct pmd_priv),
+ specific_init, specific_device,
+ nfb_eth_dev_init, params);
+
+ if (ret == 0) {
+ eth_dev = rte_eth_dev_get_by_name(params->name);
+ p = eth_dev->process_private;
+ p->eth_dev = eth_dev;
+ TAILQ_INSERT_TAIL(&nfb_eth_dev_list, p, eth_dev_list);
+ }
+ }
+
+ nc_map_info_destroy(¶ms->map_info);
+ nfb_close(nfb_dev);
return 0;
}
@@ -688,8 +739,47 @@ static int
nfb_eth_pci_probe(struct rte_pci_driver *pci_drv __rte_unused,
struct rte_pci_device *pci_dev)
{
- return rte_eth_dev_pci_generic_probe(pci_dev,
- sizeof(struct pmd_priv), nfb_eth_dev_init);
+ int ret;
+ char path[PATH_MAX];
+
+ struct nc_composed_device_info comp_dev_info;
+ struct nfb_init_params params = {0};
+
+ rte_pci_device_name(&pci_dev->addr, params.name, sizeof(params.name));
+
+ /* NFB device can be composed from multiple PCI devices,
+ * find the base char device ID for the current PCI device
+ */
+ ret = nc_get_composed_device_info_by_pci(NULL, params.name, &comp_dev_info);
+ if (ret) {
+ NFB_LOG(ERR, "Could not find NFB device for %s", params.name);
+ return -ENODEV;
+ }
+
+ ret = snprintf(path, sizeof(path), NFB_BASE_DEV_PATH "%d", comp_dev_info.nfb_id);
+ RTE_ASSERT(ret > 0 && ret < sizeof(path));
+
+ params.args = pci_dev->device.devargs ? pci_dev->device.devargs->args : NULL;
+ params.path = path;
+ params.nfb_id = comp_dev_info.nfb_id;
+
+ return nfb_eth_common_probe(&pci_dev->device, eth_dev_pci_specific_init, pci_dev, ¶ms,
+ comp_dev_info.ep_index);
+}
+
+RTE_EXPORT_INTERNAL_SYMBOL(nfb_eth_common_remove)
+int
+nfb_eth_common_remove(struct rte_device *dev)
+{
+ struct pmd_internals *entry, *temp;
+
+ RTE_TAILQ_FOREACH_SAFE(entry, &nfb_eth_dev_list, eth_dev_list, temp) {
+ if (dev == entry->eth_dev->device) {
+ TAILQ_REMOVE(&nfb_eth_dev_list, entry, eth_dev_list);
+ rte_eth_dev_destroy(entry->eth_dev, nfb_eth_dev_uninit);
+ }
+ }
+ return 0;
}
/**
@@ -706,7 +796,7 @@ nfb_eth_pci_probe(struct rte_pci_driver *pci_drv __rte_unused,
static int
nfb_eth_pci_remove(struct rte_pci_device *pci_dev)
{
- return rte_eth_dev_pci_generic_remove(pci_dev, nfb_eth_dev_uninit);
+ return nfb_eth_common_remove(&pci_dev->device);
}
static struct rte_pci_driver nfb_eth_driver = {
--
2.52.0
^ permalink raw reply related [flat|nested] 131+ messages in thread
* [PATCH v2 3/8] net/nfb: add vdev as alternative device probe method
2026-01-16 16:44 ` [PATCH v2 " spinler
2026-01-16 16:44 ` [PATCH v2 1/8] net/nfb: prepare for indirect queue mapping scheme spinler
2026-01-16 16:44 ` [PATCH v2 2/8] net/nfb: create ethdev for every eth port/channel spinler
@ 2026-01-16 16:44 ` spinler
2026-01-16 16:44 ` [PATCH v2 4/8] net/nfb: add device argument "port" to limit used ports spinler
` (5 subsequent siblings)
8 siblings, 0 replies; 131+ messages in thread
From: spinler @ 2026-01-16 16:44 UTC (permalink / raw)
To: dev; +Cc: Martin Spinler
From: Martin Spinler <spinler@cesnet.cz>
VDEV are useful for NFB devices not represented by PCI device,
e.g. virtual or simulated devices.
Signed-off-by: Martin Spinler <spinler@cesnet.cz>
---
drivers/net/nfb/meson.build | 1 +
drivers/net/nfb/nfb_vdev.c | 95 +++++++++++++++++++++++++++++++++++++
2 files changed, 96 insertions(+)
create mode 100644 drivers/net/nfb/nfb_vdev.c
diff --git a/drivers/net/nfb/meson.build b/drivers/net/nfb/meson.build
index d7a255c928..9e458dfb4a 100644
--- a/drivers/net/nfb/meson.build
+++ b/drivers/net/nfb/meson.build
@@ -16,6 +16,7 @@ ext_deps += dep
sources = files(
'nfb_ethdev.c',
+ 'nfb_vdev.c',
'nfb_rx.c',
'nfb_rxmode.c',
'nfb_stats.c',
diff --git a/drivers/net/nfb/nfb_vdev.c b/drivers/net/nfb/nfb_vdev.c
new file mode 100644
index 0000000000..3a8d8b0531
--- /dev/null
+++ b/drivers/net/nfb/nfb_vdev.c
@@ -0,0 +1,95 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2026 Cesnet
+ */
+
+#include <rte_kvargs.h>
+#include <ethdev_vdev.h>
+
+#include "nfb.h"
+
+#define VDEV_NFB_DRIVER net_vdev_nfb
+#define VDEV_NFB_ARG_DEV "dev"
+
+static int
+vdev_nfb_vdev_probe(struct rte_vdev_device *dev)
+{
+ int i;
+ int ret = 0;
+
+ struct nfb_init_params params;
+
+ const char *vdev_name = rte_vdev_device_name(dev);
+ const char *vdev_args = rte_vdev_device_args(dev);
+ char *dev_params, *dev_params_mod;
+ struct rte_kvargs *kvargs;
+
+ kvargs = rte_kvargs_parse(vdev_args, NULL);
+ if (kvargs == NULL) {
+ NFB_LOG(ERR, "Failed to parse device arguments %s", vdev_args);
+ ret = -EINVAL;
+ goto err_parse_args;
+ }
+
+ dev_params = strdup(vdev_args);
+ if (dev_params == NULL) {
+ ret = -ENOMEM;
+ goto err_strdup_params;
+ }
+
+ params.args = dev_params;
+ params.path = nfb_default_dev_path();
+ params.nfb_id = 0;
+
+ dev_params_mod = dev_params;
+
+ dev_params_mod[0] = 0;
+
+ /* Parse parameters for virtual device */
+ for (i = 0; i != (signed int)kvargs->count; ++i) {
+ const struct rte_kvargs_pair *pair = &kvargs->pairs[i];
+
+ if (!strcmp(pair->key, VDEV_NFB_ARG_DEV)) {
+ params.path = pair->value;
+ } else {
+ dev_params_mod += sprintf(dev_params_mod, "%s%s=%s",
+ dev_params_mod == dev_params ? "" : ",",
+ pair->key, pair->value);
+ }
+ }
+
+ strlcpy(params.name, vdev_name, sizeof(params.name));
+
+ ret = nfb_eth_common_probe(&dev->device, NULL, NULL, ¶ms, -1);
+ if (ret)
+ goto err_nfb_common_probe;
+
+ free(dev_params);
+ rte_kvargs_free(kvargs);
+
+ return ret;
+
+err_nfb_common_probe:
+ free(dev_params);
+err_strdup_params:
+ rte_kvargs_free(kvargs);
+err_parse_args:
+ return ret;
+}
+
+static int
+vdev_nfb_vdev_remove(struct rte_vdev_device *dev)
+{
+ return nfb_eth_common_remove(&dev->device);
+}
+
+/** Virtual device descriptor. */
+static struct rte_vdev_driver vdev_nfb_vdev = {
+ .probe = vdev_nfb_vdev_probe,
+ .remove = vdev_nfb_vdev_remove,
+};
+
+RTE_PMD_REGISTER_VDEV(VDEV_NFB_DRIVER, vdev_nfb_vdev);
+RTE_PMD_REGISTER_ALIAS(VDEV_NFB_DRIVER, eth_vdev_nfb);
+RTE_PMD_REGISTER_PARAM_STRING(net_vdev_nfb,
+ VDEV_NFB_ARG_DEV "=<string>"
+ );
--
2.52.0
^ permalink raw reply related [flat|nested] 131+ messages in thread
* [PATCH v2 4/8] net/nfb: add device argument "port" to limit used ports
2026-01-16 16:44 ` [PATCH v2 " spinler
` (2 preceding siblings ...)
2026-01-16 16:44 ` [PATCH v2 3/8] net/nfb: add vdev as alternative device probe method spinler
@ 2026-01-16 16:44 ` spinler
2026-01-16 16:44 ` [PATCH v2 5/8] net/nfb: init only MACs associated with device spinler
` (4 subsequent siblings)
8 siblings, 0 replies; 131+ messages in thread
From: spinler @ 2026-01-16 16:44 UTC (permalink / raw)
To: dev; +Cc: Martin Spinler
From: Martin Spinler <spinler@cesnet.cz>
NFB devices do not use separate PCI device for each port; one PCI device
contains handles for all ports instead. For some application this approach
can be limiting.
The "port" argument can be used to select only desired ports.
It can be used multiple times. When is not used at all, the driver
selects all ports.
Signed-off-by: Martin Spinler <spinler@cesnet.cz>
---
drivers/net/nfb/nfb.h | 6 ++++
drivers/net/nfb/nfb_ethdev.c | 55 +++++++++++++++++++++++++++++++++++-
drivers/net/nfb/nfb_vdev.c | 3 +-
3 files changed, 62 insertions(+), 2 deletions(-)
diff --git a/drivers/net/nfb/nfb.h b/drivers/net/nfb/nfb.h
index bd381d4320..04d06a75cb 100644
--- a/drivers/net/nfb/nfb.h
+++ b/drivers/net/nfb/nfb.h
@@ -42,6 +42,12 @@ extern int nfb_logtype;
#define RTE_NFB_DRIVER_NAME net_nfb
+/* Device arguments */
+#define NFB_ARG_PORT "port"
+
+#define NFB_COMMON_ARGS \
+ NFB_ARG_PORT "=<number>" \
+ ""
struct pmd_internals {
uint16_t max_rxmac;
diff --git a/drivers/net/nfb/nfb_ethdev.c b/drivers/net/nfb/nfb_ethdev.c
index 2afd2a42db..0a948e4a2a 100644
--- a/drivers/net/nfb/nfb_ethdev.c
+++ b/drivers/net/nfb/nfb_ethdev.c
@@ -22,6 +22,11 @@
#include "nfb_rxmode.h"
#include "nfb.h"
+static const char * const VALID_KEYS[] = {
+ NFB_ARG_PORT,
+ NULL
+};
+
/* The TAILQ entries are used for cleanup of allocated resources
* in situations, where dev_close is not called.
*/
@@ -657,6 +662,24 @@ nfb_eth_dev_uninit(struct rte_eth_dev *dev)
return 0;
}
+static int fill_port_mask(const char *key __rte_unused, const char *value, void *opaque)
+{
+ int ret = 0;
+ char *end = NULL;
+ uint64_t *port_mask = opaque;
+ int port;
+
+ port = strtol(value, &end, 0);
+ if ((value[0] == '\0') || end == NULL || (*end != '\0'))
+ ret = -1;
+
+ if (ret != 0 || port >= 64 || port < 0)
+ return -1;
+
+ *port_mask |= (1ull << port);
+ return 0;
+}
+
RTE_EXPORT_INTERNAL_SYMBOL(nfb_eth_common_probe)
int
nfb_eth_common_probe(struct rte_device *device,
@@ -664,7 +687,7 @@ nfb_eth_common_probe(struct rte_device *device,
struct nfb_init_params *params, int ep_index)
{
int i;
- int ret;
+ int ret = 0;
int basename_len;
struct nc_ifc_info *ifc;
@@ -672,6 +695,9 @@ nfb_eth_common_probe(struct rte_device *device,
struct rte_eth_dev *eth_dev;
struct pmd_internals *p;
+ struct rte_kvargs *kvlist;
+ uint64_t port_mask = 0xFFFFFFFFFFFFFFFFull;
+
basename_len = strlen(params->name);
nfb_dev = nfb_open(params->path);
@@ -682,6 +708,27 @@ nfb_eth_common_probe(struct rte_device *device,
nc_ifc_map_info_create_ordinary(nfb_dev, ¶ms->map_info);
+ if (params->args != NULL && strlen(params->args) > 0) {
+ kvlist = rte_kvargs_parse(params->args, VALID_KEYS);
+ if (kvlist == NULL) {
+ NFB_LOG(ERR, "Failed to parse device arguments %s", params->args);
+ return -EINVAL;
+ }
+ if (rte_kvargs_count(kvlist, NFB_ARG_PORT)) {
+ port_mask = 0;
+ if (rte_kvargs_process(kvlist, NFB_ARG_PORT, fill_port_mask,
+ (void *)&port_mask))
+ ret = -1;
+ if (ret || port_mask >= (1ull << params->map_info.ifc_cnt))
+ port_mask = 0;
+ }
+ rte_kvargs_free(kvlist);
+ if (port_mask == 0) {
+ NFB_LOG(ERR, "Failed to parse device port argument");
+ return -EINVAL;
+ }
+ }
+
for (i = 0; i < params->map_info.ifc_cnt; i++) {
ifc = ¶ms->map_info.ifc[i];
params->ifc_info = ifc;
@@ -691,6 +738,9 @@ nfb_eth_common_probe(struct rte_device *device,
(ifc->flags & NC_IFC_INFO_FLAG_ACTIVE) == 0)
continue;
+ if ((port_mask & (1ull << i)) == 0)
+ continue;
+
snprintf(params->name + basename_len, sizeof(params->name) - basename_len,
"_eth%d", params->ifc_info->id);
@@ -809,3 +859,6 @@ RTE_PMD_REGISTER_PCI(RTE_NFB_DRIVER_NAME, nfb_eth_driver);
RTE_PMD_REGISTER_PCI_TABLE(RTE_NFB_DRIVER_NAME, nfb_pci_id_table);
RTE_PMD_REGISTER_KMOD_DEP(RTE_NFB_DRIVER_NAME, "* nfb");
RTE_LOG_REGISTER_DEFAULT(nfb_logtype, NOTICE);
+RTE_PMD_REGISTER_PARAM_STRING(RTE_NFB_DRIVER_NAME,
+ NFB_COMMON_ARGS
+ );
diff --git a/drivers/net/nfb/nfb_vdev.c b/drivers/net/nfb/nfb_vdev.c
index 3a8d8b0531..c815ca08f6 100644
--- a/drivers/net/nfb/nfb_vdev.c
+++ b/drivers/net/nfb/nfb_vdev.c
@@ -91,5 +91,6 @@ static struct rte_vdev_driver vdev_nfb_vdev = {
RTE_PMD_REGISTER_VDEV(VDEV_NFB_DRIVER, vdev_nfb_vdev);
RTE_PMD_REGISTER_ALIAS(VDEV_NFB_DRIVER, eth_vdev_nfb);
RTE_PMD_REGISTER_PARAM_STRING(net_vdev_nfb,
- VDEV_NFB_ARG_DEV "=<string>"
+ VDEV_NFB_ARG_DEV "=<string> "
+ NFB_COMMON_ARGS
);
--
2.52.0
^ permalink raw reply related [flat|nested] 131+ messages in thread
* [PATCH v2 5/8] net/nfb: init only MACs associated with device
2026-01-16 16:44 ` [PATCH v2 " spinler
` (3 preceding siblings ...)
2026-01-16 16:44 ` [PATCH v2 4/8] net/nfb: add device argument "port" to limit used ports spinler
@ 2026-01-16 16:44 ` spinler
2026-01-16 16:44 ` [PATCH v2 6/8] net/nfb: add compatible cards to driver PCI ID table spinler
` (3 subsequent siblings)
8 siblings, 0 replies; 131+ messages in thread
From: spinler @ 2026-01-16 16:44 UTC (permalink / raw)
To: dev; +Cc: Martin Spinler
From: Martin Spinler <spinler@cesnet.cz>
Use information obtained from the new manner of creating NFB ethdevs
and initialize only RxMACs / TxMACs associated with actual ethdev.
This enables separated management and configuration of interface ports.
Signed-off-by: Martin Spinler <spinler@cesnet.cz>
---
drivers/net/nfb/nfb.h | 9 +--
drivers/net/nfb/nfb_ethdev.c | 135 +++++++++++++++++++----------------
2 files changed, 74 insertions(+), 70 deletions(-)
diff --git a/drivers/net/nfb/nfb.h b/drivers/net/nfb/nfb.h
index 04d06a75cb..a40ad995f9 100644
--- a/drivers/net/nfb/nfb.h
+++ b/drivers/net/nfb/nfb.h
@@ -36,10 +36,6 @@ extern int nfb_logtype;
#define RTE_ETH_NDP_MAX_RX_QUEUES 32
#define RTE_ETH_NDP_MAX_TX_QUEUES 32
-/* Max index of rx/tx dmas */
-#define RTE_MAX_NC_RXMAC 256
-#define RTE_MAX_NC_TXMAC 256
-
#define RTE_NFB_DRIVER_NAME net_nfb
/* Device arguments */
@@ -52,9 +48,8 @@ extern int nfb_logtype;
struct pmd_internals {
uint16_t max_rxmac;
uint16_t max_txmac;
- struct nc_rxmac *rxmac[RTE_MAX_NC_RXMAC];
- struct nc_txmac *txmac[RTE_MAX_NC_TXMAC];
-
+ struct nc_rxmac **rxmac;
+ struct nc_txmac **txmac;
int *queue_map_rx;
int *queue_map_tx;
diff --git a/drivers/net/nfb/nfb_ethdev.c b/drivers/net/nfb/nfb_ethdev.c
index 0a948e4a2a..d45a88ba92 100644
--- a/drivers/net/nfb/nfb_ethdev.c
+++ b/drivers/net/nfb/nfb_ethdev.c
@@ -44,81 +44,94 @@ static const struct rte_ether_addr eth_addr = {
};
/**
- * Open all RX DMA queues
+ * Open RX MAC components associated with current ifc
*
- * @param dev
- * Pointer to nfb device.
- * @param[out] rxmac
- * Pointer to output array of nc_rxmac
- * @param[out] max_rxmac
- * Pointer to output max index of rxmac
+ * @param priv
+ * Pointer to driver private structure
+ * @param params
+ * Pointer to init parameters structure
*/
-static void
-nfb_nc_rxmac_init(struct nfb_device *nfb,
- struct nc_rxmac *rxmac[RTE_MAX_NC_RXMAC],
- uint16_t *max_rxmac)
+static int
+nfb_nc_rxmac_init(struct pmd_internals *priv, struct nfb_init_params *params)
{
- *max_rxmac = 0;
- while ((rxmac[*max_rxmac] = nc_rxmac_open_index(nfb, *max_rxmac)))
- ++(*max_rxmac);
+ int i, j;
+ struct nc_ifc_info *ifc = params->ifc_info;
+ struct nc_ifc_map_info *mi = ¶ms->map_info;
+
+ priv->rxmac = rte_zmalloc("NFB RxMAC", sizeof(*priv->rxmac) * ifc->eth_cnt, 0);
+ if (priv->rxmac == NULL)
+ return -ENOMEM;
+
+ for (i = 0, j = 0; i < mi->eth_cnt && j < ifc->eth_cnt; i++) {
+ if (mi->eth[i].ifc != ifc->id)
+ continue;
+ priv->rxmac[j] = nc_rxmac_open(priv->nfb, mi->eth[i].node_rxmac);
+ if (priv->rxmac[j])
+ j++;
+ }
+
+ priv->max_rxmac = j;
+ return 0;
}
/**
- * Open all TX DMA queues
+ * Open TX MAC components associated with current ifc
*
- * @param dev
- * Pointer to nfb device.
- * @param[out] txmac
- * Pointer to output array of nc_txmac
- * @param[out] max_rxmac
- * Pointer to output max index of txmac
+ * @param priv
+ * Pointer to driver private structure
+ * @param params
+ * Pointer to init parameters structure
*/
-static void
-nfb_nc_txmac_init(struct nfb_device *nfb,
- struct nc_txmac *txmac[RTE_MAX_NC_TXMAC],
- uint16_t *max_txmac)
+static int
+nfb_nc_txmac_init(struct pmd_internals *priv, struct nfb_init_params *params)
{
- *max_txmac = 0;
- while ((txmac[*max_txmac] = nc_txmac_open_index(nfb, *max_txmac)))
- ++(*max_txmac);
+ int i, j;
+ struct nc_ifc_info *ifc = params->ifc_info;
+ struct nc_ifc_map_info *mi = ¶ms->map_info;
+
+ priv->txmac = rte_zmalloc("NFB TxMAC", sizeof(*priv->txmac) * ifc->eth_cnt, 0);
+ if (priv->txmac == NULL)
+ return -ENOMEM;
+
+ for (i = 0, j = 0; i < mi->eth_cnt && j < ifc->eth_cnt; i++) {
+ if (mi->eth[i].ifc != ifc->id)
+ continue;
+ priv->txmac[j] = nc_txmac_open(priv->nfb, mi->eth[i].node_txmac);
+ if (priv->txmac[j])
+ j++;
+ }
+
+ priv->max_txmac = j;
+ return 0;
}
/**
- * Close all RX DMA queues
- *
- * @param rxmac
- * Pointer to array of nc_rxmac
- * @param max_rxmac
- * Maximum index of rxmac
+ * Close all RX MAC components
+ * @param priv
+ * Pointer to driver private structure
*/
static void
-nfb_nc_rxmac_deinit(struct nc_rxmac *rxmac[RTE_MAX_NC_RXMAC],
- uint16_t max_rxmac)
+nfb_nc_rxmac_deinit(struct pmd_internals *priv)
{
uint16_t i;
- for (i = 0; i < max_rxmac; i++) {
- nc_rxmac_close(rxmac[i]);
- rxmac[i] = NULL;
- }
+ for (i = 0; i < priv->max_rxmac; i++)
+ nc_rxmac_close(priv->rxmac[i]);
+
+ rte_free(priv->rxmac);
}
/**
- * Close all TX DMA queues
- *
- * @param txmac
- * Pointer to array of nc_txmac
- * @param max_txmac
- * Maximum index of txmac
+ * Close all TX MAC components
+ * @param priv
+ * Pointer to driver private structure
*/
static void
-nfb_nc_txmac_deinit(struct nc_txmac *txmac[RTE_MAX_NC_TXMAC],
- uint16_t max_txmac)
+nfb_nc_txmac_deinit(struct pmd_internals *priv)
{
uint16_t i;
- for (i = 0; i < max_txmac; i++) {
- nc_txmac_close(txmac[i]);
- txmac[i] = NULL;
- }
+ for (i = 0; i < priv->max_txmac; i++)
+ nc_txmac_close(priv->txmac[i]);
+ rte_free(priv->txmac);
}
/**
@@ -335,7 +348,7 @@ nfb_eth_link_update(struct rte_eth_dev *dev,
link.link_duplex = RTE_ETH_LINK_FULL_DUPLEX;
link.link_autoneg = RTE_ETH_LINK_SPEED_FIXED;
- if (internals->rxmac[0] != NULL) {
+ if (internals->max_rxmac) {
nc_rxmac_read_status(internals->rxmac[0], &status);
switch (status.speed) {
@@ -554,12 +567,8 @@ nfb_eth_dev_init(struct rte_eth_dev *dev, void *init_data)
goto err_nfb_open;
}
- nfb_nc_rxmac_init(internals->nfb,
- internals->rxmac,
- &internals->max_rxmac);
- nfb_nc_txmac_init(internals->nfb,
- internals->txmac,
- &internals->max_txmac);
+ nfb_nc_rxmac_init(internals, params);
+ nfb_nc_txmac_init(internals, params);
/* Set rx, tx burst functions */
dev->rx_pkt_burst = nfb_eth_ndp_rx;
@@ -626,8 +635,8 @@ nfb_eth_dev_init(struct rte_eth_dev *dev, void *init_data)
err_malloc_mac_addrs:
rte_free(internals->queue_map_rx);
err_alloc_queue_map:
- nfb_nc_rxmac_deinit(internals->rxmac, internals->max_rxmac);
- nfb_nc_txmac_deinit(internals->txmac, internals->max_txmac);
+ nfb_nc_rxmac_deinit(internals);
+ nfb_nc_txmac_deinit(internals);
nfb_close(internals->nfb);
err_nfb_open:
@@ -652,8 +661,8 @@ nfb_eth_dev_uninit(struct rte_eth_dev *dev)
TAILQ_REMOVE(&nfb_eth_dev_list, internals, eth_dev_list);
- nfb_nc_rxmac_deinit(internals->rxmac, internals->max_rxmac);
- nfb_nc_txmac_deinit(internals->txmac, internals->max_txmac);
+ nfb_nc_rxmac_deinit(internals);
+ nfb_nc_txmac_deinit(internals);
nfb_close(internals->nfb);
rte_free(internals->queue_map_rx);
--
2.52.0
^ permalink raw reply related [flat|nested] 131+ messages in thread
* [PATCH v2 6/8] net/nfb: add compatible cards to driver PCI ID table
2026-01-16 16:44 ` [PATCH v2 " spinler
` (4 preceding siblings ...)
2026-01-16 16:44 ` [PATCH v2 5/8] net/nfb: init only MACs associated with device spinler
@ 2026-01-16 16:44 ` spinler
2026-01-16 16:44 ` [PATCH v2 7/8] net/nfb: report firmware version spinler
` (2 subsequent siblings)
8 siblings, 0 replies; 131+ messages in thread
From: spinler @ 2026-01-16 16:44 UTC (permalink / raw)
To: dev; +Cc: Martin Spinler
From: Martin Spinler <spinler@cesnet.cz>
The nfb driver is compatible with:
- NFB-200G2QL card secondary PCI device
- FB2CGHH card
- COMBO-400G1 card
- Common CESNET-NDK cards
Signed-off-by: Martin Spinler <spinler@cesnet.cz>
---
drivers/net/nfb/nfb.h | 5 +++++
drivers/net/nfb/nfb_ethdev.c | 4 ++++
2 files changed, 9 insertions(+)
diff --git a/drivers/net/nfb/nfb.h b/drivers/net/nfb/nfb.h
index a40ad995f9..7b23f95377 100644
--- a/drivers/net/nfb/nfb.h
+++ b/drivers/net/nfb/nfb.h
@@ -24,13 +24,18 @@ extern int nfb_logtype;
/* PCI Vendor ID */
#define PCI_VENDOR_ID_NETCOPE 0x1b26
#define PCI_VENDOR_ID_SILICOM 0x1c2c
+#define PCI_VENDOR_ID_CESNET 0x18ec
/* PCI Device IDs */
#define PCI_DEVICE_ID_NFB_40G2 0xcb80
#define PCI_DEVICE_ID_NFB_100G2 0xc2c1
#define PCI_DEVICE_ID_NFB_200G2QL 0xc250
+#define PCI_DEVICE_ID_NFB_200G2QL_E1 0xc251
#define PCI_DEVICE_ID_FB2CGG3 0x00d0
+#define PCI_DEVICE_ID_FB2CGHH 0x00d2
#define PCI_DEVICE_ID_FB2CGG3D 0xc240
+#define PCI_DEVICE_ID_COMBO400G1 0xc400
+#define PCI_DEVICE_ID_CESNET_NDK_COMMON 0xc000
/* Max index of ndp rx/tx queues */
#define RTE_ETH_NDP_MAX_RX_QUEUES 32
diff --git a/drivers/net/nfb/nfb_ethdev.c b/drivers/net/nfb/nfb_ethdev.c
index d45a88ba92..c31c5cc7e4 100644
--- a/drivers/net/nfb/nfb_ethdev.c
+++ b/drivers/net/nfb/nfb_ethdev.c
@@ -776,8 +776,12 @@ static const struct rte_pci_id nfb_pci_id_table[] = {
{ RTE_PCI_DEVICE(PCI_VENDOR_ID_NETCOPE, PCI_DEVICE_ID_NFB_40G2) },
{ RTE_PCI_DEVICE(PCI_VENDOR_ID_NETCOPE, PCI_DEVICE_ID_NFB_100G2) },
{ RTE_PCI_DEVICE(PCI_VENDOR_ID_NETCOPE, PCI_DEVICE_ID_NFB_200G2QL) },
+ { RTE_PCI_DEVICE(PCI_VENDOR_ID_NETCOPE, PCI_DEVICE_ID_NFB_200G2QL_E1) },
{ RTE_PCI_DEVICE(PCI_VENDOR_ID_SILICOM, PCI_DEVICE_ID_FB2CGG3) },
+ { RTE_PCI_DEVICE(PCI_VENDOR_ID_SILICOM, PCI_DEVICE_ID_FB2CGHH) },
{ RTE_PCI_DEVICE(PCI_VENDOR_ID_SILICOM, PCI_DEVICE_ID_FB2CGG3D) },
+ { RTE_PCI_DEVICE(PCI_VENDOR_ID_CESNET, PCI_DEVICE_ID_COMBO400G1) },
+ { RTE_PCI_DEVICE(PCI_VENDOR_ID_CESNET, PCI_DEVICE_ID_CESNET_NDK_COMMON) },
{ .vendor_id = 0, }
};
--
2.52.0
^ permalink raw reply related [flat|nested] 131+ messages in thread
* [PATCH v2 7/8] net/nfb: report firmware version
2026-01-16 16:44 ` [PATCH v2 " spinler
` (5 preceding siblings ...)
2026-01-16 16:44 ` [PATCH v2 6/8] net/nfb: add compatible cards to driver PCI ID table spinler
@ 2026-01-16 16:44 ` spinler
2026-01-16 16:44 ` [PATCH v2 8/8] doc/nfb: cleanup and update guide spinler
2026-01-20 2:25 ` [PATCH v2 0/8] net/nfb: rework to real multiport Stephen Hemminger
8 siblings, 0 replies; 131+ messages in thread
From: spinler @ 2026-01-16 16:44 UTC (permalink / raw)
To: dev; +Cc: Martin Spinler
From: Martin Spinler <spinler@cesnet.cz>
The firmware is described by project name and project version.
Signed-off-by: Martin Spinler <spinler@cesnet.cz>
---
drivers/net/nfb/nfb_ethdev.c | 26 ++++++++++++++++++++++++++
1 file changed, 26 insertions(+)
diff --git a/drivers/net/nfb/nfb_ethdev.c b/drivers/net/nfb/nfb_ethdev.c
index c31c5cc7e4..8a152fe7ad 100644
--- a/drivers/net/nfb/nfb_ethdev.c
+++ b/drivers/net/nfb/nfb_ethdev.c
@@ -498,6 +498,31 @@ nfb_eth_mac_addr_remove(struct rte_eth_dev *dev, uint32_t index)
nc_rxmac_set_mac(internals->rxmac[i], index, 0, 0);
}
+static int
+nfb_eth_fw_version_get(struct rte_eth_dev *dev, char *fw_version,
+ size_t fw_size)
+{
+ int ret;
+ const char *proj_name, *proj_vers;
+ struct pmd_internals *priv = (struct pmd_internals *)
+ dev->process_private;
+
+ proj_name = nc_info_get_fw_project_name(priv->nfb, NULL);
+ proj_vers = nc_info_get_fw_project_version(priv->nfb, NULL);
+
+ if (proj_name == NULL)
+ proj_name = "";
+ if (proj_vers == NULL)
+ proj_vers = "";
+
+ ret = snprintf(fw_version, fw_size, "%s;%s", proj_name, proj_vers);
+
+ if (ret >= (signed int)fw_size)
+ return strlen(proj_name) + 1 + strlen(proj_vers) + 1;
+
+ return 0;
+}
+
static const struct eth_dev_ops ops = {
.dev_start = nfb_eth_dev_start,
.dev_stop = nfb_eth_dev_stop,
@@ -524,6 +549,7 @@ static const struct eth_dev_ops ops = {
.mac_addr_set = nfb_eth_mac_addr_set,
.mac_addr_add = nfb_eth_mac_addr_add,
.mac_addr_remove = nfb_eth_mac_addr_remove,
+ .fw_version_get = nfb_eth_fw_version_get,
};
/**
--
2.52.0
^ permalink raw reply related [flat|nested] 131+ messages in thread
* [PATCH v2 8/8] doc/nfb: cleanup and update guide
2026-01-16 16:44 ` [PATCH v2 " spinler
` (6 preceding siblings ...)
2026-01-16 16:44 ` [PATCH v2 7/8] net/nfb: report firmware version spinler
@ 2026-01-16 16:44 ` spinler
2026-01-20 2:25 ` [PATCH v2 0/8] net/nfb: rework to real multiport Stephen Hemminger
8 siblings, 0 replies; 131+ messages in thread
From: spinler @ 2026-01-16 16:44 UTC (permalink / raw)
To: dev; +Cc: Martin Spinler
From: Martin Spinler <spinler@cesnet.cz>
changes:
- overall cleanup
- added list of up-to-date cards and links for firmware download
- updated description of the port argument
Signed-off-by: Martin Spinler <spinler@cesnet.cz>
---
doc/guides/nics/features/nfb.ini | 4 +
doc/guides/nics/nfb.rst | 187 ++++++++++++++-----------------
2 files changed, 89 insertions(+), 102 deletions(-)
diff --git a/doc/guides/nics/features/nfb.ini b/doc/guides/nics/features/nfb.ini
index c77b592789..7ac89fc15f 100644
--- a/doc/guides/nics/features/nfb.ini
+++ b/doc/guides/nics/features/nfb.ini
@@ -9,9 +9,13 @@ Link status = Y
Queue start/stop = Y
Promiscuous mode = Y
Allmulticast mode = Y
+Unicast MAC filter = Y
+Timestamp offload = P
Basic stats = Y
Extended stats = Y
Stats per queue = Y
+FW version = Y
+Multiprocess aware = Y
Linux = Y
x86-64 = Y
Usage doc = Y
diff --git a/doc/guides/nics/nfb.rst b/doc/guides/nics/nfb.rst
index a9b4049654..55a635d8d4 100644
--- a/doc/guides/nics/nfb.rst
+++ b/doc/guides/nics/nfb.rst
@@ -2,64 +2,101 @@
Copyright 2019 Cesnet
Copyright 2019 Netcope Technologies
-NFB poll mode driver library
-=================================
+NFB Poll Mode Driver
+====================
+
+The NFB PMD implements support for the FPGA-based
+programmable NICs running `CESNET-NDK <https://www.liberouter.org/ndk/>`_
+based firmware (formerly known as the NetCOPE platform). The CESNET Network Development Kit offers
+wide spectrum of supported cards, for example:
+N6010, FB2CGG3 (Silicom Denmark),
+IA-420F, IA-440i (BittWare),
+AGI-FH400G (ReflexCES),
+and `many more <https://github.com/CESNET/ndk-fpga/tree/devel/cards>`_.
+
+The CESNET-NDK framework is open source and
+can be found on `CESNET-NDK <https://github.com/CESNET/ndk-fpga>`_.
+Ready-to-use demo firmwares can be found on the `DYNANIC page <https://dyna-nic.com/try-now/>`_.
+
+Software compatibility and firmware for
+`historical cards <https://www.liberouter.org/technologies/cards/>`_
+are left unmaintained.
+
+Software prerequisites
+----------------------
-The NFB poll mode driver library implements support for the Netcope
-FPGA Boards (**NFB-40G2, NFB-100G2, NFB-200G2QL**) and Silicom **FB2CGG3** card,
-FPGA-based programmable NICs. The NFB PMD uses interface provided by the libnfb
-library to communicate with these cards over the nfb layer.
+This PMD requires a Linux kernel module, which is responsible for initialization and
+allocation of resources needed for the nfb layer function.
+Communication between PMD and kernel modules is mediated by the libnfb library.
+The kernel module and library are not part of DPDK and must be installed
+separately. Dependencies can be found on GitHub:
+`nfb-framework <https://github.com/CESNET/ndk-sw>`_ as source code,
+or for RPM-based distributions, the prebuilt `nfb-framework` package on
+`Fedora Copr <https://copr.fedorainfracloud.org/coprs/g/CESNET/nfb-framework/>`_.
-More information about the
-`NFB cards <https://www.liberouter.org/technologies/cards/>`_
-and used technology
-(`Network Development Kit <https://www.liberouter.org/ndk/>`_)
-can be found on the `Liberouter website <http://www.liberouter.org/>`_.
+Before starting the DPDK, make sure that the kernel module is loaded (`sudo modprobe nfb`)
+and the card is running the CESNET-NDK based firmware (`nfb-info -l`).
.. note::
- Currently the driver is supported only on x86_64 architectures.
- Only x86_64 versions of the external libraries are provided.
+ Currently, the driver is supported only on x86_64 architectures.
+
+NFB card architecture
+---------------------
+
+Ethernet Ports
+~~~~~~~~~~~~~~
+
+The NFB cards are multi-port multi-queue cards, where (generally) data from any
+Ethernet port may be sent by the firmware to any queue.
-Prerequisites
--------------
+The cards were historically represented in DPDK as a single port.
+Currently each Ethernet channel is represented as one DPDK port.
-This PMD requires kernel modules which are responsible for initialization and
-allocation of resources needed for nfb layer function.
-Communication between PMD and kernel modules is mediated by libnfb library.
-These kernel modules and library are not part of DPDK and must be installed
-separately:
+.. note::
-* **libnfb library**
+ Normally, one port corresponds to one channel, but ports can often be configured in a separate
+ manner. For example one 100G port can be used as 4x25G or 4x10G independent Ethernet channels.
- The library provides API for initialization of nfb transfers, receiving and
- transmitting data segments.
+By default, all ports are initialized and used for the allowed PCI device.
+When this behaviour is limiting (e.g., for multiple instances of DPDK app on different ports
+of the same PCI device), ports can be specified by the `port` item in the `allow` argument:
-* **Kernel modules**
+.. code-block:: console
- * nfb
+ -a 0000:01:00.0,port=0,port=3
- Kernel modules manage initialization of hardware, allocation and
- sharing of resources for user space applications.
+PCIe slots
+~~~~~~~~~~
-Dependencies can be found here:
-`Netcope common <https://github.com/CESNET/ndk-sw>`_.
+Some cards employ more than one PCIe device for better data throughput. This can be achieved by
+slot bifurcation (only a minor improvement) or by an add-on cable connected to another PCIe slot.
+Both improvements can work together, as is, for example, in the case of the AGI-FH400G card.
-Versions of the packages
-~~~~~~~~~~~~~~~~~~~~~~~~
+Because primary and secondary slot(s) can be attached to different NUMA nodes
+(also applies for bifurcation on some HW), the data structures need to be correctly allocated.
+(Device-aware allocation matters also on IOMMU-enabled systems.)
+The firmware already provides DMA queue to PCI device mapping. The DPDK application just needs to
+use all PCI devices, otherwise some queues will not be available; provide all PCI endpoints
+listed in the `nfb-info -v` in the `allow` argument.
-The minimum version of the provided packages:
+.. note::
-* for DPDK from 19.05
+ For cards where the number of Ethernet ports is less than the number of PCI devices
+ (e.g., AGI-FH400G: 1 port, up to 4 PCI devices), the virtual DPDK ports are
+ created to achieve the best NUMA-aware throughput
+ (virtual ports lack a lot of configuration features).
-Configuration
--------------
+Features
+--------
Timestamps
+~~~~~~~~~~
-The PMD supports hardware timestamps of frame receipt on physical network interface. In order to use
-the timestamps, the hardware timestamping unit must be enabled (follow the documentation of the NFB
-products). The standard `RTE_ETH_RX_OFFLOAD_TIMESTAMP` flag can be used for this feature.
+The PMD supports hardware timestamps of frame receipt on physical network interface.
+In order to use the timestamps, the hardware timestamping unit must be enabled
+(follow the documentation of the NFB products).
+The standard `RTE_ETH_RX_OFFLOAD_TIMESTAMP` flag can be used for this feature.
When the timestamps are enabled, a timestamp validity flag is set in the MBUFs
containing received frames and timestamp is inserted into the `rte_mbuf` struct.
@@ -67,72 +104,18 @@ containing received frames and timestamp is inserted into the `rte_mbuf` struct.
The timestamp is an `uint64_t` field and holds the number of nanoseconds
elapsed since 1.1.1970 00:00:00 UTC.
+Simulation
+~~~~~~~~~~
-Using the NFB PMD
-----------------------
-
-Kernel modules have to be loaded before running the DPDK application.
-
-NFB card architecture
----------------------
-
-The NFB cards are multi-port multi-queue cards, where (generally) data from any
-Ethernet port may be sent to any queue.
-They are represented in DPDK as a single port.
-
-NFB-200G2QL card employs an add-on cable which allows to connect it to two
-physical PCI-E slots at the same time (see the diagram below).
-This is done to allow 200 Gbps of traffic to be transferred through the PCI-E
-bus (note that a single PCI-E 3.0 x16 slot provides only 125 Gbps theoretical
-throughput).
-
-Although each slot may be connected to a different CPU and therefore to a different
-NUMA node, the card is represented as a single port in DPDK. To work with data
-from the individual queues on the right NUMA node, connection of NUMA nodes on
-first and last queue (each NUMA node has half of the queues) need to be checked.
-
-Limitations
------------
-
-Driver is usable only on Linux architecture, namely on CentOS.
-
-Since a card is always represented as a single port, but can be connected to two
-NUMA nodes, there is need for manual check where master/slave is connected.
-
-Example of usage
-----------------
-
-Read packets from 0. and 1. receive queue and write them to 0. and 1.
-transmit queue:
+The CESNET-NDK framework offers the possibility of simulating the firmware together with DPDK.
+This allows for easy debugging of a packet flow behaviour with a specific firmware configuration.
+The DPDK NFB driver can be connected to the simulator (Questa/ModelSim/nvc) via a virtual device:
.. code-block:: console
- ./<build_dir>/app/dpdk-testpmd -l 0-3 -n 2 \
- -- --port-topology=chained --rxq=2 --txq=2 --nb-cores=2 -i -a
-
-Example output:
-
-.. code-block:: console
+ dpdk-testpmd
+ --vdev=eth_vdev_nfb,dev=libnfb-ext-grpc.so:grpc+dma_vas:localhost:50051,queue_driver=native
+ --iova-mode=va -- -i
- [...]
- EAL: PCI device 0000:06:00.0 on NUMA socket -1
- EAL: probe driver: 1b26:c1c1 net_nfb
- PMD: Initializing NFB device (0000:06:00.0)
- PMD: Available DMA queues RX: 8 TX: 8
- PMD: NFB device (0000:06:00.0) successfully initialized
- Interactive-mode selected
- Auto-start selected
- Configuring Port 0 (socket 0)
- Port 0: 00:11:17:00:00:00
- Checking link statuses...
- Port 0 Link Up - speed 10000 Mbps - full-duplex
- Done
- Start automatic packet forwarding
- io packet forwarding - CRC stripping disabled - packets/burst=32
- nb forwarding cores=2 - nb forwarding ports=1
- RX queues=2 - RX desc=128 - RX free threshold=0
- RX threshold registers: pthresh=0 hthresh=0 wthresh=0
- TX queues=2 - TX desc=512 - TX free threshold=0
- TX threshold registers: pthresh=0 hthresh=0 wthresh=0
- TX RS bit threshold=0 - TXQ flags=0x0
- testpmd>
+More info about the simulation can be found int the CESNET-NDK `documentation
+<https://cesnet.github.io/ndk-fpga/devel/ndk_apps/minimal/tests/cocotb/readme.html>`_.
--
2.52.0
^ permalink raw reply related [flat|nested] 131+ messages in thread
* Re: [PATCH 1/8] net/nfb: prepare for indirect queue mapping scheme
2026-01-15 15:16 ` [PATCH 1/8] net/nfb: prepare for indirect queue mapping scheme spinler
@ 2026-01-16 17:34 ` Stephen Hemminger
2026-01-20 15:16 ` Martin Spinler
0 siblings, 1 reply; 131+ messages in thread
From: Stephen Hemminger @ 2026-01-16 17:34 UTC (permalink / raw)
To: spinler; +Cc: dev
On Thu, 15 Jan 2026 16:16:49 +0100
spinler@cesnet.cz wrote:
> diff --git a/drivers/net/nfb/nfb.h b/drivers/net/nfb/nfb.h
> index d85c5fba62..ef41d100ac 100644
> --- a/drivers/net/nfb/nfb.h
> +++ b/drivers/net/nfb/nfb.h
> @@ -47,6 +47,10 @@ struct pmd_internals {
> uint16_t max_txmac;
> struct nc_rxmac *rxmac[RTE_MAX_NC_RXMAC];
> struct nc_txmac *txmac[RTE_MAX_NC_TXMAC];
> +
> + int *queue_map_rx;
> + int *queue_map_tx;
Since these are queues, would prefer uint16_t rather than int.
> +
> struct nfb_device *nfb;
> };
>
> diff --git a/drivers/net/nfb/nfb_ethdev.c b/drivers/net/nfb/nfb_ethdev.c
> index 74728f91dd..247f78fe24 100644
> --- a/drivers/net/nfb/nfb_ethdev.c
> +++ b/drivers/net/nfb/nfb_ethdev.c
> @@ -510,6 +510,7 @@ static const struct eth_dev_ops ops = {
> static int
> nfb_eth_dev_init(struct rte_eth_dev *dev)
> {
> + int i;
> int ret;
> uint32_t mac_count;
> struct rte_eth_dev_data *data = dev->data;
> @@ -567,6 +568,23 @@ nfb_eth_dev_init(struct rte_eth_dev *dev)
> dev->rx_pkt_burst = nfb_eth_ndp_rx;
> dev->tx_pkt_burst = nfb_eth_ndp_tx;
>
> + internals->queue_map_rx = rte_malloc("NFB queue map",
> + sizeof(*internals->queue_map_rx) *
> + (priv->max_rx_queues + priv->max_tx_queues), 0);
Use rte_calloc() of arrays.
> + if (internals->queue_map_rx == NULL) {
^ permalink raw reply [flat|nested] 131+ messages in thread
* Re: [PATCH 4/8] net/nfb: add device argument "port" to limit used ports
2026-01-15 15:16 ` [PATCH 4/8] net/nfb: add device argument "port" to limit used ports spinler
@ 2026-01-16 17:36 ` Stephen Hemminger
2026-01-20 15:16 ` Martin Spinler
2026-01-16 17:36 ` Stephen Hemminger
2026-01-16 17:37 ` Stephen Hemminger
2 siblings, 1 reply; 131+ messages in thread
From: Stephen Hemminger @ 2026-01-16 17:36 UTC (permalink / raw)
To: spinler; +Cc: dev
On Thu, 15 Jan 2026 16:16:52 +0100
spinler@cesnet.cz wrote:
>
> +static int fill_port_mask(const char *key __rte_unused, const char *value, void *opaque)
> +{
> + int ret = 0;
> + char *end = NULL;
> + uint64_t *port_mask = opaque;
> + int port;
> +
> + port = strtol(value, &end, 16);
> + if ((value[0] == '\0') || (end == NULL) || (*end != '\0'))
> + ret = -1;
> +
> + if (ret != 0 || port >= 64 || port < 0)
> + return -1;
> +
> + *port_mask |= (1ull << port);
> + return 0;
> +}
Do you really want to allow negative values, why not use strtoul()
Also, if there is a limit of 64 this should be a #define or config constant
^ permalink raw reply [flat|nested] 131+ messages in thread
* Re: [PATCH 4/8] net/nfb: add device argument "port" to limit used ports
2026-01-15 15:16 ` [PATCH 4/8] net/nfb: add device argument "port" to limit used ports spinler
2026-01-16 17:36 ` Stephen Hemminger
@ 2026-01-16 17:36 ` Stephen Hemminger
2026-01-16 17:37 ` Stephen Hemminger
2 siblings, 0 replies; 131+ messages in thread
From: Stephen Hemminger @ 2026-01-16 17:36 UTC (permalink / raw)
To: spinler; +Cc: dev
On Thu, 15 Jan 2026 16:16:52 +0100
spinler@cesnet.cz wrote:
> + if ((port_mask & (1ull << i)) == 0)
> + continue;
> +
Use RTE_BIT32() maybe?
^ permalink raw reply [flat|nested] 131+ messages in thread
* Re: [PATCH 4/8] net/nfb: add device argument "port" to limit used ports
2026-01-15 15:16 ` [PATCH 4/8] net/nfb: add device argument "port" to limit used ports spinler
2026-01-16 17:36 ` Stephen Hemminger
2026-01-16 17:36 ` Stephen Hemminger
@ 2026-01-16 17:37 ` Stephen Hemminger
2 siblings, 0 replies; 131+ messages in thread
From: Stephen Hemminger @ 2026-01-16 17:37 UTC (permalink / raw)
To: spinler; +Cc: dev
On Thu, 15 Jan 2026 16:16:52 +0100
spinler@cesnet.cz wrote:
> + if ((port_mask & (1ull << i)) == 0)
> + continue;
> +
I meant RTE_BIT64() would be good.
^ permalink raw reply [flat|nested] 131+ messages in thread
* Re: [PATCH v2 0/8] net/nfb: rework to real multiport
2026-01-16 16:44 ` [PATCH v2 " spinler
` (7 preceding siblings ...)
2026-01-16 16:44 ` [PATCH v2 8/8] doc/nfb: cleanup and update guide spinler
@ 2026-01-20 2:25 ` Stephen Hemminger
2026-01-20 15:16 ` Martin Spinler
8 siblings, 1 reply; 131+ messages in thread
From: Stephen Hemminger @ 2026-01-20 2:25 UTC (permalink / raw)
To: spinler; +Cc: dev
On Fri, 16 Jan 2026 17:44:28 +0100
spinler@cesnet.cz wrote:
> From: Martin Spinler <spinler@cesnet.cz>
>
> This series implements real multiport for better user experience.
>
> The existing driver creates one ethdev/port for one PCI device.
> As the CESNET-NDK based cards aren't capable to represent each
> Ethernet port by own PCI device, new driver implementation
> processes real port configuration from firmware/card and switches
> from rte_eth_dev_pci_generic_probe to multiple rte_eth_dev_create calls.
You still are using int for queue_max_rx and tx which seems wrong.
In general, this driver code seems to use int like old C code from K&R era.
Avoid using strtol() if you only want unsigned values.
Don't use RTE_ASSERT() for error checking, it gets compiled out usually.
Instead check for snprintf overflows and return EINVAL or ENAMETOOLONG error.
Some comments from AI reviewers.
Series-wide - ⚠️ Missing release notes for significant changes:
Multi-port architecture change
New vdev support
Port selection feature
New hardware support (3 card types)
More comments from AI but I don't trust these.
For example, it thinks DYNANIC is misspelling of DYNAMIC
Errors (Must Fix)
Patch 2/8: __rte_internal not alone on its own line (nfb.h lines 343, 348)
Warnings (Should Fix)
Patch 1/8: Grammar typo "doesn't necessary corresponds" (appears in both nfb_rx.c and nfb_tx.c)
Patch 3/8: Missing trailing newline at end of nfb_vdev.c
Patch 5/8: Return values of nfb_nc_rxmac_init() / nfb_nc_txmac_init() not checked
Patch 8/8: Typos "DYNANIC" and "found int the"
Info (Consider)
Magic number 64 could be a named constant
Minor Doxygen comment formatting
^ permalink raw reply [flat|nested] 131+ messages in thread
* Re: [PATCH v2 0/8] net/nfb: rework to real multiport
2026-01-20 2:25 ` [PATCH v2 0/8] net/nfb: rework to real multiport Stephen Hemminger
@ 2026-01-20 15:16 ` Martin Spinler
0 siblings, 0 replies; 131+ messages in thread
From: Martin Spinler @ 2026-01-20 15:16 UTC (permalink / raw)
To: Stephen Hemminger; +Cc: dev
On Mon, 2026-01-19 at 18:25 -0800, Stephen Hemminger wrote:
>
> You still are using int for queue_max_rx and tx which seems wrong.
The queue_maP_rx problem I'm describing in another email.
> In general, this driver code seems to use int like old C code from K&R era.
> Avoid using strtol() if you only want unsigned values.
>
> Don't use RTE_ASSERT() for error checking, it gets compiled out usually.
> Instead check for snprintf overflows and return EINVAL or ENAMETOOLONG error.
>
>
> Some comments from AI reviewers.
> ....
Ack. Thanks, I will incorporate all your and AI comments, except for
DYNANIC (correct) and __rte_internal (other drivers use the same
layout).
^ permalink raw reply [flat|nested] 131+ messages in thread
* Re: [PATCH 1/8] net/nfb: prepare for indirect queue mapping scheme
2026-01-16 17:34 ` Stephen Hemminger
@ 2026-01-20 15:16 ` Martin Spinler
0 siblings, 0 replies; 131+ messages in thread
From: Martin Spinler @ 2026-01-20 15:16 UTC (permalink / raw)
To: Stephen Hemminger; +Cc: dev
On Fri, 2026-01-16 at 09:34 -0800, Stephen Hemminger wrote:
> On Thu, 15 Jan 2026 16:16:49 +0100
> spinler@cesnet.cz wrote:
>
> > diff --git a/drivers/net/nfb/nfb.h b/drivers/net/nfb/nfb.h
> > index d85c5fba62..ef41d100ac 100644
> > --- a/drivers/net/nfb/nfb.h
> > +++ b/drivers/net/nfb/nfb.h
> > @@ -47,6 +47,10 @@ struct pmd_internals {
> > uint16_t max_txmac;
> > struct nc_rxmac *rxmac[RTE_MAX_NC_RXMAC];
> > struct nc_txmac *txmac[RTE_MAX_NC_TXMAC];
> > +
> > + int *queue_map_rx;
> > + int *queue_map_tx;
>
>
> Since these are queues, would prefer uint16_t rather than int.
The int type of the queue_map_rx values here corresponds with the
libnfb API.
However this patch should have update also the types in function
headers (nfb_eth_*x_queue_init; the nfb_eth_*x_queue_setup is fine).
> > + internals->queue_map_rx = rte_malloc("NFB queue map",
> > + sizeof(*internals->queue_map_rx) *
> > + (priv->max_rx_queues + priv->max_tx_queues), 0);
>
> Use rte_calloc() of arrays.
> > + if (internals->queue_map_rx == NULL) {
Ack.
^ permalink raw reply [flat|nested] 131+ messages in thread
* Re: [PATCH 4/8] net/nfb: add device argument "port" to limit used ports
2026-01-16 17:36 ` Stephen Hemminger
@ 2026-01-20 15:16 ` Martin Spinler
0 siblings, 0 replies; 131+ messages in thread
From: Martin Spinler @ 2026-01-20 15:16 UTC (permalink / raw)
To: Stephen Hemminger; +Cc: dev
On Fri, 2026-01-16 at 09:36 -0800, Stephen Hemminger wrote:
> On Thu, 15 Jan 2026 16:16:52 +0100
> spinler@cesnet.cz wrote:
>
> >
> > +static int fill_port_mask(const char *key __rte_unused, const char *value, void *opaque)
> > +{
> > + int ret = 0;
> > + char *end = NULL;
> > + uint64_t *port_mask = opaque;
> > + int port;
> > +
> > + port = strtol(value, &end, 16);
> > + if ((value[0] == '\0') || (end == NULL) || (*end != '\0'))
> > + ret = -1;
> > +
> > + if (ret != 0 || port >= 64 || port < 0)
> > + return -1;
> > +
> > + *port_mask |= (1ull << port);
> > + return 0;
> > +}
>
> Do you really want to allow negative values, why not use strtoul()
> Also, if there is a limit of 64 this should be a #define or config constant
Good point, thanks.
Also will use the RTE_BIT64 for port_mask.
^ permalink raw reply [flat|nested] 131+ messages in thread
* [PATCH v3 0/8] net/nfb: rework to real multiport
2026-01-15 15:16 [PATCH 0/8] net/nfb: rework to real multiport spinler
` (9 preceding siblings ...)
2026-01-16 16:44 ` [PATCH v2 " spinler
@ 2026-01-21 17:03 ` spinler
2026-01-21 17:03 ` [PATCH v3 1/8] net/nfb: prepare for indirect queue mapping scheme spinler
` (8 more replies)
2026-01-22 7:27 ` [PATCH v4 " spinler
` (6 subsequent siblings)
17 siblings, 9 replies; 131+ messages in thread
From: spinler @ 2026-01-21 17:03 UTC (permalink / raw)
To: dev; +Cc: Martin Spinler
From: Martin Spinler <spinler@cesnet.cz>
This series implements real multiport for better user experience.
The existing driver creates one ethdev/port for one PCI device.
As the CESNET-NDK based cards aren't capable to represent each
Ethernet port by own PCI device, new driver implementation
processes real port configuration from firmware/card and switches
from rte_eth_dev_pci_generic_probe to multiple rte_eth_dev_create calls.
---
v3:
* Polished int type usage of QueueID for libnfb API.
* Used rte_calloc instead of rte_malloc.
* Moved queue_map_*x from pmd_internals to pmd_priv.
* Removed RTE_ASSERT for error checking.
* Used RTE_BIT for mask and interger limit constants.
* Added check of return value of nfb_nc_*xmac_init.
* Added release notes entry.
Depends-on: series-37064 ("net/nfb: code cleanup")
Martin Spinler (8):
net/nfb: prepare for indirect queue mapping scheme
net/nfb: create ethdev for every eth port/channel
net/nfb: add vdev as alternative device probe method
net/nfb: add device argument "port" to limit used ports
net/nfb: init only MACs associated with device
net/nfb: add compatible cards to driver PCI ID table
net/nfb: report firmware version
doc/nfb: cleanup and update guide
doc/guides/nics/features/nfb.ini | 4 +
doc/guides/nics/nfb.rst | 187 +++++------
doc/guides/rel_notes/release_26_03.rst | 9 +
drivers/net/nfb/meson.build | 1 +
drivers/net/nfb/nfb.h | 46 ++-
drivers/net/nfb/nfb_ethdev.c | 415 +++++++++++++++++++------
drivers/net/nfb/nfb_rx.c | 30 +-
drivers/net/nfb/nfb_rx.h | 9 +-
drivers/net/nfb/nfb_tx.c | 27 +-
drivers/net/nfb/nfb_tx.h | 7 +-
drivers/net/nfb/nfb_vdev.c | 101 ++++++
11 files changed, 590 insertions(+), 246 deletions(-)
create mode 100644 drivers/net/nfb/nfb_vdev.c
--
2.52.0
^ permalink raw reply [flat|nested] 131+ messages in thread
* [PATCH v3 1/8] net/nfb: prepare for indirect queue mapping scheme
2026-01-21 17:03 ` [PATCH v3 " spinler
@ 2026-01-21 17:03 ` spinler
2026-01-21 17:03 ` [PATCH v3 2/8] net/nfb: create ethdev for every eth port/channel spinler
` (7 subsequent siblings)
8 siblings, 0 replies; 131+ messages in thread
From: spinler @ 2026-01-21 17:03 UTC (permalink / raw)
To: dev; +Cc: Martin Spinler
From: Martin Spinler <spinler@cesnet.cz>
This prepares queue mapping for port-aware driver implementation.
Signed-off-by: Martin Spinler <spinler@cesnet.cz>
---
drivers/net/nfb/nfb.h | 3 +++
drivers/net/nfb/nfb_ethdev.c | 21 +++++++++++++++++++++
drivers/net/nfb/nfb_rx.c | 30 +++++++++++++++---------------
drivers/net/nfb/nfb_rx.h | 9 ++++-----
drivers/net/nfb/nfb_tx.c | 27 +++++++++++++++------------
drivers/net/nfb/nfb_tx.h | 7 +++----
6 files changed, 61 insertions(+), 36 deletions(-)
diff --git a/drivers/net/nfb/nfb.h b/drivers/net/nfb/nfb.h
index 90b04c6151..3821732c37 100644
--- a/drivers/net/nfb/nfb.h
+++ b/drivers/net/nfb/nfb.h
@@ -60,6 +60,9 @@ struct pmd_internals {
struct pmd_priv {
uint16_t max_rx_queues;
uint16_t max_tx_queues;
+
+ int *queue_map_rx;
+ int *queue_map_tx;
};
#endif /* _NFB_H_ */
diff --git a/drivers/net/nfb/nfb_ethdev.c b/drivers/net/nfb/nfb_ethdev.c
index 947ee9e21d..803815138c 100644
--- a/drivers/net/nfb/nfb_ethdev.c
+++ b/drivers/net/nfb/nfb_ethdev.c
@@ -509,6 +509,7 @@ static const struct eth_dev_ops ops = {
static int
nfb_eth_dev_init(struct rte_eth_dev *dev)
{
+ int i;
int ret;
uint32_t mac_count;
struct rte_eth_dev_data *data = dev->data;
@@ -577,6 +578,20 @@ nfb_eth_dev_init(struct rte_eth_dev *dev)
priv->max_rx_queues = max_rx_queues;
priv->max_tx_queues = max_tx_queues;
+ priv->queue_map_rx = rte_calloc("NFB queue map", (max_rx_queues + max_tx_queues),
+ sizeof(*priv->queue_map_rx), 0);
+ if (priv->queue_map_rx == NULL) {
+ ret = -ENOMEM;
+ goto err_alloc_queue_map;
+ }
+ priv->queue_map_tx = priv->queue_map_rx + max_rx_queues;
+
+ /* default queue mapping is 1:1 */
+ for (i = 0; i < max_rx_queues; i++)
+ priv->queue_map_rx[i] = i;
+ for (i = 0; i < max_tx_queues; i++)
+ priv->queue_map_tx[i] = i;
+
/* Allocate space for MAC addresses */
mac_count = nfb_eth_get_max_mac_address_count(dev);
data->mac_addrs = rte_zmalloc(data->name,
@@ -608,6 +623,8 @@ nfb_eth_dev_init(struct rte_eth_dev *dev)
return 0;
err_malloc_mac_addrs:
+ rte_free(priv->queue_map_rx);
+err_alloc_queue_map:
nfb_nc_rxmac_deinit(internals->rxmac, internals->max_rxmac);
nfb_nc_txmac_deinit(internals->txmac, internals->max_txmac);
nfb_close(internals->nfb);
@@ -633,6 +650,10 @@ nfb_eth_dev_uninit(struct rte_eth_dev *dev)
struct rte_pci_device *pci_dev = RTE_ETH_DEV_TO_PCI(dev);
struct rte_pci_addr *pci_addr = &pci_dev->addr;
struct pmd_internals *internals = dev->process_private;
+ struct pmd_priv *priv = dev->data->dev_private;
+
+ if (rte_eal_process_type() == RTE_PROC_PRIMARY)
+ rte_free(priv->queue_map_rx);
nfb_nc_rxmac_deinit(internals->rxmac, internals->max_rxmac);
nfb_nc_txmac_deinit(internals->txmac, internals->max_txmac);
diff --git a/drivers/net/nfb/nfb_rx.c b/drivers/net/nfb/nfb_rx.c
index 413d275853..ff630f32f4 100644
--- a/drivers/net/nfb/nfb_rx.c
+++ b/drivers/net/nfb/nfb_rx.c
@@ -61,12 +61,13 @@ nfb_eth_rx_queue_setup(struct rte_eth_dev *dev,
struct rte_mempool *mb_pool)
{
struct pmd_internals *internals = dev->process_private;
+ struct pmd_priv *priv = dev->data->dev_private;
- struct ndp_rx_queue *rxq;
int ret;
+ int qid;
+ struct ndp_rx_queue *rxq;
- rxq = rte_zmalloc_socket("ndp rx queue",
- sizeof(struct ndp_rx_queue),
+ rxq = rte_zmalloc_socket("ndp rx queue", sizeof(struct ndp_rx_queue),
RTE_CACHE_LINE_SIZE, socket_id);
if (rxq == NULL) {
@@ -77,23 +78,23 @@ nfb_eth_rx_queue_setup(struct rte_eth_dev *dev,
rxq->flags = 0;
- ret = nfb_eth_rx_queue_init(internals->nfb,
- rx_queue_id,
- dev->data->port_id,
- mb_pool,
- rxq);
+ qid = priv->queue_map_rx[rx_queue_id];
- if (ret == 0)
- dev->data->rx_queues[rx_queue_id] = rxq;
- else
- rte_free(rxq);
+ ret = nfb_eth_rx_queue_init(internals->nfb, qid, dev->data->port_id, mb_pool, rxq);
+ if (ret)
+ goto err_queue_init;
+ dev->data->rx_queues[rx_queue_id] = rxq;
+ return 0;
+
+err_queue_init:
+ rte_free(rxq);
return ret;
}
int
nfb_eth_rx_queue_init(struct nfb_device *nfb,
- uint16_t rx_queue_id,
+ int qid,
uint16_t port_id,
struct rte_mempool *mb_pool,
struct ndp_rx_queue *rxq)
@@ -104,12 +105,11 @@ nfb_eth_rx_queue_init(struct nfb_device *nfb,
if (nfb == NULL)
return -EINVAL;
- rxq->queue = ndp_open_rx_queue(nfb, rx_queue_id);
+ rxq->queue = ndp_open_rx_queue(nfb, qid);
if (rxq->queue == NULL)
return -EINVAL;
rxq->nfb = nfb;
- rxq->rx_queue_id = rx_queue_id;
rxq->in_port = port_id;
rxq->mb_pool = mb_pool;
rxq->buf_size = (uint16_t)(mbp_priv->mbuf_data_room_size -
diff --git a/drivers/net/nfb/nfb_rx.h b/drivers/net/nfb/nfb_rx.h
index 67b3b00e2a..831945c577 100644
--- a/drivers/net/nfb/nfb_rx.h
+++ b/drivers/net/nfb/nfb_rx.h
@@ -30,7 +30,6 @@ nfb_timestamp_dynfield(struct rte_mbuf *mbuf)
struct ndp_rx_queue {
struct nfb_device *nfb; /* nfb dev structure */
struct ndp_queue *queue; /* rx queue */
- uint16_t rx_queue_id; /* index */
uint8_t in_port; /* port */
uint8_t flags; /* setup flags */
@@ -47,8 +46,8 @@ struct ndp_rx_queue {
*
* @param nfb
* Pointer to nfb device structure.
- * @param rx_queue_id
- * RX queue index.
+ * @param qid
+ * RX queue ID.
* @param port_id
* Device [external] port identifier.
* @param mb_pool
@@ -60,7 +59,7 @@ struct ndp_rx_queue {
*/
int
nfb_eth_rx_queue_init(struct nfb_device *nfb,
- uint16_t rx_queue_id,
+ int qid,
uint16_t port_id,
struct rte_mempool *mb_pool,
struct ndp_rx_queue *rxq);
@@ -70,7 +69,7 @@ nfb_eth_rx_queue_init(struct nfb_device *nfb,
*
* @param dev
* Pointer to Ethernet device structure.
- * @param idx
+ * @param rx_queue_id
* RX queue index.
* @param desc
* Number of descriptors to configure in queue.
diff --git a/drivers/net/nfb/nfb_tx.c b/drivers/net/nfb/nfb_tx.c
index 1f997ce22f..b5cbedeaf6 100644
--- a/drivers/net/nfb/nfb_tx.c
+++ b/drivers/net/nfb/nfb_tx.c
@@ -54,11 +54,13 @@ nfb_eth_tx_queue_setup(struct rte_eth_dev *dev,
const struct rte_eth_txconf *tx_conf __rte_unused)
{
struct pmd_internals *internals = dev->process_private;
+ struct pmd_priv *priv = dev->data->dev_private;
+
int ret;
+ int qid;
struct ndp_tx_queue *txq;
- txq = rte_zmalloc_socket("ndp tx queue",
- sizeof(struct ndp_tx_queue),
+ txq = rte_zmalloc_socket("ndp tx queue", sizeof(struct ndp_tx_queue),
RTE_CACHE_LINE_SIZE, socket_id);
if (txq == NULL) {
@@ -67,32 +69,33 @@ nfb_eth_tx_queue_setup(struct rte_eth_dev *dev,
return -ENOMEM;
}
- ret = nfb_eth_tx_queue_init(internals->nfb,
- tx_queue_id,
- txq);
+ qid = priv->queue_map_tx[tx_queue_id];
- if (ret == 0)
- dev->data->tx_queues[tx_queue_id] = txq;
- else
- rte_free(txq);
+ ret = nfb_eth_tx_queue_init(internals->nfb, qid, txq);
+ if (ret)
+ goto err_queue_init;
+ dev->data->tx_queues[tx_queue_id] = txq;
+ return 0;
+
+err_queue_init:
+ rte_free(txq);
return ret;
}
int
nfb_eth_tx_queue_init(struct nfb_device *nfb,
- uint16_t tx_queue_id,
+ int qid,
struct ndp_tx_queue *txq)
{
if (nfb == NULL)
return -EINVAL;
- txq->queue = ndp_open_tx_queue(nfb, tx_queue_id);
+ txq->queue = ndp_open_tx_queue(nfb, qid);
if (txq->queue == NULL)
return -EINVAL;
txq->nfb = nfb;
- txq->tx_queue_id = tx_queue_id;
txq->tx_pkts = 0;
txq->tx_bytes = 0;
diff --git a/drivers/net/nfb/nfb_tx.h b/drivers/net/nfb/nfb_tx.h
index f107cf914b..c253af1a86 100644
--- a/drivers/net/nfb/nfb_tx.h
+++ b/drivers/net/nfb/nfb_tx.h
@@ -17,7 +17,6 @@
struct ndp_tx_queue {
struct nfb_device *nfb; /* nfb dev structure */
struct ndp_queue *queue; /* tx queue */
- uint16_t tx_queue_id; /* index */
volatile uint64_t tx_pkts; /* packets transmitted */
volatile uint64_t tx_bytes; /* bytes transmitted */
volatile uint64_t err_pkts; /* erroneous packets */
@@ -54,8 +53,8 @@ nfb_eth_tx_queue_setup(struct rte_eth_dev *dev,
*
* @param nfb
* Pointer to nfb device structure.
- * @param tx_queue_id
- * TX queue index.
+ * @param qid
+ * TX queue ID.
* @param[out] txq
* Pointer to ndp_tx_queue output structure
*
@@ -64,7 +63,7 @@ nfb_eth_tx_queue_setup(struct rte_eth_dev *dev,
*/
int
nfb_eth_tx_queue_init(struct nfb_device *nfb,
- uint16_t tx_queue_id,
+ int qid,
struct ndp_tx_queue *txq);
/**
--
2.52.0
^ permalink raw reply related [flat|nested] 131+ messages in thread
* [PATCH v3 2/8] net/nfb: create ethdev for every eth port/channel
2026-01-21 17:03 ` [PATCH v3 " spinler
2026-01-21 17:03 ` [PATCH v3 1/8] net/nfb: prepare for indirect queue mapping scheme spinler
@ 2026-01-21 17:03 ` spinler
2026-01-21 17:03 ` [PATCH v3 3/8] net/nfb: add vdev as alternative device probe method spinler
` (6 subsequent siblings)
8 siblings, 0 replies; 131+ messages in thread
From: spinler @ 2026-01-21 17:03 UTC (permalink / raw)
To: dev; +Cc: Martin Spinler
From: Martin Spinler <spinler@cesnet.cz>
The existing driver creates one ethdev/port for one PCI device.
As the CESNET-NDK-based cards are mostly unable to represent
each physical port with one PCI device, this atypical behaviour
often limits the user.
The nfb-framework comes with generic port representation API,
which provides information about ports and their mapping to firmware
resource (physical ports, DMA queues, processing pipelines).
This driver design switches from one rte_eth_dev_pci_generic_probe call
to multiple rte_eth_dev_create calls, while parses the firmware mapping
information.
Signed-off-by: Martin Spinler <spinler@cesnet.cz>
---
drivers/net/nfb/nfb.h | 23 +++++
drivers/net/nfb/nfb_ethdev.c | 181 ++++++++++++++++++++++++++---------
2 files changed, 160 insertions(+), 44 deletions(-)
diff --git a/drivers/net/nfb/nfb.h b/drivers/net/nfb/nfb.h
index 3821732c37..d0d46f4a38 100644
--- a/drivers/net/nfb/nfb.h
+++ b/drivers/net/nfb/nfb.h
@@ -11,6 +11,7 @@
#include <nfb/ndp.h>
#include <netcope/rxmac.h>
#include <netcope/txmac.h>
+#include <netcope/info.h>
extern int nfb_logtype;
#define RTE_LOGTYPE_NFB nfb_logtype
@@ -51,6 +52,9 @@ struct pmd_internals {
struct nc_rxmac *rxmac[RTE_MAX_NC_RXMAC];
struct nc_txmac *txmac[RTE_MAX_NC_TXMAC];
struct nfb_device *nfb;
+
+ TAILQ_ENTRY(pmd_internals) eth_dev_list;
+ struct rte_eth_dev *eth_dev;
};
/*
@@ -65,4 +69,23 @@ struct pmd_priv {
int *queue_map_tx;
};
+struct nfb_init_params {
+ const char *path;
+ const char *args;
+ int nfb_id;
+
+ struct nc_ifc_map_info map_info;
+ struct nc_ifc_info *ifc_info;
+
+ char name[RTE_DEV_NAME_MAX_LEN];
+};
+
+
+__rte_internal
+int nfb_eth_common_probe(struct rte_device *device,
+ ethdev_bus_specific_init specific_init, void *specific_device,
+ struct nfb_init_params *params, int ep_index);
+__rte_internal
+int nfb_eth_common_remove(struct rte_device *dev);
+
#endif /* _NFB_H_ */
diff --git a/drivers/net/nfb/nfb_ethdev.c b/drivers/net/nfb/nfb_ethdev.c
index 803815138c..3c009fc1a4 100644
--- a/drivers/net/nfb/nfb_ethdev.c
+++ b/drivers/net/nfb/nfb_ethdev.c
@@ -4,6 +4,10 @@
* All rights reserved.
*/
+#include <sys/queue.h>
+#include <eal_export.h>
+#include <rte_tailq.h>
+
#include <nfb/nfb.h>
#include <nfb/ndp.h>
#include <netcope/rxmac.h>
@@ -18,6 +22,13 @@
#include "nfb_rxmode.h"
#include "nfb.h"
+/* The TAILQ entries are used for cleanup of allocated resources
+ * in situations, where dev_close is not called.
+ */
+TAILQ_HEAD(pmd_internals_head, pmd_internals);
+static struct pmd_internals_head nfb_eth_dev_list =
+ TAILQ_HEAD_INITIALIZER(nfb_eth_dev_list);
+
static int nfb_eth_dev_uninit(struct rte_eth_dev *dev);
/**
@@ -507,23 +518,19 @@ static const struct eth_dev_ops ops = {
* 0 on success, a negative errno value otherwise.
*/
static int
-nfb_eth_dev_init(struct rte_eth_dev *dev)
+nfb_eth_dev_init(struct rte_eth_dev *dev, void *init_data)
{
int i;
+ int cnt;
int ret;
- uint32_t mac_count;
struct rte_eth_dev_data *data = dev->data;
struct pmd_internals *internals;
+ struct nfb_init_params *params = init_data;
+ struct nc_ifc_info *ifc = params->ifc_info;
+ struct nc_ifc_map_info *mi = ¶ms->map_info;
struct pmd_priv *priv = data->dev_private;
- struct rte_pci_device *pci_dev = RTE_ETH_DEV_TO_PCI(dev);
- struct rte_pci_addr *pci_addr = &pci_dev->addr;
struct rte_ether_addr eth_addr_init;
uint16_t max_rx_queues, max_tx_queues;
- char nfb_dev[PATH_MAX];
-
- NFB_LOG(INFO, "Initializing NFB device (" PCI_PRI_FMT ")",
- pci_addr->domain, pci_addr->bus, pci_addr->devid,
- pci_addr->function);
internals = rte_zmalloc_socket("nfb_internals",
sizeof(struct pmd_internals), RTE_CACHE_LINE_SIZE,
@@ -535,27 +542,17 @@ nfb_eth_dev_init(struct rte_eth_dev *dev)
dev->process_private = internals;
- snprintf(nfb_dev, sizeof(nfb_dev),
- "/dev/nfb/by-pci-slot/" PCI_PRI_FMT,
- pci_addr->domain, pci_addr->bus, pci_addr->devid,
- pci_addr->function);
-
- /*
- * Get number of available DMA RX and TX queues, which is maximum
- * number of queues that can be created and store it in private device
- * data structure.
- */
- internals->nfb = nfb_open(nfb_dev);
+ /* Open device handle */
+ internals->nfb = nfb_open(params->path);
if (internals->nfb == NULL) {
- NFB_LOG(ERR, "nfb_open(): failed to open %s", nfb_dev);
+ NFB_LOG(ERR, "nfb_open(): failed to open %s", params->path);
ret = -EINVAL;
goto err_nfb_open;
}
- max_rx_queues = ndp_get_rx_queue_available_count(internals->nfb);
- max_tx_queues = ndp_get_tx_queue_available_count(internals->nfb);
- NFB_LOG(INFO, "Available NDP queues RX: %u TX: %u",
- max_rx_queues, max_tx_queues);
+ /* Get number of available DMA RX and TX queues */
+ max_rx_queues = ifc->rxq_cnt;
+ max_tx_queues = ifc->txq_cnt;
nfb_nc_rxmac_init(internals->nfb,
internals->rxmac,
@@ -586,16 +583,23 @@ nfb_eth_dev_init(struct rte_eth_dev *dev)
}
priv->queue_map_tx = priv->queue_map_rx + max_rx_queues;
- /* default queue mapping is 1:1 */
- for (i = 0; i < max_rx_queues; i++)
- priv->queue_map_rx[i] = i;
- for (i = 0; i < max_tx_queues; i++)
- priv->queue_map_tx[i] = i;
+ /* Use queue mapping provided by libnfb */
+ cnt = 0;
+ for (i = 0; i < mi->rxq_cnt; i++) {
+ if (mi->rxq[i].ifc == ifc->id)
+ priv->queue_map_rx[cnt++] = mi->rxq[i].id;
+ }
+
+ cnt = 0;
+ for (i = 0; i < mi->txq_cnt; i++) {
+ if (mi->txq[i].ifc == ifc->id)
+ priv->queue_map_tx[cnt++] = mi->txq[i].id;
+ }
/* Allocate space for MAC addresses */
- mac_count = nfb_eth_get_max_mac_address_count(dev);
+ cnt = nfb_eth_get_max_mac_address_count(dev);
data->mac_addrs = rte_zmalloc(data->name,
- sizeof(struct rte_ether_addr) * mac_count, RTE_CACHE_LINE_SIZE);
+ sizeof(struct rte_ether_addr) * cnt, RTE_CACHE_LINE_SIZE);
if (data->mac_addrs == NULL) {
NFB_LOG(ERR, "Could not alloc space for MAC address");
ret = -ENOMEM;
@@ -616,10 +620,6 @@ nfb_eth_dev_init(struct rte_eth_dev *dev)
data->dev_flags |= RTE_ETH_DEV_AUTOFILL_QUEUE_XSTATS;
}
- NFB_LOG(INFO, "NFB device (" PCI_PRI_FMT ") successfully initialized",
- pci_addr->domain, pci_addr->bus, pci_addr->devid,
- pci_addr->function);
-
return 0;
err_malloc_mac_addrs:
@@ -647,23 +647,75 @@ nfb_eth_dev_init(struct rte_eth_dev *dev)
static int
nfb_eth_dev_uninit(struct rte_eth_dev *dev)
{
- struct rte_pci_device *pci_dev = RTE_ETH_DEV_TO_PCI(dev);
- struct rte_pci_addr *pci_addr = &pci_dev->addr;
struct pmd_internals *internals = dev->process_private;
struct pmd_priv *priv = dev->data->dev_private;
if (rte_eal_process_type() == RTE_PROC_PRIMARY)
rte_free(priv->queue_map_rx);
+ TAILQ_REMOVE(&nfb_eth_dev_list, internals, eth_dev_list);
+
nfb_nc_rxmac_deinit(internals->rxmac, internals->max_rxmac);
nfb_nc_txmac_deinit(internals->txmac, internals->max_txmac);
nfb_close(internals->nfb);
rte_free(internals);
- NFB_LOG(INFO, "NFB device (" PCI_PRI_FMT ") successfully uninitialized",
- pci_addr->domain, pci_addr->bus, pci_addr->devid,
- pci_addr->function);
+ return 0;
+}
+
+RTE_EXPORT_INTERNAL_SYMBOL(nfb_eth_common_probe)
+int
+nfb_eth_common_probe(struct rte_device *device,
+ ethdev_bus_specific_init specific_init, void *specific_device,
+ struct nfb_init_params *params, int ep_index)
+{
+ int i;
+ int ret;
+ int basename_len;
+
+ struct nc_ifc_info *ifc;
+ struct nfb_device *nfb_dev;
+ struct rte_eth_dev *eth_dev;
+ struct pmd_internals *p;
+
+ basename_len = strlen(params->name);
+
+ nfb_dev = nfb_open(params->path);
+ if (nfb_dev == NULL) {
+ NFB_LOG(ERR, "nfb_open(): failed to open %s", params->path);
+ return -EINVAL;
+ }
+
+ nc_ifc_map_info_create_ordinary(nfb_dev, ¶ms->map_info);
+
+ for (i = 0; i < params->map_info.ifc_cnt; i++) {
+ ifc = ¶ms->map_info.ifc[i];
+ params->ifc_info = ifc;
+
+ /* Skip interfaces which doesn't belong to this PCI device */
+ if ((ep_index != -1 && ifc->ep != ep_index) ||
+ (ifc->flags & NC_IFC_INFO_FLAG_ACTIVE) == 0)
+ continue;
+
+ snprintf(params->name + basename_len, sizeof(params->name) - basename_len,
+ "_eth%d", params->ifc_info->id);
+
+ ret = rte_eth_dev_create(device, params->name,
+ sizeof(struct pmd_priv),
+ specific_init, specific_device,
+ nfb_eth_dev_init, params);
+
+ if (ret == 0) {
+ eth_dev = rte_eth_dev_get_by_name(params->name);
+ p = eth_dev->process_private;
+ p->eth_dev = eth_dev;
+ TAILQ_INSERT_TAIL(&nfb_eth_dev_list, p, eth_dev_list);
+ }
+ }
+
+ nc_map_info_destroy(¶ms->map_info);
+ nfb_close(nfb_dev);
return 0;
}
@@ -694,8 +746,49 @@ static int
nfb_eth_pci_probe(struct rte_pci_driver *pci_drv __rte_unused,
struct rte_pci_device *pci_dev)
{
- return rte_eth_dev_pci_generic_probe(pci_dev,
- sizeof(struct pmd_priv), nfb_eth_dev_init);
+ int ret;
+ char path[PATH_MAX];
+
+ struct nc_composed_device_info comp_dev_info;
+ struct nfb_init_params params = {0};
+
+ rte_pci_device_name(&pci_dev->addr, params.name, sizeof(params.name));
+
+ /*
+ * NFB device can be composed from multiple PCI devices,
+ * find the base char device ID for the current PCI device
+ */
+ ret = nc_get_composed_device_info_by_pci(NULL, params.name, &comp_dev_info);
+ if (ret) {
+ NFB_LOG(ERR, "Could not find NFB device for %s", params.name);
+ return -ENODEV;
+ }
+
+ ret = snprintf(path, sizeof(path), NFB_BASE_DEV_PATH "%d", comp_dev_info.nfb_id);
+ if (ret < 0 || ret >= (signed int)sizeof(path))
+ return -EINVAL;
+
+ params.args = pci_dev->device.devargs ? pci_dev->device.devargs->args : NULL;
+ params.path = path;
+ params.nfb_id = comp_dev_info.nfb_id;
+
+ return nfb_eth_common_probe(&pci_dev->device, eth_dev_pci_specific_init, pci_dev, ¶ms,
+ comp_dev_info.ep_index);
+}
+
+RTE_EXPORT_INTERNAL_SYMBOL(nfb_eth_common_remove)
+int
+nfb_eth_common_remove(struct rte_device *dev)
+{
+ struct pmd_internals *entry, *temp;
+
+ RTE_TAILQ_FOREACH_SAFE(entry, &nfb_eth_dev_list, eth_dev_list, temp) {
+ if (dev == entry->eth_dev->device) {
+ TAILQ_REMOVE(&nfb_eth_dev_list, entry, eth_dev_list);
+ rte_eth_dev_destroy(entry->eth_dev, nfb_eth_dev_uninit);
+ }
+ }
+ return 0;
}
/**
@@ -712,7 +805,7 @@ nfb_eth_pci_probe(struct rte_pci_driver *pci_drv __rte_unused,
static int
nfb_eth_pci_remove(struct rte_pci_device *pci_dev)
{
- return rte_eth_dev_pci_generic_remove(pci_dev, nfb_eth_dev_uninit);
+ return nfb_eth_common_remove(&pci_dev->device);
}
static struct rte_pci_driver nfb_eth_driver = {
--
2.52.0
^ permalink raw reply related [flat|nested] 131+ messages in thread
* [PATCH v3 3/8] net/nfb: add vdev as alternative device probe method
2026-01-21 17:03 ` [PATCH v3 " spinler
2026-01-21 17:03 ` [PATCH v3 1/8] net/nfb: prepare for indirect queue mapping scheme spinler
2026-01-21 17:03 ` [PATCH v3 2/8] net/nfb: create ethdev for every eth port/channel spinler
@ 2026-01-21 17:03 ` spinler
2026-01-21 17:40 ` Stephen Hemminger
2026-01-21 17:03 ` [PATCH v3 4/8] net/nfb: add device argument "port" to limit used ports spinler
` (5 subsequent siblings)
8 siblings, 1 reply; 131+ messages in thread
From: spinler @ 2026-01-21 17:03 UTC (permalink / raw)
To: dev; +Cc: Martin Spinler
From: Martin Spinler <spinler@cesnet.cz>
VDEV are useful for NFB devices not represented by PCI device,
e.g. virtual or simulated devices.
Signed-off-by: Martin Spinler <spinler@cesnet.cz>
---
drivers/net/nfb/meson.build | 1 +
drivers/net/nfb/nfb_vdev.c | 100 ++++++++++++++++++++++++++++++++++++
2 files changed, 101 insertions(+)
create mode 100644 drivers/net/nfb/nfb_vdev.c
diff --git a/drivers/net/nfb/meson.build b/drivers/net/nfb/meson.build
index d7a255c928..9e458dfb4a 100644
--- a/drivers/net/nfb/meson.build
+++ b/drivers/net/nfb/meson.build
@@ -16,6 +16,7 @@ ext_deps += dep
sources = files(
'nfb_ethdev.c',
+ 'nfb_vdev.c',
'nfb_rx.c',
'nfb_rxmode.c',
'nfb_stats.c',
diff --git a/drivers/net/nfb/nfb_vdev.c b/drivers/net/nfb/nfb_vdev.c
new file mode 100644
index 0000000000..adda148056
--- /dev/null
+++ b/drivers/net/nfb/nfb_vdev.c
@@ -0,0 +1,100 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2026 Cesnet
+ */
+
+#include <rte_kvargs.h>
+#include <ethdev_vdev.h>
+
+#include "nfb.h"
+
+#define VDEV_NFB_DRIVER net_vdev_nfb
+#define VDEV_NFB_ARG_DEV "dev"
+
+static int
+vdev_nfb_vdev_probe(struct rte_vdev_device *dev)
+{
+ unsigned int count;
+ int ret = 0;
+
+ struct nfb_init_params params;
+
+ const char *vdev_name = rte_vdev_device_name(dev);
+ const char *vdev_args = rte_vdev_device_args(dev);
+ char *dev_params, *dev_params_mod;
+ struct rte_kvargs *kvargs;
+
+ kvargs = rte_kvargs_parse(vdev_args, NULL);
+ if (kvargs == NULL) {
+ NFB_LOG(ERR, "Failed to parse device arguments %s", vdev_args);
+ ret = -EINVAL;
+ goto err_parse_args;
+ }
+
+ dev_params = strdup(vdev_args);
+ if (dev_params == NULL) {
+ ret = -ENOMEM;
+ goto err_strdup_params;
+ }
+
+ params.args = dev_params;
+ params.path = nfb_default_dev_path();
+ params.nfb_id = 0;
+
+ dev_params_mod = dev_params;
+
+ dev_params_mod[0] = 0;
+
+ /* Parse parameters for virtual device */
+ for (count = 0; count != kvargs->count; ++count) {
+ const struct rte_kvargs_pair *pair = &kvargs->pairs[count];
+
+ if (!strcmp(pair->key, VDEV_NFB_ARG_DEV)) {
+ params.path = pair->value;
+ } else {
+ /* Clone non-vdev arguments, result is shorter or equal length */
+ ret = sprintf(dev_params_mod, "%s%s=%s",
+ dev_params_mod == dev_params ? "" : ",",
+ pair->key, pair->value);
+ if (ret < 0)
+ goto err_clone_args;
+ dev_params_mod += ret;
+ }
+ }
+
+ strlcpy(params.name, vdev_name, sizeof(params.name));
+
+ ret = nfb_eth_common_probe(&dev->device, NULL, NULL, ¶ms, -1);
+ if (ret)
+ goto err_nfb_common_probe;
+
+ free(dev_params);
+ rte_kvargs_free(kvargs);
+
+ return ret;
+
+err_nfb_common_probe:
+err_clone_args:
+ free(dev_params);
+err_strdup_params:
+ rte_kvargs_free(kvargs);
+err_parse_args:
+ return ret;
+}
+
+static int
+vdev_nfb_vdev_remove(struct rte_vdev_device *dev)
+{
+ return nfb_eth_common_remove(&dev->device);
+}
+
+/** Virtual device descriptor. */
+static struct rte_vdev_driver vdev_nfb_vdev = {
+ .probe = vdev_nfb_vdev_probe,
+ .remove = vdev_nfb_vdev_remove,
+};
+
+RTE_PMD_REGISTER_VDEV(VDEV_NFB_DRIVER, vdev_nfb_vdev);
+RTE_PMD_REGISTER_ALIAS(VDEV_NFB_DRIVER, eth_vdev_nfb);
+RTE_PMD_REGISTER_PARAM_STRING(net_vdev_nfb,
+ VDEV_NFB_ARG_DEV "=<string>"
+ );
--
2.52.0
^ permalink raw reply related [flat|nested] 131+ messages in thread
* [PATCH v3 4/8] net/nfb: add device argument "port" to limit used ports
2026-01-21 17:03 ` [PATCH v3 " spinler
` (2 preceding siblings ...)
2026-01-21 17:03 ` [PATCH v3 3/8] net/nfb: add vdev as alternative device probe method spinler
@ 2026-01-21 17:03 ` spinler
2026-01-21 17:03 ` [PATCH v3 5/8] net/nfb: init only MACs associated with device spinler
` (4 subsequent siblings)
8 siblings, 0 replies; 131+ messages in thread
From: spinler @ 2026-01-21 17:03 UTC (permalink / raw)
To: dev; +Cc: Martin Spinler
From: Martin Spinler <spinler@cesnet.cz>
NFB devices do not use separate PCI device for each port; one PCI device
contains handles for all ports instead. For some application this approach
can be limiting.
The "port" argument can be used to select only desired ports.
It can be used multiple times. When is not used at all, the driver
selects all ports.
Signed-off-by: Martin Spinler <spinler@cesnet.cz>
---
drivers/net/nfb/nfb.h | 7 +++++
drivers/net/nfb/nfb_ethdev.c | 54 +++++++++++++++++++++++++++++++++++-
drivers/net/nfb/nfb_vdev.c | 3 +-
3 files changed, 62 insertions(+), 2 deletions(-)
diff --git a/drivers/net/nfb/nfb.h b/drivers/net/nfb/nfb.h
index d0d46f4a38..605ea8e875 100644
--- a/drivers/net/nfb/nfb.h
+++ b/drivers/net/nfb/nfb.h
@@ -42,6 +42,13 @@ extern int nfb_logtype;
#define RTE_NFB_DRIVER_NAME net_nfb
+/* Device arguments */
+#define NFB_ARG_PORT "port"
+
+#define NFB_COMMON_ARGS \
+ NFB_ARG_PORT "=<number>" \
+ ""
+
/*
* Handles obtained from the libnfb: each process must use own instance.
* Stored inside dev->process_private.
diff --git a/drivers/net/nfb/nfb_ethdev.c b/drivers/net/nfb/nfb_ethdev.c
index 3c009fc1a4..f7c50ea3c4 100644
--- a/drivers/net/nfb/nfb_ethdev.c
+++ b/drivers/net/nfb/nfb_ethdev.c
@@ -22,6 +22,11 @@
#include "nfb_rxmode.h"
#include "nfb.h"
+static const char * const VALID_KEYS[] = {
+ NFB_ARG_PORT,
+ NULL
+};
+
/* The TAILQ entries are used for cleanup of allocated resources
* in situations, where dev_close is not called.
*/
@@ -664,6 +669,23 @@ nfb_eth_dev_uninit(struct rte_eth_dev *dev)
return 0;
}
+static int fill_port_mask(const char *key __rte_unused, const char *value, void *opaque)
+{
+ char *end;
+ uint64_t *port_mask = opaque;
+ unsigned long port;
+
+ if (value == NULL || strlen(value) == 0 || !isdigit(*value))
+ return -1;
+
+ port = strtoul(value, &end, 0);
+ if (*end != '\0' || port >= ULONG_WIDTH)
+ return -1;
+
+ *port_mask |= RTE_BIT64(port);
+ return 0;
+}
+
RTE_EXPORT_INTERNAL_SYMBOL(nfb_eth_common_probe)
int
nfb_eth_common_probe(struct rte_device *device,
@@ -671,7 +693,7 @@ nfb_eth_common_probe(struct rte_device *device,
struct nfb_init_params *params, int ep_index)
{
int i;
- int ret;
+ int ret = 0;
int basename_len;
struct nc_ifc_info *ifc;
@@ -679,6 +701,9 @@ nfb_eth_common_probe(struct rte_device *device,
struct rte_eth_dev *eth_dev;
struct pmd_internals *p;
+ struct rte_kvargs *kvlist;
+ uint64_t port_mask = UINT64_MAX;
+
basename_len = strlen(params->name);
nfb_dev = nfb_open(params->path);
@@ -689,6 +714,27 @@ nfb_eth_common_probe(struct rte_device *device,
nc_ifc_map_info_create_ordinary(nfb_dev, ¶ms->map_info);
+ if (params->args != NULL && strlen(params->args) > 0) {
+ kvlist = rte_kvargs_parse(params->args, VALID_KEYS);
+ if (kvlist == NULL) {
+ NFB_LOG(ERR, "Failed to parse device arguments %s", params->args);
+ return -EINVAL;
+ }
+ if (rte_kvargs_count(kvlist, NFB_ARG_PORT)) {
+ port_mask = 0;
+ if (rte_kvargs_process(kvlist, NFB_ARG_PORT, fill_port_mask,
+ (void *)&port_mask))
+ ret = -1;
+ if (ret || port_mask >= RTE_BIT64(params->map_info.ifc_cnt))
+ port_mask = 0;
+ }
+ rte_kvargs_free(kvlist);
+ if (port_mask == 0) {
+ NFB_LOG(ERR, "Failed to parse device port argument");
+ return -EINVAL;
+ }
+ }
+
for (i = 0; i < params->map_info.ifc_cnt; i++) {
ifc = ¶ms->map_info.ifc[i];
params->ifc_info = ifc;
@@ -698,6 +744,9 @@ nfb_eth_common_probe(struct rte_device *device,
(ifc->flags & NC_IFC_INFO_FLAG_ACTIVE) == 0)
continue;
+ if ((port_mask & RTE_BIT64(i)) == 0)
+ continue;
+
snprintf(params->name + basename_len, sizeof(params->name) - basename_len,
"_eth%d", params->ifc_info->id);
@@ -818,3 +867,6 @@ RTE_PMD_REGISTER_PCI(RTE_NFB_DRIVER_NAME, nfb_eth_driver);
RTE_PMD_REGISTER_PCI_TABLE(RTE_NFB_DRIVER_NAME, nfb_pci_id_table);
RTE_PMD_REGISTER_KMOD_DEP(RTE_NFB_DRIVER_NAME, "* nfb");
RTE_LOG_REGISTER_DEFAULT(nfb_logtype, NOTICE);
+RTE_PMD_REGISTER_PARAM_STRING(RTE_NFB_DRIVER_NAME,
+ NFB_COMMON_ARGS
+ );
diff --git a/drivers/net/nfb/nfb_vdev.c b/drivers/net/nfb/nfb_vdev.c
index adda148056..8947d5c9bc 100644
--- a/drivers/net/nfb/nfb_vdev.c
+++ b/drivers/net/nfb/nfb_vdev.c
@@ -96,5 +96,6 @@ static struct rte_vdev_driver vdev_nfb_vdev = {
RTE_PMD_REGISTER_VDEV(VDEV_NFB_DRIVER, vdev_nfb_vdev);
RTE_PMD_REGISTER_ALIAS(VDEV_NFB_DRIVER, eth_vdev_nfb);
RTE_PMD_REGISTER_PARAM_STRING(net_vdev_nfb,
- VDEV_NFB_ARG_DEV "=<string>"
+ VDEV_NFB_ARG_DEV "=<string> "
+ NFB_COMMON_ARGS
);
--
2.52.0
^ permalink raw reply related [flat|nested] 131+ messages in thread
* [PATCH v3 5/8] net/nfb: init only MACs associated with device
2026-01-21 17:03 ` [PATCH v3 " spinler
` (3 preceding siblings ...)
2026-01-21 17:03 ` [PATCH v3 4/8] net/nfb: add device argument "port" to limit used ports spinler
@ 2026-01-21 17:03 ` spinler
2026-01-21 17:03 ` [PATCH v3 6/8] net/nfb: add compatible cards to driver PCI ID table spinler
` (3 subsequent siblings)
8 siblings, 0 replies; 131+ messages in thread
From: spinler @ 2026-01-21 17:03 UTC (permalink / raw)
To: dev; +Cc: Martin Spinler
From: Martin Spinler <spinler@cesnet.cz>
Use information obtained from the new manner of creating NFB ethdevs
and initialize only RxMACs / TxMACs associated with actual ethdev.
This enables separated management and configuration of interface ports.
Signed-off-by: Martin Spinler <spinler@cesnet.cz>
---
drivers/net/nfb/nfb.h | 8 +-
drivers/net/nfb/nfb_ethdev.c | 141 +++++++++++++++++++----------------
2 files changed, 80 insertions(+), 69 deletions(-)
diff --git a/drivers/net/nfb/nfb.h b/drivers/net/nfb/nfb.h
index 605ea8e875..cfbc50251f 100644
--- a/drivers/net/nfb/nfb.h
+++ b/drivers/net/nfb/nfb.h
@@ -36,10 +36,6 @@ extern int nfb_logtype;
#define RTE_ETH_NDP_MAX_RX_QUEUES 32
#define RTE_ETH_NDP_MAX_TX_QUEUES 32
-/* Max index of rx/tx dmas */
-#define RTE_MAX_NC_RXMAC 256
-#define RTE_MAX_NC_TXMAC 256
-
#define RTE_NFB_DRIVER_NAME net_nfb
/* Device arguments */
@@ -56,8 +52,8 @@ extern int nfb_logtype;
struct pmd_internals {
uint16_t max_rxmac;
uint16_t max_txmac;
- struct nc_rxmac *rxmac[RTE_MAX_NC_RXMAC];
- struct nc_txmac *txmac[RTE_MAX_NC_TXMAC];
+ struct nc_rxmac **rxmac;
+ struct nc_txmac **txmac;
struct nfb_device *nfb;
TAILQ_ENTRY(pmd_internals) eth_dev_list;
diff --git a/drivers/net/nfb/nfb_ethdev.c b/drivers/net/nfb/nfb_ethdev.c
index f7c50ea3c4..59e5a51b58 100644
--- a/drivers/net/nfb/nfb_ethdev.c
+++ b/drivers/net/nfb/nfb_ethdev.c
@@ -44,81 +44,94 @@ static const struct rte_ether_addr eth_addr = {
};
/**
- * Open all RX DMA queues
+ * Open RX MAC components associated with current ifc
*
- * @param dev
- * Pointer to nfb device.
- * @param[out] rxmac
- * Pointer to output array of nc_rxmac
- * @param[out] max_rxmac
- * Pointer to output max index of rxmac
+ * @param priv
+ * Pointer to driver private structure
+ * @param params
+ * Pointer to init parameters structure
*/
-static void
-nfb_nc_rxmac_init(struct nfb_device *nfb,
- struct nc_rxmac *rxmac[RTE_MAX_NC_RXMAC],
- uint16_t *max_rxmac)
+static int
+nfb_nc_rxmac_init(struct pmd_internals *priv, struct nfb_init_params *params)
{
- *max_rxmac = 0;
- while ((rxmac[*max_rxmac] = nc_rxmac_open_index(nfb, *max_rxmac)))
- ++(*max_rxmac);
+ int i, j;
+ struct nc_ifc_info *ifc = params->ifc_info;
+ struct nc_ifc_map_info *mi = ¶ms->map_info;
+
+ priv->rxmac = rte_calloc("NFB RxMAC", ifc->eth_cnt, sizeof(*priv->rxmac), 0);
+ if (ifc->eth_cnt && priv->rxmac == NULL)
+ return -ENOMEM;
+
+ for (i = 0, j = 0; i < mi->eth_cnt && j < ifc->eth_cnt; i++) {
+ if (mi->eth[i].ifc != ifc->id)
+ continue;
+ priv->rxmac[j] = nc_rxmac_open(priv->nfb, mi->eth[i].node_rxmac);
+ if (priv->rxmac[j])
+ j++;
+ }
+
+ priv->max_rxmac = j;
+ return 0;
}
/**
- * Open all TX DMA queues
+ * Open TX MAC components associated with current ifc
*
- * @param dev
- * Pointer to nfb device.
- * @param[out] txmac
- * Pointer to output array of nc_txmac
- * @param[out] max_rxmac
- * Pointer to output max index of txmac
+ * @param priv
+ * Pointer to driver private structure
+ * @param params
+ * Pointer to init parameters structure
*/
-static void
-nfb_nc_txmac_init(struct nfb_device *nfb,
- struct nc_txmac *txmac[RTE_MAX_NC_TXMAC],
- uint16_t *max_txmac)
+static int
+nfb_nc_txmac_init(struct pmd_internals *priv, struct nfb_init_params *params)
{
- *max_txmac = 0;
- while ((txmac[*max_txmac] = nc_txmac_open_index(nfb, *max_txmac)))
- ++(*max_txmac);
+ int i, j;
+ struct nc_ifc_info *ifc = params->ifc_info;
+ struct nc_ifc_map_info *mi = ¶ms->map_info;
+
+ priv->txmac = rte_calloc("NFB TxMAC", ifc->eth_cnt, sizeof(*priv->txmac), 0);
+ if (ifc->eth_cnt && priv->txmac == NULL)
+ return -ENOMEM;
+
+ for (i = 0, j = 0; i < mi->eth_cnt && j < ifc->eth_cnt; i++) {
+ if (mi->eth[i].ifc != ifc->id)
+ continue;
+ priv->txmac[j] = nc_txmac_open(priv->nfb, mi->eth[i].node_txmac);
+ if (priv->txmac[j])
+ j++;
+ }
+
+ priv->max_txmac = j;
+ return 0;
}
/**
- * Close all RX DMA queues
- *
- * @param rxmac
- * Pointer to array of nc_rxmac
- * @param max_rxmac
- * Maximum index of rxmac
+ * Close all RX MAC components
+ * @param priv
+ * Pointer to driver private structure
*/
static void
-nfb_nc_rxmac_deinit(struct nc_rxmac *rxmac[RTE_MAX_NC_RXMAC],
- uint16_t max_rxmac)
+nfb_nc_rxmac_deinit(struct pmd_internals *priv)
{
uint16_t i;
- for (i = 0; i < max_rxmac; i++) {
- nc_rxmac_close(rxmac[i]);
- rxmac[i] = NULL;
- }
+ for (i = 0; i < priv->max_rxmac; i++)
+ nc_rxmac_close(priv->rxmac[i]);
+
+ rte_free(priv->rxmac);
}
/**
- * Close all TX DMA queues
- *
- * @param txmac
- * Pointer to array of nc_txmac
- * @param max_txmac
- * Maximum index of txmac
+ * Close all TX MAC components
+ * @param priv
+ * Pointer to driver private structure
*/
static void
-nfb_nc_txmac_deinit(struct nc_txmac *txmac[RTE_MAX_NC_TXMAC],
- uint16_t max_txmac)
+nfb_nc_txmac_deinit(struct pmd_internals *priv)
{
uint16_t i;
- for (i = 0; i < max_txmac; i++) {
- nc_txmac_close(txmac[i]);
- txmac[i] = NULL;
- }
+ for (i = 0; i < priv->max_txmac; i++)
+ nc_txmac_close(priv->txmac[i]);
+ rte_free(priv->txmac);
}
/**
@@ -335,7 +348,7 @@ nfb_eth_link_update(struct rte_eth_dev *dev,
link.link_duplex = RTE_ETH_LINK_FULL_DUPLEX;
link.link_autoneg = RTE_ETH_LINK_SPEED_FIXED;
- if (internals->rxmac[0] != NULL) {
+ if (internals->max_rxmac) {
nc_rxmac_read_status(internals->rxmac[0], &status);
switch (status.speed) {
@@ -559,12 +572,12 @@ nfb_eth_dev_init(struct rte_eth_dev *dev, void *init_data)
max_rx_queues = ifc->rxq_cnt;
max_tx_queues = ifc->txq_cnt;
- nfb_nc_rxmac_init(internals->nfb,
- internals->rxmac,
- &internals->max_rxmac);
- nfb_nc_txmac_init(internals->nfb,
- internals->txmac,
- &internals->max_txmac);
+ ret = nfb_nc_rxmac_init(internals, params);
+ if (ret)
+ goto err_rxmac_init;
+ ret = nfb_nc_txmac_init(internals, params);
+ if (ret)
+ goto err_txmac_init;
/* Set rx, tx burst functions */
dev->rx_pkt_burst = nfb_eth_ndp_rx;
@@ -630,8 +643,10 @@ nfb_eth_dev_init(struct rte_eth_dev *dev, void *init_data)
err_malloc_mac_addrs:
rte_free(priv->queue_map_rx);
err_alloc_queue_map:
- nfb_nc_rxmac_deinit(internals->rxmac, internals->max_rxmac);
- nfb_nc_txmac_deinit(internals->txmac, internals->max_txmac);
+ nfb_nc_txmac_deinit(internals);
+err_txmac_init:
+ nfb_nc_rxmac_deinit(internals);
+err_rxmac_init:
nfb_close(internals->nfb);
err_nfb_open:
@@ -660,8 +675,8 @@ nfb_eth_dev_uninit(struct rte_eth_dev *dev)
TAILQ_REMOVE(&nfb_eth_dev_list, internals, eth_dev_list);
- nfb_nc_rxmac_deinit(internals->rxmac, internals->max_rxmac);
- nfb_nc_txmac_deinit(internals->txmac, internals->max_txmac);
+ nfb_nc_rxmac_deinit(internals);
+ nfb_nc_txmac_deinit(internals);
nfb_close(internals->nfb);
rte_free(internals);
--
2.52.0
^ permalink raw reply related [flat|nested] 131+ messages in thread
* [PATCH v3 6/8] net/nfb: add compatible cards to driver PCI ID table
2026-01-21 17:03 ` [PATCH v3 " spinler
` (4 preceding siblings ...)
2026-01-21 17:03 ` [PATCH v3 5/8] net/nfb: init only MACs associated with device spinler
@ 2026-01-21 17:03 ` spinler
2026-01-21 17:03 ` [PATCH v3 7/8] net/nfb: report firmware version spinler
` (2 subsequent siblings)
8 siblings, 0 replies; 131+ messages in thread
From: spinler @ 2026-01-21 17:03 UTC (permalink / raw)
To: dev; +Cc: Martin Spinler
From: Martin Spinler <spinler@cesnet.cz>
The nfb driver is compatible with:
- NFB-200G2QL card secondary PCI device
- FB2CGHH card
- COMBO-400G1 card
- Common CESNET-NDK cards
Signed-off-by: Martin Spinler <spinler@cesnet.cz>
---
drivers/net/nfb/nfb.h | 5 +++++
drivers/net/nfb/nfb_ethdev.c | 4 ++++
2 files changed, 9 insertions(+)
diff --git a/drivers/net/nfb/nfb.h b/drivers/net/nfb/nfb.h
index cfbc50251f..0e35e00d2e 100644
--- a/drivers/net/nfb/nfb.h
+++ b/drivers/net/nfb/nfb.h
@@ -24,13 +24,18 @@ extern int nfb_logtype;
/* PCI Vendor ID */
#define PCI_VENDOR_ID_NETCOPE 0x1b26
#define PCI_VENDOR_ID_SILICOM 0x1c2c
+#define PCI_VENDOR_ID_CESNET 0x18ec
/* PCI Device IDs */
#define PCI_DEVICE_ID_NFB_40G2 0xcb80
#define PCI_DEVICE_ID_NFB_100G2 0xc2c1
#define PCI_DEVICE_ID_NFB_200G2QL 0xc250
+#define PCI_DEVICE_ID_NFB_200G2QL_E1 0xc251
#define PCI_DEVICE_ID_FB2CGG3 0x00d0
+#define PCI_DEVICE_ID_FB2CGHH 0x00d2
#define PCI_DEVICE_ID_FB2CGG3D 0xc240
+#define PCI_DEVICE_ID_COMBO400G1 0xc400
+#define PCI_DEVICE_ID_CESNET_NDK_COMMON 0xc000
/* Max index of ndp rx/tx queues */
#define RTE_ETH_NDP_MAX_RX_QUEUES 32
diff --git a/drivers/net/nfb/nfb_ethdev.c b/drivers/net/nfb/nfb_ethdev.c
index 59e5a51b58..e2959c3a04 100644
--- a/drivers/net/nfb/nfb_ethdev.c
+++ b/drivers/net/nfb/nfb_ethdev.c
@@ -788,8 +788,12 @@ static const struct rte_pci_id nfb_pci_id_table[] = {
{ RTE_PCI_DEVICE(PCI_VENDOR_ID_NETCOPE, PCI_DEVICE_ID_NFB_40G2) },
{ RTE_PCI_DEVICE(PCI_VENDOR_ID_NETCOPE, PCI_DEVICE_ID_NFB_100G2) },
{ RTE_PCI_DEVICE(PCI_VENDOR_ID_NETCOPE, PCI_DEVICE_ID_NFB_200G2QL) },
+ { RTE_PCI_DEVICE(PCI_VENDOR_ID_NETCOPE, PCI_DEVICE_ID_NFB_200G2QL_E1) },
{ RTE_PCI_DEVICE(PCI_VENDOR_ID_SILICOM, PCI_DEVICE_ID_FB2CGG3) },
+ { RTE_PCI_DEVICE(PCI_VENDOR_ID_SILICOM, PCI_DEVICE_ID_FB2CGHH) },
{ RTE_PCI_DEVICE(PCI_VENDOR_ID_SILICOM, PCI_DEVICE_ID_FB2CGG3D) },
+ { RTE_PCI_DEVICE(PCI_VENDOR_ID_CESNET, PCI_DEVICE_ID_COMBO400G1) },
+ { RTE_PCI_DEVICE(PCI_VENDOR_ID_CESNET, PCI_DEVICE_ID_CESNET_NDK_COMMON) },
{ .vendor_id = 0, }
};
--
2.52.0
^ permalink raw reply related [flat|nested] 131+ messages in thread
* [PATCH v3 7/8] net/nfb: report firmware version
2026-01-21 17:03 ` [PATCH v3 " spinler
` (5 preceding siblings ...)
2026-01-21 17:03 ` [PATCH v3 6/8] net/nfb: add compatible cards to driver PCI ID table spinler
@ 2026-01-21 17:03 ` spinler
2026-01-21 17:03 ` [PATCH v3 8/8] doc/nfb: cleanup and update guide spinler
2026-01-21 17:42 ` [PATCH v3 0/8] net/nfb: rework to real multiport Stephen Hemminger
8 siblings, 0 replies; 131+ messages in thread
From: spinler @ 2026-01-21 17:03 UTC (permalink / raw)
To: dev; +Cc: Martin Spinler
From: Martin Spinler <spinler@cesnet.cz>
The firmware is described by project name and project version.
Signed-off-by: Martin Spinler <spinler@cesnet.cz>
---
drivers/net/nfb/nfb_ethdev.c | 26 ++++++++++++++++++++++++++
1 file changed, 26 insertions(+)
diff --git a/drivers/net/nfb/nfb_ethdev.c b/drivers/net/nfb/nfb_ethdev.c
index e2959c3a04..85b05c99d0 100644
--- a/drivers/net/nfb/nfb_ethdev.c
+++ b/drivers/net/nfb/nfb_ethdev.c
@@ -498,6 +498,31 @@ nfb_eth_mac_addr_remove(struct rte_eth_dev *dev, uint32_t index)
nc_rxmac_set_mac(internals->rxmac[i], index, 0, 0);
}
+static int
+nfb_eth_fw_version_get(struct rte_eth_dev *dev, char *fw_version,
+ size_t fw_size)
+{
+ int ret;
+ const char *proj_name, *proj_vers;
+ struct pmd_internals *priv = (struct pmd_internals *)
+ dev->process_private;
+
+ proj_name = nc_info_get_fw_project_name(priv->nfb, NULL);
+ proj_vers = nc_info_get_fw_project_version(priv->nfb, NULL);
+
+ if (proj_name == NULL)
+ proj_name = "";
+ if (proj_vers == NULL)
+ proj_vers = "";
+
+ ret = snprintf(fw_version, fw_size, "%s;%s", proj_name, proj_vers);
+
+ if (ret >= (signed int)fw_size)
+ return strlen(proj_name) + 1 + strlen(proj_vers) + 1;
+
+ return 0;
+}
+
static const struct eth_dev_ops ops = {
.dev_start = nfb_eth_dev_start,
.dev_stop = nfb_eth_dev_stop,
@@ -524,6 +549,7 @@ static const struct eth_dev_ops ops = {
.mac_addr_set = nfb_eth_mac_addr_set,
.mac_addr_add = nfb_eth_mac_addr_add,
.mac_addr_remove = nfb_eth_mac_addr_remove,
+ .fw_version_get = nfb_eth_fw_version_get,
};
/**
--
2.52.0
^ permalink raw reply related [flat|nested] 131+ messages in thread
* [PATCH v3 8/8] doc/nfb: cleanup and update guide
2026-01-21 17:03 ` [PATCH v3 " spinler
` (6 preceding siblings ...)
2026-01-21 17:03 ` [PATCH v3 7/8] net/nfb: report firmware version spinler
@ 2026-01-21 17:03 ` spinler
2026-01-21 17:41 ` Stephen Hemminger
2026-01-21 17:42 ` [PATCH v3 0/8] net/nfb: rework to real multiport Stephen Hemminger
8 siblings, 1 reply; 131+ messages in thread
From: spinler @ 2026-01-21 17:03 UTC (permalink / raw)
To: dev; +Cc: Martin Spinler
From: Martin Spinler <spinler@cesnet.cz>
changes:
- overall cleanup
- added list of up-to-date cards and links for firmware download
- updated description of the port argument
Signed-off-by: Martin Spinler <spinler@cesnet.cz>
---
doc/guides/nics/features/nfb.ini | 4 +
doc/guides/nics/nfb.rst | 187 +++++++++++--------------
doc/guides/rel_notes/release_26_03.rst | 9 ++
3 files changed, 98 insertions(+), 102 deletions(-)
diff --git a/doc/guides/nics/features/nfb.ini b/doc/guides/nics/features/nfb.ini
index c77b592789..7ac89fc15f 100644
--- a/doc/guides/nics/features/nfb.ini
+++ b/doc/guides/nics/features/nfb.ini
@@ -9,9 +9,13 @@ Link status = Y
Queue start/stop = Y
Promiscuous mode = Y
Allmulticast mode = Y
+Unicast MAC filter = Y
+Timestamp offload = P
Basic stats = Y
Extended stats = Y
Stats per queue = Y
+FW version = Y
+Multiprocess aware = Y
Linux = Y
x86-64 = Y
Usage doc = Y
diff --git a/doc/guides/nics/nfb.rst b/doc/guides/nics/nfb.rst
index a9b4049654..55a635d8d4 100644
--- a/doc/guides/nics/nfb.rst
+++ b/doc/guides/nics/nfb.rst
@@ -2,64 +2,101 @@
Copyright 2019 Cesnet
Copyright 2019 Netcope Technologies
-NFB poll mode driver library
-=================================
+NFB Poll Mode Driver
+====================
+
+The NFB PMD implements support for the FPGA-based
+programmable NICs running `CESNET-NDK <https://www.liberouter.org/ndk/>`_
+based firmware (formerly known as the NetCOPE platform). The CESNET Network Development Kit offers
+wide spectrum of supported cards, for example:
+N6010, FB2CGG3 (Silicom Denmark),
+IA-420F, IA-440i (BittWare),
+AGI-FH400G (ReflexCES),
+and `many more <https://github.com/CESNET/ndk-fpga/tree/devel/cards>`_.
+
+The CESNET-NDK framework is open source and
+can be found on `CESNET-NDK <https://github.com/CESNET/ndk-fpga>`_.
+Ready-to-use demo firmwares can be found on the `DYNANIC page <https://dyna-nic.com/try-now/>`_.
+
+Software compatibility and firmware for
+`historical cards <https://www.liberouter.org/technologies/cards/>`_
+are left unmaintained.
+
+Software prerequisites
+----------------------
-The NFB poll mode driver library implements support for the Netcope
-FPGA Boards (**NFB-40G2, NFB-100G2, NFB-200G2QL**) and Silicom **FB2CGG3** card,
-FPGA-based programmable NICs. The NFB PMD uses interface provided by the libnfb
-library to communicate with these cards over the nfb layer.
+This PMD requires a Linux kernel module, which is responsible for initialization and
+allocation of resources needed for the nfb layer function.
+Communication between PMD and kernel modules is mediated by the libnfb library.
+The kernel module and library are not part of DPDK and must be installed
+separately. Dependencies can be found on GitHub:
+`nfb-framework <https://github.com/CESNET/ndk-sw>`_ as source code,
+or for RPM-based distributions, the prebuilt `nfb-framework` package on
+`Fedora Copr <https://copr.fedorainfracloud.org/coprs/g/CESNET/nfb-framework/>`_.
-More information about the
-`NFB cards <https://www.liberouter.org/technologies/cards/>`_
-and used technology
-(`Network Development Kit <https://www.liberouter.org/ndk/>`_)
-can be found on the `Liberouter website <http://www.liberouter.org/>`_.
+Before starting the DPDK, make sure that the kernel module is loaded (`sudo modprobe nfb`)
+and the card is running the CESNET-NDK based firmware (`nfb-info -l`).
.. note::
- Currently the driver is supported only on x86_64 architectures.
- Only x86_64 versions of the external libraries are provided.
+ Currently, the driver is supported only on x86_64 architectures.
+
+NFB card architecture
+---------------------
+
+Ethernet Ports
+~~~~~~~~~~~~~~
+
+The NFB cards are multi-port multi-queue cards, where (generally) data from any
+Ethernet port may be sent by the firmware to any queue.
-Prerequisites
--------------
+The cards were historically represented in DPDK as a single port.
+Currently each Ethernet channel is represented as one DPDK port.
-This PMD requires kernel modules which are responsible for initialization and
-allocation of resources needed for nfb layer function.
-Communication between PMD and kernel modules is mediated by libnfb library.
-These kernel modules and library are not part of DPDK and must be installed
-separately:
+.. note::
-* **libnfb library**
+ Normally, one port corresponds to one channel, but ports can often be configured in a separate
+ manner. For example one 100G port can be used as 4x25G or 4x10G independent Ethernet channels.
- The library provides API for initialization of nfb transfers, receiving and
- transmitting data segments.
+By default, all ports are initialized and used for the allowed PCI device.
+When this behaviour is limiting (e.g., for multiple instances of DPDK app on different ports
+of the same PCI device), ports can be specified by the `port` item in the `allow` argument:
-* **Kernel modules**
+.. code-block:: console
- * nfb
+ -a 0000:01:00.0,port=0,port=3
- Kernel modules manage initialization of hardware, allocation and
- sharing of resources for user space applications.
+PCIe slots
+~~~~~~~~~~
-Dependencies can be found here:
-`Netcope common <https://github.com/CESNET/ndk-sw>`_.
+Some cards employ more than one PCIe device for better data throughput. This can be achieved by
+slot bifurcation (only a minor improvement) or by an add-on cable connected to another PCIe slot.
+Both improvements can work together, as is, for example, in the case of the AGI-FH400G card.
-Versions of the packages
-~~~~~~~~~~~~~~~~~~~~~~~~
+Because primary and secondary slot(s) can be attached to different NUMA nodes
+(also applies for bifurcation on some HW), the data structures need to be correctly allocated.
+(Device-aware allocation matters also on IOMMU-enabled systems.)
+The firmware already provides DMA queue to PCI device mapping. The DPDK application just needs to
+use all PCI devices, otherwise some queues will not be available; provide all PCI endpoints
+listed in the `nfb-info -v` in the `allow` argument.
-The minimum version of the provided packages:
+.. note::
-* for DPDK from 19.05
+ For cards where the number of Ethernet ports is less than the number of PCI devices
+ (e.g., AGI-FH400G: 1 port, up to 4 PCI devices), the virtual DPDK ports are
+ created to achieve the best NUMA-aware throughput
+ (virtual ports lack a lot of configuration features).
-Configuration
--------------
+Features
+--------
Timestamps
+~~~~~~~~~~
-The PMD supports hardware timestamps of frame receipt on physical network interface. In order to use
-the timestamps, the hardware timestamping unit must be enabled (follow the documentation of the NFB
-products). The standard `RTE_ETH_RX_OFFLOAD_TIMESTAMP` flag can be used for this feature.
+The PMD supports hardware timestamps of frame receipt on physical network interface.
+In order to use the timestamps, the hardware timestamping unit must be enabled
+(follow the documentation of the NFB products).
+The standard `RTE_ETH_RX_OFFLOAD_TIMESTAMP` flag can be used for this feature.
When the timestamps are enabled, a timestamp validity flag is set in the MBUFs
containing received frames and timestamp is inserted into the `rte_mbuf` struct.
@@ -67,72 +104,18 @@ containing received frames and timestamp is inserted into the `rte_mbuf` struct.
The timestamp is an `uint64_t` field and holds the number of nanoseconds
elapsed since 1.1.1970 00:00:00 UTC.
+Simulation
+~~~~~~~~~~
-Using the NFB PMD
-----------------------
-
-Kernel modules have to be loaded before running the DPDK application.
-
-NFB card architecture
----------------------
-
-The NFB cards are multi-port multi-queue cards, where (generally) data from any
-Ethernet port may be sent to any queue.
-They are represented in DPDK as a single port.
-
-NFB-200G2QL card employs an add-on cable which allows to connect it to two
-physical PCI-E slots at the same time (see the diagram below).
-This is done to allow 200 Gbps of traffic to be transferred through the PCI-E
-bus (note that a single PCI-E 3.0 x16 slot provides only 125 Gbps theoretical
-throughput).
-
-Although each slot may be connected to a different CPU and therefore to a different
-NUMA node, the card is represented as a single port in DPDK. To work with data
-from the individual queues on the right NUMA node, connection of NUMA nodes on
-first and last queue (each NUMA node has half of the queues) need to be checked.
-
-Limitations
------------
-
-Driver is usable only on Linux architecture, namely on CentOS.
-
-Since a card is always represented as a single port, but can be connected to two
-NUMA nodes, there is need for manual check where master/slave is connected.
-
-Example of usage
-----------------
-
-Read packets from 0. and 1. receive queue and write them to 0. and 1.
-transmit queue:
+The CESNET-NDK framework offers the possibility of simulating the firmware together with DPDK.
+This allows for easy debugging of a packet flow behaviour with a specific firmware configuration.
+The DPDK NFB driver can be connected to the simulator (Questa/ModelSim/nvc) via a virtual device:
.. code-block:: console
- ./<build_dir>/app/dpdk-testpmd -l 0-3 -n 2 \
- -- --port-topology=chained --rxq=2 --txq=2 --nb-cores=2 -i -a
-
-Example output:
-
-.. code-block:: console
+ dpdk-testpmd
+ --vdev=eth_vdev_nfb,dev=libnfb-ext-grpc.so:grpc+dma_vas:localhost:50051,queue_driver=native
+ --iova-mode=va -- -i
- [...]
- EAL: PCI device 0000:06:00.0 on NUMA socket -1
- EAL: probe driver: 1b26:c1c1 net_nfb
- PMD: Initializing NFB device (0000:06:00.0)
- PMD: Available DMA queues RX: 8 TX: 8
- PMD: NFB device (0000:06:00.0) successfully initialized
- Interactive-mode selected
- Auto-start selected
- Configuring Port 0 (socket 0)
- Port 0: 00:11:17:00:00:00
- Checking link statuses...
- Port 0 Link Up - speed 10000 Mbps - full-duplex
- Done
- Start automatic packet forwarding
- io packet forwarding - CRC stripping disabled - packets/burst=32
- nb forwarding cores=2 - nb forwarding ports=1
- RX queues=2 - RX desc=128 - RX free threshold=0
- RX threshold registers: pthresh=0 hthresh=0 wthresh=0
- TX queues=2 - TX desc=512 - TX free threshold=0
- TX threshold registers: pthresh=0 hthresh=0 wthresh=0
- TX RS bit threshold=0 - TXQ flags=0x0
- testpmd>
+More info about the simulation can be found int the CESNET-NDK `documentation
+<https://cesnet.github.io/ndk-fpga/devel/ndk_apps/minimal/tests/cocotb/readme.html>`_.
diff --git a/doc/guides/rel_notes/release_26_03.rst b/doc/guides/rel_notes/release_26_03.rst
index 52663580e3..d484ca98d2 100644
--- a/doc/guides/rel_notes/release_26_03.rst
+++ b/doc/guides/rel_notes/release_26_03.rst
@@ -55,6 +55,15 @@ New Features
Also, make sure to start the actual text at the margin.
=======================================================
+* **Updated CESNET NFB ethernet driver.**
+
+ * The timestamp value has been updated to make it usable.
+ * The DPDK port now represents just one Ethernet port instead of all Ethernet ports on the NIC.
+ * All ports are used by default, but a subset can be selected using the ``port`` argument.
+ * Report firmware version.
+ * Common CESNET-NDK-based adapters have been added,
+ including the FB2CGHH (Silicom Denmark) and XpressSX AGI-FH400G (Reflex CES).
+
Removed Items
-------------
--
2.52.0
^ permalink raw reply related [flat|nested] 131+ messages in thread
* Re: [PATCH v3 3/8] net/nfb: add vdev as alternative device probe method
2026-01-21 17:03 ` [PATCH v3 3/8] net/nfb: add vdev as alternative device probe method spinler
@ 2026-01-21 17:40 ` Stephen Hemminger
0 siblings, 0 replies; 131+ messages in thread
From: Stephen Hemminger @ 2026-01-21 17:40 UTC (permalink / raw)
To: spinler; +Cc: dev
On Wed, 21 Jan 2026 18:03:28 +0100
spinler@cesnet.cz wrote:
> + /* Clone non-vdev arguments, result is shorter or equal length */
> + ret = sprintf(dev_params_mod, "%s%s=%s",
> + dev_params_mod == dev_params ? "" : ",",
> + pair->key, pair->value);
AI review raises good point here:
Minor style note: The sprintf() call at line 897-899 uses dev_params_mod both as target and in format string. This is technically undefined behavior in C, though it works in practice. Consider using a separate buffer or snprintf() with proper handling.
Verdict: ⚠️ Minor concern - The sprintf(dev_params_mod, "%s%s=%s", dev_params_mod == dev_params ? "" : ",", ...) pattern is risky. Consider refactoring.
^ permalink raw reply [flat|nested] 131+ messages in thread
* Re: [PATCH v3 8/8] doc/nfb: cleanup and update guide
2026-01-21 17:03 ` [PATCH v3 8/8] doc/nfb: cleanup and update guide spinler
@ 2026-01-21 17:41 ` Stephen Hemminger
0 siblings, 0 replies; 131+ messages in thread
From: Stephen Hemminger @ 2026-01-21 17:41 UTC (permalink / raw)
To: spinler; +Cc: dev
On Wed, 21 Jan 2026 18:03:33 +0100
spinler@cesnet.cz wrote:
> From: Martin Spinler <spinler@cesnet.cz>
>
> changes:
> - overall cleanup
> - added list of up-to-date cards and links for firmware download
> - updated description of the port argument
>
> Signed-off-by: Martin Spinler <spinler@cesnet.cz>
AI review nit.
**Minor issue:** The commit body format is unusual:
```
changes:
- overall cleanup
- added list of up-to-date cards...
```
Convention is to use prose in imperative mood. Suggest rewording to something like:
```
Clean up documentation and update the guide with:
- Current card list with firmware download links
- Updated port argument description
^ permalink raw reply [flat|nested] 131+ messages in thread
* Re: [PATCH v3 0/8] net/nfb: rework to real multiport
2026-01-21 17:03 ` [PATCH v3 " spinler
` (7 preceding siblings ...)
2026-01-21 17:03 ` [PATCH v3 8/8] doc/nfb: cleanup and update guide spinler
@ 2026-01-21 17:42 ` Stephen Hemminger
8 siblings, 0 replies; 131+ messages in thread
From: Stephen Hemminger @ 2026-01-21 17:42 UTC (permalink / raw)
To: spinler; +Cc: dev
On Wed, 21 Jan 2026 18:03:25 +0100
spinler@cesnet.cz wrote:
> From: Martin Spinler <spinler@cesnet.cz>
>
> This series implements real multiport for better user experience.
>
> The existing driver creates one ethdev/port for one PCI device.
> As the CESNET-NDK based cards aren't capable to represent each
> Ethernet port by own PCI device, new driver implementation
> processes real port configuration from firmware/card and switches
> from rte_eth_dev_pci_generic_probe to multiple rte_eth_dev_create calls.
>
> ---
TLDR; Fix the use of sprintf() in patch 3 and resubmit
Wordy AI review output...
## NFB Multiport Patch Series v3 Review
### Series Overview
This is a significant feature series that transforms the NFB driver from single-port-per-PCI to multi-port-per-PCI architecture, with vdev support and new card additions.
---
### Patch 1/8: `net/nfb: prepare for indirect queue mapping scheme`
**Commit Message:**
| Check | Status | Notes |
|-------|--------|-------|
| Subject ≤60 chars | ✅ PASS | 54 characters |
| Lowercase after colon | ✅ PASS | |
| Imperative mood | ✅ PASS | |
| `Signed-off-by:` | ✅ PASS | |
**Code Review:**
- Adds `queue_map_rx` and `queue_map_tx` arrays to `pmd_priv`
- Allocates both in a single `rte_calloc()` call with `queue_map_tx = queue_map_rx + max_rx_queues` - efficient
- Proper error handling with goto cleanup
- Removes unused `rx_queue_id` / `tx_queue_id` from queue structures
- Changes `nfb_eth_rx_queue_init()` and `nfb_eth_tx_queue_init()` signatures from `uint16_t rx_queue_id` to `int qid`
**Verdict: ✅ Ready for merge**
---
### Patch 2/8: `net/nfb: create ethdev for every eth port/channel`
**Commit Message:**
| Check | Status | Notes |
|-------|--------|-------|
| Subject ≤60 chars | ✅ PASS | 52 characters |
| Lowercase after colon | ✅ PASS | |
| `Signed-off-by:` | ✅ PASS | |
**Code Review:**
- Major architectural change: replaces `rte_eth_dev_pci_generic_probe()` with multiple `rte_eth_dev_create()` calls
- Uses TAILQ for tracking eth_dev instances for cleanup
- Adds `nfb_eth_common_probe()` and `nfb_eth_common_remove()` as internal symbols
- Properly uses `nc_ifc_map_info_create_ordinary()` for port/queue mapping info
- `<netcope/info.h>` added for new API
**Issue - `__rte_internal` placement:**
```c
__rte_internal
int nfb_eth_common_probe(struct rte_device *device,
```
Per AGENTS.md: "`__rte_internal` alone on line, only in headers". This is correct ✅
**Verdict: ✅ Ready for merge**
---
### Patch 3/8: `net/nfb: add vdev as alternative device probe method`
**Commit Message:**
| Check | Status | Notes |
|-------|--------|-------|
| Subject ≤60 chars | ✅ PASS | 55 characters |
| Lowercase after colon | ✅ PASS | |
| `Signed-off-by:` | ✅ PASS | |
**Code Review:**
- New file `nfb_vdev.c` with proper SPDX header
- Uses `rte_kvargs` for argument parsing
- Registers both `net_vdev_nfb` and alias `eth_vdev_nfb`
- Proper error handling with goto-style cleanup
- Uses `nfb_default_dev_path()` for default device
**Minor style note:** The `sprintf()` call at line 897-899 uses `dev_params_mod` both as target and in format string. This is technically undefined behavior in C, though it works in practice. Consider using a separate buffer or `snprintf()` with proper handling.
**Verdict: ⚠️ Minor concern** - The `sprintf(dev_params_mod, "%s%s=%s", dev_params_mod == dev_params ? "" : ",", ...)` pattern is risky. Consider refactoring.
---
### Patch 4/8: `net/nfb: add device argument "port" to limit used ports`
**Commit Message:**
| Check | Status | Notes |
|-------|--------|-------|
| Subject ≤60 chars | ✅ PASS | 53 characters |
| Lowercase after colon | ✅ PASS | |
| `Signed-off-by:` | ✅ PASS | |
**Code Review:**
- Adds `port=N` argument support, can be used multiple times
- Uses bitmask for port selection (up to 64 ports via `uint64_t`)
- Proper validation with `fill_port_mask()` callback
- `RTE_PMD_REGISTER_PARAM_STRING` updated for both PCI and vdev drivers
**Code quality:**
```c
if (port >= ULONG_WIDTH)
return -1;
```
Good bounds checking for the bitmask.
**Verdict: ✅ Ready for merge**
---
### Patch 5/8: `net/nfb: init only MACs associated with device`
**Commit Message:**
| Check | Status | Notes |
|-------|--------|-------|
| Subject ≤60 chars | ✅ PASS | 49 characters |
| Lowercase after colon | ✅ PASS | |
| `Signed-off-by:` | ✅ PASS | |
**Code Review:**
- Converts fixed-size arrays `rxmac[RTE_MAX_NC_RXMAC]` to dynamically allocated arrays
- Removes `RTE_MAX_NC_RXMAC` and `RTE_MAX_NC_TXMAC` defines (no longer needed)
- `nfb_nc_rxmac_init()` / `nfb_nc_txmac_init()` now take `priv` and `params` and return error codes
- Proper cleanup in `nfb_nc_rxmac_deinit()` / `nfb_nc_txmac_deinit()` with `rte_free()`
- Error path properly unwinds: `err_txmac_init` → `nfb_nc_rxmac_deinit()` → `err_rxmac_init`
**Verdict: ✅ Ready for merge**
---
### Patch 6/8: `net/nfb: add compatible cards to driver PCI ID table`
**Commit Message:**
| Check | Status | Notes |
|-------|--------|-------|
| Subject ≤60 chars | ✅ PASS | 56 characters |
| Lowercase after colon | ✅ PASS | |
| `Signed-off-by:` | ✅ PASS | |
**Code Review:**
- Adds new PCI IDs:
- `PCI_VENDOR_ID_CESNET` (0x18ec)
- `PCI_DEVICE_ID_NFB_200G2QL_E1` (secondary endpoint)
- `PCI_DEVICE_ID_FB2CGHH`
- `PCI_DEVICE_ID_COMBO400G1`
- `PCI_DEVICE_ID_CESNET_NDK_COMMON`
- Straightforward addition to PCI table
**Verdict: ✅ Ready for merge**
---
### Patch 7/8: `net/nfb: report firmware version`
**Commit Message:**
| Check | Status | Notes |
|-------|--------|-------|
| Subject ≤60 chars | ✅ PASS | 34 characters |
| Lowercase after colon | ✅ PASS | |
| `Signed-off-by:` | ✅ PASS | |
**Code Review:**
- Implements `fw_version_get` dev_op
- Returns "project_name;project_version" format
- Handles NULL returns from `nc_info_get_fw_project_*()` gracefully
- Returns required buffer size when truncated (per API contract)
**Verdict: ✅ Ready for merge**
---
### Patch 8/8: `doc/nfb: cleanup and update guide`
**Commit Message:**
| Check | Status | Notes |
|-------|--------|-------|
| Subject ≤60 chars | ✅ PASS | 33 characters |
| Lowercase after colon | ✅ PASS | |
| `Signed-off-by:` | ✅ PASS | |
| Body starts with lowercase | ⚠️ WARNING | Body starts with "changes:" which is unconventional |
**Code Review:**
- Updates `doc/guides/nics/nfb.rst` comprehensively
- Updates `doc/guides/nics/features/nfb.ini` with new features:
- `Unicast MAC filter = Y`
- `Timestamp offload = P`
- `FW version = Y`
- `Multiprocess aware = Y`
- Adds release notes entry in `doc/guides/rel_notes/release_26_03.rst`
**Documentation quality:** Much improved with clearer card list, links to GitHub and DYNANIC, and better architecture explanation.
**Minor issue:** The commit body format is unusual:
```
changes:
- overall cleanup
- added list of up-to-date cards...
```
Convention is to use prose in imperative mood. Suggest rewording to something like:
```
Clean up documentation and update the guide with:
- Current card list with firmware download links
- Updated port argument description
```
**Verdict: ⚠️ Minor style issue** in commit body, but content is good
---
## Summary
| Patch | Status | Action Required |
|-------|--------|-----------------|
| 1/8 | ✅ Ready | None |
| 2/8 | ✅ Ready | None |
| 3/8 | ⚠️ Warning | Consider fixing `sprintf()` self-referencing pattern |
| 4/8 | ✅ Ready | None |
| 5/8 | ✅ Ready | None |
| 6/8 | ✅ Ready | None |
| 7/8 | ✅ Ready | None |
| 8/8 | ⚠️ Minor | Consider rewording commit body to use imperative prose |
### Overall Assessment
This is a well-structured series that makes a significant architectural improvement to the NFB driver. The code quality is good, error handling is thorough, and the documentation updates are comprehensive. The series properly includes release notes as required.
**Recommendation:** The series can be merged with the minor fixes noted above, or accepted as-is given the issues are relatively minor.
^ permalink raw reply [flat|nested] 131+ messages in thread
* [PATCH v4 0/8] net/nfb: rework to real multiport
2026-01-15 15:16 [PATCH 0/8] net/nfb: rework to real multiport spinler
` (10 preceding siblings ...)
2026-01-21 17:03 ` [PATCH v3 " spinler
@ 2026-01-22 7:27 ` spinler
2026-01-22 7:27 ` [PATCH v4 1/8] net/nfb: prepare for indirect queue mapping scheme spinler
` (10 more replies)
2026-01-23 17:22 ` [PATCH v5 " spinler
` (5 subsequent siblings)
17 siblings, 11 replies; 131+ messages in thread
From: spinler @ 2026-01-22 7:27 UTC (permalink / raw)
To: dev; +Cc: Martin Spinler
From: Martin Spinler <spinler@cesnet.cz>
This series implements real multiport for better user experience.
The existing driver creates one ethdev/port for one PCI device.
As the CESNET-NDK based cards aren't capable to represent each
Ethernet port by own PCI device, new driver implementation
processes real port configuration from firmware/card and switches
from rte_eth_dev_pci_generic_probe to multiple rte_eth_dev_create calls.
---
v4:
* Cleaned usage of sprintf
* Updated commit message for doc cleanup
v3:
* Polished int type usage of QueueID for libnfb API.
* Used rte_calloc instead of rte_malloc.
* Moved queue_map_*x from pmd_internals to pmd_priv.
* Removed RTE_ASSERT for error checking.
* Used RTE_BIT for mask and interger limit constants.
* Added check of return value of nfb_nc_*xmac_init.
* Added release notes entry.
Depends-on: series-37064 ("net/nfb: code cleanup")
Martin Spinler (8):
net/nfb: prepare for indirect queue mapping scheme
net/nfb: create ethdev for every eth port/channel
net/nfb: add vdev as alternative device probe method
net/nfb: add device argument "port" to limit used ports
net/nfb: init only MACs associated with device
net/nfb: add compatible cards to driver PCI ID table
net/nfb: report firmware version
doc/nfb: cleanup and update guide
doc/guides/nics/features/nfb.ini | 4 +
doc/guides/nics/nfb.rst | 187 +++++------
doc/guides/rel_notes/release_26_03.rst | 9 +
drivers/net/nfb/meson.build | 1 +
drivers/net/nfb/nfb.h | 46 ++-
drivers/net/nfb/nfb_ethdev.c | 415 +++++++++++++++++++------
drivers/net/nfb/nfb_rx.c | 30 +-
drivers/net/nfb/nfb_rx.h | 9 +-
drivers/net/nfb/nfb_tx.c | 27 +-
drivers/net/nfb/nfb_tx.h | 7 +-
drivers/net/nfb/nfb_vdev.c | 103 ++++++
11 files changed, 592 insertions(+), 246 deletions(-)
create mode 100644 drivers/net/nfb/nfb_vdev.c
--
2.52.0
^ permalink raw reply [flat|nested] 131+ messages in thread
* [PATCH v4 1/8] net/nfb: prepare for indirect queue mapping scheme
2026-01-22 7:27 ` [PATCH v4 " spinler
@ 2026-01-22 7:27 ` spinler
2026-01-22 7:27 ` [PATCH v4 2/8] net/nfb: create ethdev for every eth port/channel spinler
` (9 subsequent siblings)
10 siblings, 0 replies; 131+ messages in thread
From: spinler @ 2026-01-22 7:27 UTC (permalink / raw)
To: dev; +Cc: Martin Spinler
From: Martin Spinler <spinler@cesnet.cz>
This prepares queue mapping for port-aware driver implementation.
Signed-off-by: Martin Spinler <spinler@cesnet.cz>
---
drivers/net/nfb/nfb.h | 3 +++
drivers/net/nfb/nfb_ethdev.c | 21 +++++++++++++++++++++
drivers/net/nfb/nfb_rx.c | 30 +++++++++++++++---------------
drivers/net/nfb/nfb_rx.h | 9 ++++-----
drivers/net/nfb/nfb_tx.c | 27 +++++++++++++++------------
drivers/net/nfb/nfb_tx.h | 7 +++----
6 files changed, 61 insertions(+), 36 deletions(-)
diff --git a/drivers/net/nfb/nfb.h b/drivers/net/nfb/nfb.h
index 90b04c6151..3821732c37 100644
--- a/drivers/net/nfb/nfb.h
+++ b/drivers/net/nfb/nfb.h
@@ -60,6 +60,9 @@ struct pmd_internals {
struct pmd_priv {
uint16_t max_rx_queues;
uint16_t max_tx_queues;
+
+ int *queue_map_rx;
+ int *queue_map_tx;
};
#endif /* _NFB_H_ */
diff --git a/drivers/net/nfb/nfb_ethdev.c b/drivers/net/nfb/nfb_ethdev.c
index 947ee9e21d..803815138c 100644
--- a/drivers/net/nfb/nfb_ethdev.c
+++ b/drivers/net/nfb/nfb_ethdev.c
@@ -509,6 +509,7 @@ static const struct eth_dev_ops ops = {
static int
nfb_eth_dev_init(struct rte_eth_dev *dev)
{
+ int i;
int ret;
uint32_t mac_count;
struct rte_eth_dev_data *data = dev->data;
@@ -577,6 +578,20 @@ nfb_eth_dev_init(struct rte_eth_dev *dev)
priv->max_rx_queues = max_rx_queues;
priv->max_tx_queues = max_tx_queues;
+ priv->queue_map_rx = rte_calloc("NFB queue map", (max_rx_queues + max_tx_queues),
+ sizeof(*priv->queue_map_rx), 0);
+ if (priv->queue_map_rx == NULL) {
+ ret = -ENOMEM;
+ goto err_alloc_queue_map;
+ }
+ priv->queue_map_tx = priv->queue_map_rx + max_rx_queues;
+
+ /* default queue mapping is 1:1 */
+ for (i = 0; i < max_rx_queues; i++)
+ priv->queue_map_rx[i] = i;
+ for (i = 0; i < max_tx_queues; i++)
+ priv->queue_map_tx[i] = i;
+
/* Allocate space for MAC addresses */
mac_count = nfb_eth_get_max_mac_address_count(dev);
data->mac_addrs = rte_zmalloc(data->name,
@@ -608,6 +623,8 @@ nfb_eth_dev_init(struct rte_eth_dev *dev)
return 0;
err_malloc_mac_addrs:
+ rte_free(priv->queue_map_rx);
+err_alloc_queue_map:
nfb_nc_rxmac_deinit(internals->rxmac, internals->max_rxmac);
nfb_nc_txmac_deinit(internals->txmac, internals->max_txmac);
nfb_close(internals->nfb);
@@ -633,6 +650,10 @@ nfb_eth_dev_uninit(struct rte_eth_dev *dev)
struct rte_pci_device *pci_dev = RTE_ETH_DEV_TO_PCI(dev);
struct rte_pci_addr *pci_addr = &pci_dev->addr;
struct pmd_internals *internals = dev->process_private;
+ struct pmd_priv *priv = dev->data->dev_private;
+
+ if (rte_eal_process_type() == RTE_PROC_PRIMARY)
+ rte_free(priv->queue_map_rx);
nfb_nc_rxmac_deinit(internals->rxmac, internals->max_rxmac);
nfb_nc_txmac_deinit(internals->txmac, internals->max_txmac);
diff --git a/drivers/net/nfb/nfb_rx.c b/drivers/net/nfb/nfb_rx.c
index 413d275853..ff630f32f4 100644
--- a/drivers/net/nfb/nfb_rx.c
+++ b/drivers/net/nfb/nfb_rx.c
@@ -61,12 +61,13 @@ nfb_eth_rx_queue_setup(struct rte_eth_dev *dev,
struct rte_mempool *mb_pool)
{
struct pmd_internals *internals = dev->process_private;
+ struct pmd_priv *priv = dev->data->dev_private;
- struct ndp_rx_queue *rxq;
int ret;
+ int qid;
+ struct ndp_rx_queue *rxq;
- rxq = rte_zmalloc_socket("ndp rx queue",
- sizeof(struct ndp_rx_queue),
+ rxq = rte_zmalloc_socket("ndp rx queue", sizeof(struct ndp_rx_queue),
RTE_CACHE_LINE_SIZE, socket_id);
if (rxq == NULL) {
@@ -77,23 +78,23 @@ nfb_eth_rx_queue_setup(struct rte_eth_dev *dev,
rxq->flags = 0;
- ret = nfb_eth_rx_queue_init(internals->nfb,
- rx_queue_id,
- dev->data->port_id,
- mb_pool,
- rxq);
+ qid = priv->queue_map_rx[rx_queue_id];
- if (ret == 0)
- dev->data->rx_queues[rx_queue_id] = rxq;
- else
- rte_free(rxq);
+ ret = nfb_eth_rx_queue_init(internals->nfb, qid, dev->data->port_id, mb_pool, rxq);
+ if (ret)
+ goto err_queue_init;
+ dev->data->rx_queues[rx_queue_id] = rxq;
+ return 0;
+
+err_queue_init:
+ rte_free(rxq);
return ret;
}
int
nfb_eth_rx_queue_init(struct nfb_device *nfb,
- uint16_t rx_queue_id,
+ int qid,
uint16_t port_id,
struct rte_mempool *mb_pool,
struct ndp_rx_queue *rxq)
@@ -104,12 +105,11 @@ nfb_eth_rx_queue_init(struct nfb_device *nfb,
if (nfb == NULL)
return -EINVAL;
- rxq->queue = ndp_open_rx_queue(nfb, rx_queue_id);
+ rxq->queue = ndp_open_rx_queue(nfb, qid);
if (rxq->queue == NULL)
return -EINVAL;
rxq->nfb = nfb;
- rxq->rx_queue_id = rx_queue_id;
rxq->in_port = port_id;
rxq->mb_pool = mb_pool;
rxq->buf_size = (uint16_t)(mbp_priv->mbuf_data_room_size -
diff --git a/drivers/net/nfb/nfb_rx.h b/drivers/net/nfb/nfb_rx.h
index 67b3b00e2a..831945c577 100644
--- a/drivers/net/nfb/nfb_rx.h
+++ b/drivers/net/nfb/nfb_rx.h
@@ -30,7 +30,6 @@ nfb_timestamp_dynfield(struct rte_mbuf *mbuf)
struct ndp_rx_queue {
struct nfb_device *nfb; /* nfb dev structure */
struct ndp_queue *queue; /* rx queue */
- uint16_t rx_queue_id; /* index */
uint8_t in_port; /* port */
uint8_t flags; /* setup flags */
@@ -47,8 +46,8 @@ struct ndp_rx_queue {
*
* @param nfb
* Pointer to nfb device structure.
- * @param rx_queue_id
- * RX queue index.
+ * @param qid
+ * RX queue ID.
* @param port_id
* Device [external] port identifier.
* @param mb_pool
@@ -60,7 +59,7 @@ struct ndp_rx_queue {
*/
int
nfb_eth_rx_queue_init(struct nfb_device *nfb,
- uint16_t rx_queue_id,
+ int qid,
uint16_t port_id,
struct rte_mempool *mb_pool,
struct ndp_rx_queue *rxq);
@@ -70,7 +69,7 @@ nfb_eth_rx_queue_init(struct nfb_device *nfb,
*
* @param dev
* Pointer to Ethernet device structure.
- * @param idx
+ * @param rx_queue_id
* RX queue index.
* @param desc
* Number of descriptors to configure in queue.
diff --git a/drivers/net/nfb/nfb_tx.c b/drivers/net/nfb/nfb_tx.c
index 1f997ce22f..b5cbedeaf6 100644
--- a/drivers/net/nfb/nfb_tx.c
+++ b/drivers/net/nfb/nfb_tx.c
@@ -54,11 +54,13 @@ nfb_eth_tx_queue_setup(struct rte_eth_dev *dev,
const struct rte_eth_txconf *tx_conf __rte_unused)
{
struct pmd_internals *internals = dev->process_private;
+ struct pmd_priv *priv = dev->data->dev_private;
+
int ret;
+ int qid;
struct ndp_tx_queue *txq;
- txq = rte_zmalloc_socket("ndp tx queue",
- sizeof(struct ndp_tx_queue),
+ txq = rte_zmalloc_socket("ndp tx queue", sizeof(struct ndp_tx_queue),
RTE_CACHE_LINE_SIZE, socket_id);
if (txq == NULL) {
@@ -67,32 +69,33 @@ nfb_eth_tx_queue_setup(struct rte_eth_dev *dev,
return -ENOMEM;
}
- ret = nfb_eth_tx_queue_init(internals->nfb,
- tx_queue_id,
- txq);
+ qid = priv->queue_map_tx[tx_queue_id];
- if (ret == 0)
- dev->data->tx_queues[tx_queue_id] = txq;
- else
- rte_free(txq);
+ ret = nfb_eth_tx_queue_init(internals->nfb, qid, txq);
+ if (ret)
+ goto err_queue_init;
+ dev->data->tx_queues[tx_queue_id] = txq;
+ return 0;
+
+err_queue_init:
+ rte_free(txq);
return ret;
}
int
nfb_eth_tx_queue_init(struct nfb_device *nfb,
- uint16_t tx_queue_id,
+ int qid,
struct ndp_tx_queue *txq)
{
if (nfb == NULL)
return -EINVAL;
- txq->queue = ndp_open_tx_queue(nfb, tx_queue_id);
+ txq->queue = ndp_open_tx_queue(nfb, qid);
if (txq->queue == NULL)
return -EINVAL;
txq->nfb = nfb;
- txq->tx_queue_id = tx_queue_id;
txq->tx_pkts = 0;
txq->tx_bytes = 0;
diff --git a/drivers/net/nfb/nfb_tx.h b/drivers/net/nfb/nfb_tx.h
index f107cf914b..c253af1a86 100644
--- a/drivers/net/nfb/nfb_tx.h
+++ b/drivers/net/nfb/nfb_tx.h
@@ -17,7 +17,6 @@
struct ndp_tx_queue {
struct nfb_device *nfb; /* nfb dev structure */
struct ndp_queue *queue; /* tx queue */
- uint16_t tx_queue_id; /* index */
volatile uint64_t tx_pkts; /* packets transmitted */
volatile uint64_t tx_bytes; /* bytes transmitted */
volatile uint64_t err_pkts; /* erroneous packets */
@@ -54,8 +53,8 @@ nfb_eth_tx_queue_setup(struct rte_eth_dev *dev,
*
* @param nfb
* Pointer to nfb device structure.
- * @param tx_queue_id
- * TX queue index.
+ * @param qid
+ * TX queue ID.
* @param[out] txq
* Pointer to ndp_tx_queue output structure
*
@@ -64,7 +63,7 @@ nfb_eth_tx_queue_setup(struct rte_eth_dev *dev,
*/
int
nfb_eth_tx_queue_init(struct nfb_device *nfb,
- uint16_t tx_queue_id,
+ int qid,
struct ndp_tx_queue *txq);
/**
--
2.52.0
^ permalink raw reply related [flat|nested] 131+ messages in thread
* [PATCH v4 2/8] net/nfb: create ethdev for every eth port/channel
2026-01-22 7:27 ` [PATCH v4 " spinler
2026-01-22 7:27 ` [PATCH v4 1/8] net/nfb: prepare for indirect queue mapping scheme spinler
@ 2026-01-22 7:27 ` spinler
2026-01-22 7:27 ` [PATCH v4 3/8] net/nfb: add vdev as alternative device probe method spinler
` (8 subsequent siblings)
10 siblings, 0 replies; 131+ messages in thread
From: spinler @ 2026-01-22 7:27 UTC (permalink / raw)
To: dev; +Cc: Martin Spinler
From: Martin Spinler <spinler@cesnet.cz>
The existing driver creates one ethdev/port for one PCI device.
As the CESNET-NDK-based cards are mostly unable to represent
each physical port with one PCI device, this atypical behaviour
often limits the user.
The nfb-framework comes with generic port representation API,
which provides information about ports and their mapping to firmware
resource (physical ports, DMA queues, processing pipelines).
This driver design switches from one rte_eth_dev_pci_generic_probe call
to multiple rte_eth_dev_create calls, while parses the firmware mapping
information.
Signed-off-by: Martin Spinler <spinler@cesnet.cz>
---
drivers/net/nfb/nfb.h | 23 +++++
drivers/net/nfb/nfb_ethdev.c | 181 ++++++++++++++++++++++++++---------
2 files changed, 160 insertions(+), 44 deletions(-)
diff --git a/drivers/net/nfb/nfb.h b/drivers/net/nfb/nfb.h
index 3821732c37..d0d46f4a38 100644
--- a/drivers/net/nfb/nfb.h
+++ b/drivers/net/nfb/nfb.h
@@ -11,6 +11,7 @@
#include <nfb/ndp.h>
#include <netcope/rxmac.h>
#include <netcope/txmac.h>
+#include <netcope/info.h>
extern int nfb_logtype;
#define RTE_LOGTYPE_NFB nfb_logtype
@@ -51,6 +52,9 @@ struct pmd_internals {
struct nc_rxmac *rxmac[RTE_MAX_NC_RXMAC];
struct nc_txmac *txmac[RTE_MAX_NC_TXMAC];
struct nfb_device *nfb;
+
+ TAILQ_ENTRY(pmd_internals) eth_dev_list;
+ struct rte_eth_dev *eth_dev;
};
/*
@@ -65,4 +69,23 @@ struct pmd_priv {
int *queue_map_tx;
};
+struct nfb_init_params {
+ const char *path;
+ const char *args;
+ int nfb_id;
+
+ struct nc_ifc_map_info map_info;
+ struct nc_ifc_info *ifc_info;
+
+ char name[RTE_DEV_NAME_MAX_LEN];
+};
+
+
+__rte_internal
+int nfb_eth_common_probe(struct rte_device *device,
+ ethdev_bus_specific_init specific_init, void *specific_device,
+ struct nfb_init_params *params, int ep_index);
+__rte_internal
+int nfb_eth_common_remove(struct rte_device *dev);
+
#endif /* _NFB_H_ */
diff --git a/drivers/net/nfb/nfb_ethdev.c b/drivers/net/nfb/nfb_ethdev.c
index 803815138c..3c009fc1a4 100644
--- a/drivers/net/nfb/nfb_ethdev.c
+++ b/drivers/net/nfb/nfb_ethdev.c
@@ -4,6 +4,10 @@
* All rights reserved.
*/
+#include <sys/queue.h>
+#include <eal_export.h>
+#include <rte_tailq.h>
+
#include <nfb/nfb.h>
#include <nfb/ndp.h>
#include <netcope/rxmac.h>
@@ -18,6 +22,13 @@
#include "nfb_rxmode.h"
#include "nfb.h"
+/* The TAILQ entries are used for cleanup of allocated resources
+ * in situations, where dev_close is not called.
+ */
+TAILQ_HEAD(pmd_internals_head, pmd_internals);
+static struct pmd_internals_head nfb_eth_dev_list =
+ TAILQ_HEAD_INITIALIZER(nfb_eth_dev_list);
+
static int nfb_eth_dev_uninit(struct rte_eth_dev *dev);
/**
@@ -507,23 +518,19 @@ static const struct eth_dev_ops ops = {
* 0 on success, a negative errno value otherwise.
*/
static int
-nfb_eth_dev_init(struct rte_eth_dev *dev)
+nfb_eth_dev_init(struct rte_eth_dev *dev, void *init_data)
{
int i;
+ int cnt;
int ret;
- uint32_t mac_count;
struct rte_eth_dev_data *data = dev->data;
struct pmd_internals *internals;
+ struct nfb_init_params *params = init_data;
+ struct nc_ifc_info *ifc = params->ifc_info;
+ struct nc_ifc_map_info *mi = ¶ms->map_info;
struct pmd_priv *priv = data->dev_private;
- struct rte_pci_device *pci_dev = RTE_ETH_DEV_TO_PCI(dev);
- struct rte_pci_addr *pci_addr = &pci_dev->addr;
struct rte_ether_addr eth_addr_init;
uint16_t max_rx_queues, max_tx_queues;
- char nfb_dev[PATH_MAX];
-
- NFB_LOG(INFO, "Initializing NFB device (" PCI_PRI_FMT ")",
- pci_addr->domain, pci_addr->bus, pci_addr->devid,
- pci_addr->function);
internals = rte_zmalloc_socket("nfb_internals",
sizeof(struct pmd_internals), RTE_CACHE_LINE_SIZE,
@@ -535,27 +542,17 @@ nfb_eth_dev_init(struct rte_eth_dev *dev)
dev->process_private = internals;
- snprintf(nfb_dev, sizeof(nfb_dev),
- "/dev/nfb/by-pci-slot/" PCI_PRI_FMT,
- pci_addr->domain, pci_addr->bus, pci_addr->devid,
- pci_addr->function);
-
- /*
- * Get number of available DMA RX and TX queues, which is maximum
- * number of queues that can be created and store it in private device
- * data structure.
- */
- internals->nfb = nfb_open(nfb_dev);
+ /* Open device handle */
+ internals->nfb = nfb_open(params->path);
if (internals->nfb == NULL) {
- NFB_LOG(ERR, "nfb_open(): failed to open %s", nfb_dev);
+ NFB_LOG(ERR, "nfb_open(): failed to open %s", params->path);
ret = -EINVAL;
goto err_nfb_open;
}
- max_rx_queues = ndp_get_rx_queue_available_count(internals->nfb);
- max_tx_queues = ndp_get_tx_queue_available_count(internals->nfb);
- NFB_LOG(INFO, "Available NDP queues RX: %u TX: %u",
- max_rx_queues, max_tx_queues);
+ /* Get number of available DMA RX and TX queues */
+ max_rx_queues = ifc->rxq_cnt;
+ max_tx_queues = ifc->txq_cnt;
nfb_nc_rxmac_init(internals->nfb,
internals->rxmac,
@@ -586,16 +583,23 @@ nfb_eth_dev_init(struct rte_eth_dev *dev)
}
priv->queue_map_tx = priv->queue_map_rx + max_rx_queues;
- /* default queue mapping is 1:1 */
- for (i = 0; i < max_rx_queues; i++)
- priv->queue_map_rx[i] = i;
- for (i = 0; i < max_tx_queues; i++)
- priv->queue_map_tx[i] = i;
+ /* Use queue mapping provided by libnfb */
+ cnt = 0;
+ for (i = 0; i < mi->rxq_cnt; i++) {
+ if (mi->rxq[i].ifc == ifc->id)
+ priv->queue_map_rx[cnt++] = mi->rxq[i].id;
+ }
+
+ cnt = 0;
+ for (i = 0; i < mi->txq_cnt; i++) {
+ if (mi->txq[i].ifc == ifc->id)
+ priv->queue_map_tx[cnt++] = mi->txq[i].id;
+ }
/* Allocate space for MAC addresses */
- mac_count = nfb_eth_get_max_mac_address_count(dev);
+ cnt = nfb_eth_get_max_mac_address_count(dev);
data->mac_addrs = rte_zmalloc(data->name,
- sizeof(struct rte_ether_addr) * mac_count, RTE_CACHE_LINE_SIZE);
+ sizeof(struct rte_ether_addr) * cnt, RTE_CACHE_LINE_SIZE);
if (data->mac_addrs == NULL) {
NFB_LOG(ERR, "Could not alloc space for MAC address");
ret = -ENOMEM;
@@ -616,10 +620,6 @@ nfb_eth_dev_init(struct rte_eth_dev *dev)
data->dev_flags |= RTE_ETH_DEV_AUTOFILL_QUEUE_XSTATS;
}
- NFB_LOG(INFO, "NFB device (" PCI_PRI_FMT ") successfully initialized",
- pci_addr->domain, pci_addr->bus, pci_addr->devid,
- pci_addr->function);
-
return 0;
err_malloc_mac_addrs:
@@ -647,23 +647,75 @@ nfb_eth_dev_init(struct rte_eth_dev *dev)
static int
nfb_eth_dev_uninit(struct rte_eth_dev *dev)
{
- struct rte_pci_device *pci_dev = RTE_ETH_DEV_TO_PCI(dev);
- struct rte_pci_addr *pci_addr = &pci_dev->addr;
struct pmd_internals *internals = dev->process_private;
struct pmd_priv *priv = dev->data->dev_private;
if (rte_eal_process_type() == RTE_PROC_PRIMARY)
rte_free(priv->queue_map_rx);
+ TAILQ_REMOVE(&nfb_eth_dev_list, internals, eth_dev_list);
+
nfb_nc_rxmac_deinit(internals->rxmac, internals->max_rxmac);
nfb_nc_txmac_deinit(internals->txmac, internals->max_txmac);
nfb_close(internals->nfb);
rte_free(internals);
- NFB_LOG(INFO, "NFB device (" PCI_PRI_FMT ") successfully uninitialized",
- pci_addr->domain, pci_addr->bus, pci_addr->devid,
- pci_addr->function);
+ return 0;
+}
+
+RTE_EXPORT_INTERNAL_SYMBOL(nfb_eth_common_probe)
+int
+nfb_eth_common_probe(struct rte_device *device,
+ ethdev_bus_specific_init specific_init, void *specific_device,
+ struct nfb_init_params *params, int ep_index)
+{
+ int i;
+ int ret;
+ int basename_len;
+
+ struct nc_ifc_info *ifc;
+ struct nfb_device *nfb_dev;
+ struct rte_eth_dev *eth_dev;
+ struct pmd_internals *p;
+
+ basename_len = strlen(params->name);
+
+ nfb_dev = nfb_open(params->path);
+ if (nfb_dev == NULL) {
+ NFB_LOG(ERR, "nfb_open(): failed to open %s", params->path);
+ return -EINVAL;
+ }
+
+ nc_ifc_map_info_create_ordinary(nfb_dev, ¶ms->map_info);
+
+ for (i = 0; i < params->map_info.ifc_cnt; i++) {
+ ifc = ¶ms->map_info.ifc[i];
+ params->ifc_info = ifc;
+
+ /* Skip interfaces which doesn't belong to this PCI device */
+ if ((ep_index != -1 && ifc->ep != ep_index) ||
+ (ifc->flags & NC_IFC_INFO_FLAG_ACTIVE) == 0)
+ continue;
+
+ snprintf(params->name + basename_len, sizeof(params->name) - basename_len,
+ "_eth%d", params->ifc_info->id);
+
+ ret = rte_eth_dev_create(device, params->name,
+ sizeof(struct pmd_priv),
+ specific_init, specific_device,
+ nfb_eth_dev_init, params);
+
+ if (ret == 0) {
+ eth_dev = rte_eth_dev_get_by_name(params->name);
+ p = eth_dev->process_private;
+ p->eth_dev = eth_dev;
+ TAILQ_INSERT_TAIL(&nfb_eth_dev_list, p, eth_dev_list);
+ }
+ }
+
+ nc_map_info_destroy(¶ms->map_info);
+ nfb_close(nfb_dev);
return 0;
}
@@ -694,8 +746,49 @@ static int
nfb_eth_pci_probe(struct rte_pci_driver *pci_drv __rte_unused,
struct rte_pci_device *pci_dev)
{
- return rte_eth_dev_pci_generic_probe(pci_dev,
- sizeof(struct pmd_priv), nfb_eth_dev_init);
+ int ret;
+ char path[PATH_MAX];
+
+ struct nc_composed_device_info comp_dev_info;
+ struct nfb_init_params params = {0};
+
+ rte_pci_device_name(&pci_dev->addr, params.name, sizeof(params.name));
+
+ /*
+ * NFB device can be composed from multiple PCI devices,
+ * find the base char device ID for the current PCI device
+ */
+ ret = nc_get_composed_device_info_by_pci(NULL, params.name, &comp_dev_info);
+ if (ret) {
+ NFB_LOG(ERR, "Could not find NFB device for %s", params.name);
+ return -ENODEV;
+ }
+
+ ret = snprintf(path, sizeof(path), NFB_BASE_DEV_PATH "%d", comp_dev_info.nfb_id);
+ if (ret < 0 || ret >= (signed int)sizeof(path))
+ return -EINVAL;
+
+ params.args = pci_dev->device.devargs ? pci_dev->device.devargs->args : NULL;
+ params.path = path;
+ params.nfb_id = comp_dev_info.nfb_id;
+
+ return nfb_eth_common_probe(&pci_dev->device, eth_dev_pci_specific_init, pci_dev, ¶ms,
+ comp_dev_info.ep_index);
+}
+
+RTE_EXPORT_INTERNAL_SYMBOL(nfb_eth_common_remove)
+int
+nfb_eth_common_remove(struct rte_device *dev)
+{
+ struct pmd_internals *entry, *temp;
+
+ RTE_TAILQ_FOREACH_SAFE(entry, &nfb_eth_dev_list, eth_dev_list, temp) {
+ if (dev == entry->eth_dev->device) {
+ TAILQ_REMOVE(&nfb_eth_dev_list, entry, eth_dev_list);
+ rte_eth_dev_destroy(entry->eth_dev, nfb_eth_dev_uninit);
+ }
+ }
+ return 0;
}
/**
@@ -712,7 +805,7 @@ nfb_eth_pci_probe(struct rte_pci_driver *pci_drv __rte_unused,
static int
nfb_eth_pci_remove(struct rte_pci_device *pci_dev)
{
- return rte_eth_dev_pci_generic_remove(pci_dev, nfb_eth_dev_uninit);
+ return nfb_eth_common_remove(&pci_dev->device);
}
static struct rte_pci_driver nfb_eth_driver = {
--
2.52.0
^ permalink raw reply related [flat|nested] 131+ messages in thread
* [PATCH v4 3/8] net/nfb: add vdev as alternative device probe method
2026-01-22 7:27 ` [PATCH v4 " spinler
2026-01-22 7:27 ` [PATCH v4 1/8] net/nfb: prepare for indirect queue mapping scheme spinler
2026-01-22 7:27 ` [PATCH v4 2/8] net/nfb: create ethdev for every eth port/channel spinler
@ 2026-01-22 7:27 ` spinler
2026-01-22 7:27 ` [PATCH v4 4/8] net/nfb: add device argument "port" to limit used ports spinler
` (7 subsequent siblings)
10 siblings, 0 replies; 131+ messages in thread
From: spinler @ 2026-01-22 7:27 UTC (permalink / raw)
To: dev; +Cc: Martin Spinler
From: Martin Spinler <spinler@cesnet.cz>
VDEV are useful for NFB devices not represented by PCI device,
e.g. virtual or simulated devices.
Signed-off-by: Martin Spinler <spinler@cesnet.cz>
---
drivers/net/nfb/meson.build | 1 +
drivers/net/nfb/nfb_vdev.c | 102 ++++++++++++++++++++++++++++++++++++
2 files changed, 103 insertions(+)
create mode 100644 drivers/net/nfb/nfb_vdev.c
diff --git a/drivers/net/nfb/meson.build b/drivers/net/nfb/meson.build
index d7a255c928..9e458dfb4a 100644
--- a/drivers/net/nfb/meson.build
+++ b/drivers/net/nfb/meson.build
@@ -16,6 +16,7 @@ ext_deps += dep
sources = files(
'nfb_ethdev.c',
+ 'nfb_vdev.c',
'nfb_rx.c',
'nfb_rxmode.c',
'nfb_stats.c',
diff --git a/drivers/net/nfb/nfb_vdev.c b/drivers/net/nfb/nfb_vdev.c
new file mode 100644
index 0000000000..c3629bd395
--- /dev/null
+++ b/drivers/net/nfb/nfb_vdev.c
@@ -0,0 +1,102 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2026 Cesnet
+ */
+
+#include <rte_kvargs.h>
+#include <ethdev_vdev.h>
+
+#include "nfb.h"
+
+#define VDEV_NFB_DRIVER net_vdev_nfb
+#define VDEV_NFB_ARG_DEV "dev"
+
+static int
+vdev_nfb_vdev_probe(struct rte_vdev_device *dev)
+{
+ unsigned int count;
+ int ret = 0;
+ size_t len, pos;
+
+ struct nfb_init_params params;
+
+ const char *vdev_name = rte_vdev_device_name(dev);
+ const char *vdev_args = rte_vdev_device_args(dev);
+ char *dev_params, *dev_params_mod;
+ struct rte_kvargs *kvargs;
+
+ kvargs = rte_kvargs_parse(vdev_args, NULL);
+ if (kvargs == NULL) {
+ NFB_LOG(ERR, "Failed to parse device arguments %s", vdev_args);
+ ret = -EINVAL;
+ goto err_parse_args;
+ }
+
+ dev_params = strdup(vdev_args);
+ if (dev_params == NULL) {
+ ret = -ENOMEM;
+ goto err_strdup_params;
+ }
+
+ params.args = dev_params;
+ params.path = nfb_default_dev_path();
+ params.nfb_id = 0;
+
+ len = strlen(dev_params) + 1;
+ pos = 0;
+ dev_params[pos] = '\0';
+
+ /* Parse parameters for virtual device */
+ for (count = 0; count != kvargs->count; ++count) {
+ const struct rte_kvargs_pair *pair = &kvargs->pairs[count];
+
+ if (!strcmp(pair->key, VDEV_NFB_ARG_DEV)) {
+ params.path = pair->value;
+ } else {
+ /* Clone non-vdev arguments, result is shorter or equal length */
+ dev_params_mod = dev_params + pos;
+ ret = snprintf(dev_params_mod, len, "%s%s=%s",
+ pos == 0 ? "" : ",", pair->key, pair->value);
+ if (ret < 0 || ret >= (signed int)len)
+ goto err_clone_args;
+ pos += ret;
+ len -= ret;
+ }
+ }
+
+ strlcpy(params.name, vdev_name, sizeof(params.name));
+
+ ret = nfb_eth_common_probe(&dev->device, NULL, NULL, ¶ms, -1);
+ if (ret)
+ goto err_nfb_common_probe;
+
+ free(dev_params);
+ rte_kvargs_free(kvargs);
+
+ return ret;
+
+err_nfb_common_probe:
+err_clone_args:
+ free(dev_params);
+err_strdup_params:
+ rte_kvargs_free(kvargs);
+err_parse_args:
+ return ret;
+}
+
+static int
+vdev_nfb_vdev_remove(struct rte_vdev_device *dev)
+{
+ return nfb_eth_common_remove(&dev->device);
+}
+
+/** Virtual device descriptor. */
+static struct rte_vdev_driver vdev_nfb_vdev = {
+ .probe = vdev_nfb_vdev_probe,
+ .remove = vdev_nfb_vdev_remove,
+};
+
+RTE_PMD_REGISTER_VDEV(VDEV_NFB_DRIVER, vdev_nfb_vdev);
+RTE_PMD_REGISTER_ALIAS(VDEV_NFB_DRIVER, eth_vdev_nfb);
+RTE_PMD_REGISTER_PARAM_STRING(net_vdev_nfb,
+ VDEV_NFB_ARG_DEV "=<string>"
+ );
--
2.52.0
^ permalink raw reply related [flat|nested] 131+ messages in thread
* [PATCH v4 4/8] net/nfb: add device argument "port" to limit used ports
2026-01-22 7:27 ` [PATCH v4 " spinler
` (2 preceding siblings ...)
2026-01-22 7:27 ` [PATCH v4 3/8] net/nfb: add vdev as alternative device probe method spinler
@ 2026-01-22 7:27 ` spinler
2026-01-22 7:27 ` [PATCH v4 5/8] net/nfb: init only MACs associated with device spinler
` (6 subsequent siblings)
10 siblings, 0 replies; 131+ messages in thread
From: spinler @ 2026-01-22 7:27 UTC (permalink / raw)
To: dev; +Cc: Martin Spinler
From: Martin Spinler <spinler@cesnet.cz>
NFB devices do not use separate PCI device for each port; one PCI device
contains handles for all ports instead. For some application this approach
can be limiting.
The "port" argument can be used to select only desired ports.
It can be used multiple times. When is not used at all, the driver
selects all ports.
Signed-off-by: Martin Spinler <spinler@cesnet.cz>
---
drivers/net/nfb/nfb.h | 7 +++++
drivers/net/nfb/nfb_ethdev.c | 54 +++++++++++++++++++++++++++++++++++-
drivers/net/nfb/nfb_vdev.c | 3 +-
3 files changed, 62 insertions(+), 2 deletions(-)
diff --git a/drivers/net/nfb/nfb.h b/drivers/net/nfb/nfb.h
index d0d46f4a38..605ea8e875 100644
--- a/drivers/net/nfb/nfb.h
+++ b/drivers/net/nfb/nfb.h
@@ -42,6 +42,13 @@ extern int nfb_logtype;
#define RTE_NFB_DRIVER_NAME net_nfb
+/* Device arguments */
+#define NFB_ARG_PORT "port"
+
+#define NFB_COMMON_ARGS \
+ NFB_ARG_PORT "=<number>" \
+ ""
+
/*
* Handles obtained from the libnfb: each process must use own instance.
* Stored inside dev->process_private.
diff --git a/drivers/net/nfb/nfb_ethdev.c b/drivers/net/nfb/nfb_ethdev.c
index 3c009fc1a4..f7c50ea3c4 100644
--- a/drivers/net/nfb/nfb_ethdev.c
+++ b/drivers/net/nfb/nfb_ethdev.c
@@ -22,6 +22,11 @@
#include "nfb_rxmode.h"
#include "nfb.h"
+static const char * const VALID_KEYS[] = {
+ NFB_ARG_PORT,
+ NULL
+};
+
/* The TAILQ entries are used for cleanup of allocated resources
* in situations, where dev_close is not called.
*/
@@ -664,6 +669,23 @@ nfb_eth_dev_uninit(struct rte_eth_dev *dev)
return 0;
}
+static int fill_port_mask(const char *key __rte_unused, const char *value, void *opaque)
+{
+ char *end;
+ uint64_t *port_mask = opaque;
+ unsigned long port;
+
+ if (value == NULL || strlen(value) == 0 || !isdigit(*value))
+ return -1;
+
+ port = strtoul(value, &end, 0);
+ if (*end != '\0' || port >= ULONG_WIDTH)
+ return -1;
+
+ *port_mask |= RTE_BIT64(port);
+ return 0;
+}
+
RTE_EXPORT_INTERNAL_SYMBOL(nfb_eth_common_probe)
int
nfb_eth_common_probe(struct rte_device *device,
@@ -671,7 +693,7 @@ nfb_eth_common_probe(struct rte_device *device,
struct nfb_init_params *params, int ep_index)
{
int i;
- int ret;
+ int ret = 0;
int basename_len;
struct nc_ifc_info *ifc;
@@ -679,6 +701,9 @@ nfb_eth_common_probe(struct rte_device *device,
struct rte_eth_dev *eth_dev;
struct pmd_internals *p;
+ struct rte_kvargs *kvlist;
+ uint64_t port_mask = UINT64_MAX;
+
basename_len = strlen(params->name);
nfb_dev = nfb_open(params->path);
@@ -689,6 +714,27 @@ nfb_eth_common_probe(struct rte_device *device,
nc_ifc_map_info_create_ordinary(nfb_dev, ¶ms->map_info);
+ if (params->args != NULL && strlen(params->args) > 0) {
+ kvlist = rte_kvargs_parse(params->args, VALID_KEYS);
+ if (kvlist == NULL) {
+ NFB_LOG(ERR, "Failed to parse device arguments %s", params->args);
+ return -EINVAL;
+ }
+ if (rte_kvargs_count(kvlist, NFB_ARG_PORT)) {
+ port_mask = 0;
+ if (rte_kvargs_process(kvlist, NFB_ARG_PORT, fill_port_mask,
+ (void *)&port_mask))
+ ret = -1;
+ if (ret || port_mask >= RTE_BIT64(params->map_info.ifc_cnt))
+ port_mask = 0;
+ }
+ rte_kvargs_free(kvlist);
+ if (port_mask == 0) {
+ NFB_LOG(ERR, "Failed to parse device port argument");
+ return -EINVAL;
+ }
+ }
+
for (i = 0; i < params->map_info.ifc_cnt; i++) {
ifc = ¶ms->map_info.ifc[i];
params->ifc_info = ifc;
@@ -698,6 +744,9 @@ nfb_eth_common_probe(struct rte_device *device,
(ifc->flags & NC_IFC_INFO_FLAG_ACTIVE) == 0)
continue;
+ if ((port_mask & RTE_BIT64(i)) == 0)
+ continue;
+
snprintf(params->name + basename_len, sizeof(params->name) - basename_len,
"_eth%d", params->ifc_info->id);
@@ -818,3 +867,6 @@ RTE_PMD_REGISTER_PCI(RTE_NFB_DRIVER_NAME, nfb_eth_driver);
RTE_PMD_REGISTER_PCI_TABLE(RTE_NFB_DRIVER_NAME, nfb_pci_id_table);
RTE_PMD_REGISTER_KMOD_DEP(RTE_NFB_DRIVER_NAME, "* nfb");
RTE_LOG_REGISTER_DEFAULT(nfb_logtype, NOTICE);
+RTE_PMD_REGISTER_PARAM_STRING(RTE_NFB_DRIVER_NAME,
+ NFB_COMMON_ARGS
+ );
diff --git a/drivers/net/nfb/nfb_vdev.c b/drivers/net/nfb/nfb_vdev.c
index c3629bd395..81f9301def 100644
--- a/drivers/net/nfb/nfb_vdev.c
+++ b/drivers/net/nfb/nfb_vdev.c
@@ -98,5 +98,6 @@ static struct rte_vdev_driver vdev_nfb_vdev = {
RTE_PMD_REGISTER_VDEV(VDEV_NFB_DRIVER, vdev_nfb_vdev);
RTE_PMD_REGISTER_ALIAS(VDEV_NFB_DRIVER, eth_vdev_nfb);
RTE_PMD_REGISTER_PARAM_STRING(net_vdev_nfb,
- VDEV_NFB_ARG_DEV "=<string>"
+ VDEV_NFB_ARG_DEV "=<string> "
+ NFB_COMMON_ARGS
);
--
2.52.0
^ permalink raw reply related [flat|nested] 131+ messages in thread
* [PATCH v4 5/8] net/nfb: init only MACs associated with device
2026-01-22 7:27 ` [PATCH v4 " spinler
` (3 preceding siblings ...)
2026-01-22 7:27 ` [PATCH v4 4/8] net/nfb: add device argument "port" to limit used ports spinler
@ 2026-01-22 7:27 ` spinler
2026-01-22 7:27 ` [PATCH v4 6/8] net/nfb: add compatible cards to driver PCI ID table spinler
` (5 subsequent siblings)
10 siblings, 0 replies; 131+ messages in thread
From: spinler @ 2026-01-22 7:27 UTC (permalink / raw)
To: dev; +Cc: Martin Spinler
From: Martin Spinler <spinler@cesnet.cz>
Use information obtained from the new manner of creating NFB ethdevs
and initialize only RxMACs / TxMACs associated with actual ethdev.
This enables separated management and configuration of interface ports.
Signed-off-by: Martin Spinler <spinler@cesnet.cz>
---
drivers/net/nfb/nfb.h | 8 +-
drivers/net/nfb/nfb_ethdev.c | 141 +++++++++++++++++++----------------
2 files changed, 80 insertions(+), 69 deletions(-)
diff --git a/drivers/net/nfb/nfb.h b/drivers/net/nfb/nfb.h
index 605ea8e875..cfbc50251f 100644
--- a/drivers/net/nfb/nfb.h
+++ b/drivers/net/nfb/nfb.h
@@ -36,10 +36,6 @@ extern int nfb_logtype;
#define RTE_ETH_NDP_MAX_RX_QUEUES 32
#define RTE_ETH_NDP_MAX_TX_QUEUES 32
-/* Max index of rx/tx dmas */
-#define RTE_MAX_NC_RXMAC 256
-#define RTE_MAX_NC_TXMAC 256
-
#define RTE_NFB_DRIVER_NAME net_nfb
/* Device arguments */
@@ -56,8 +52,8 @@ extern int nfb_logtype;
struct pmd_internals {
uint16_t max_rxmac;
uint16_t max_txmac;
- struct nc_rxmac *rxmac[RTE_MAX_NC_RXMAC];
- struct nc_txmac *txmac[RTE_MAX_NC_TXMAC];
+ struct nc_rxmac **rxmac;
+ struct nc_txmac **txmac;
struct nfb_device *nfb;
TAILQ_ENTRY(pmd_internals) eth_dev_list;
diff --git a/drivers/net/nfb/nfb_ethdev.c b/drivers/net/nfb/nfb_ethdev.c
index f7c50ea3c4..59e5a51b58 100644
--- a/drivers/net/nfb/nfb_ethdev.c
+++ b/drivers/net/nfb/nfb_ethdev.c
@@ -44,81 +44,94 @@ static const struct rte_ether_addr eth_addr = {
};
/**
- * Open all RX DMA queues
+ * Open RX MAC components associated with current ifc
*
- * @param dev
- * Pointer to nfb device.
- * @param[out] rxmac
- * Pointer to output array of nc_rxmac
- * @param[out] max_rxmac
- * Pointer to output max index of rxmac
+ * @param priv
+ * Pointer to driver private structure
+ * @param params
+ * Pointer to init parameters structure
*/
-static void
-nfb_nc_rxmac_init(struct nfb_device *nfb,
- struct nc_rxmac *rxmac[RTE_MAX_NC_RXMAC],
- uint16_t *max_rxmac)
+static int
+nfb_nc_rxmac_init(struct pmd_internals *priv, struct nfb_init_params *params)
{
- *max_rxmac = 0;
- while ((rxmac[*max_rxmac] = nc_rxmac_open_index(nfb, *max_rxmac)))
- ++(*max_rxmac);
+ int i, j;
+ struct nc_ifc_info *ifc = params->ifc_info;
+ struct nc_ifc_map_info *mi = ¶ms->map_info;
+
+ priv->rxmac = rte_calloc("NFB RxMAC", ifc->eth_cnt, sizeof(*priv->rxmac), 0);
+ if (ifc->eth_cnt && priv->rxmac == NULL)
+ return -ENOMEM;
+
+ for (i = 0, j = 0; i < mi->eth_cnt && j < ifc->eth_cnt; i++) {
+ if (mi->eth[i].ifc != ifc->id)
+ continue;
+ priv->rxmac[j] = nc_rxmac_open(priv->nfb, mi->eth[i].node_rxmac);
+ if (priv->rxmac[j])
+ j++;
+ }
+
+ priv->max_rxmac = j;
+ return 0;
}
/**
- * Open all TX DMA queues
+ * Open TX MAC components associated with current ifc
*
- * @param dev
- * Pointer to nfb device.
- * @param[out] txmac
- * Pointer to output array of nc_txmac
- * @param[out] max_rxmac
- * Pointer to output max index of txmac
+ * @param priv
+ * Pointer to driver private structure
+ * @param params
+ * Pointer to init parameters structure
*/
-static void
-nfb_nc_txmac_init(struct nfb_device *nfb,
- struct nc_txmac *txmac[RTE_MAX_NC_TXMAC],
- uint16_t *max_txmac)
+static int
+nfb_nc_txmac_init(struct pmd_internals *priv, struct nfb_init_params *params)
{
- *max_txmac = 0;
- while ((txmac[*max_txmac] = nc_txmac_open_index(nfb, *max_txmac)))
- ++(*max_txmac);
+ int i, j;
+ struct nc_ifc_info *ifc = params->ifc_info;
+ struct nc_ifc_map_info *mi = ¶ms->map_info;
+
+ priv->txmac = rte_calloc("NFB TxMAC", ifc->eth_cnt, sizeof(*priv->txmac), 0);
+ if (ifc->eth_cnt && priv->txmac == NULL)
+ return -ENOMEM;
+
+ for (i = 0, j = 0; i < mi->eth_cnt && j < ifc->eth_cnt; i++) {
+ if (mi->eth[i].ifc != ifc->id)
+ continue;
+ priv->txmac[j] = nc_txmac_open(priv->nfb, mi->eth[i].node_txmac);
+ if (priv->txmac[j])
+ j++;
+ }
+
+ priv->max_txmac = j;
+ return 0;
}
/**
- * Close all RX DMA queues
- *
- * @param rxmac
- * Pointer to array of nc_rxmac
- * @param max_rxmac
- * Maximum index of rxmac
+ * Close all RX MAC components
+ * @param priv
+ * Pointer to driver private structure
*/
static void
-nfb_nc_rxmac_deinit(struct nc_rxmac *rxmac[RTE_MAX_NC_RXMAC],
- uint16_t max_rxmac)
+nfb_nc_rxmac_deinit(struct pmd_internals *priv)
{
uint16_t i;
- for (i = 0; i < max_rxmac; i++) {
- nc_rxmac_close(rxmac[i]);
- rxmac[i] = NULL;
- }
+ for (i = 0; i < priv->max_rxmac; i++)
+ nc_rxmac_close(priv->rxmac[i]);
+
+ rte_free(priv->rxmac);
}
/**
- * Close all TX DMA queues
- *
- * @param txmac
- * Pointer to array of nc_txmac
- * @param max_txmac
- * Maximum index of txmac
+ * Close all TX MAC components
+ * @param priv
+ * Pointer to driver private structure
*/
static void
-nfb_nc_txmac_deinit(struct nc_txmac *txmac[RTE_MAX_NC_TXMAC],
- uint16_t max_txmac)
+nfb_nc_txmac_deinit(struct pmd_internals *priv)
{
uint16_t i;
- for (i = 0; i < max_txmac; i++) {
- nc_txmac_close(txmac[i]);
- txmac[i] = NULL;
- }
+ for (i = 0; i < priv->max_txmac; i++)
+ nc_txmac_close(priv->txmac[i]);
+ rte_free(priv->txmac);
}
/**
@@ -335,7 +348,7 @@ nfb_eth_link_update(struct rte_eth_dev *dev,
link.link_duplex = RTE_ETH_LINK_FULL_DUPLEX;
link.link_autoneg = RTE_ETH_LINK_SPEED_FIXED;
- if (internals->rxmac[0] != NULL) {
+ if (internals->max_rxmac) {
nc_rxmac_read_status(internals->rxmac[0], &status);
switch (status.speed) {
@@ -559,12 +572,12 @@ nfb_eth_dev_init(struct rte_eth_dev *dev, void *init_data)
max_rx_queues = ifc->rxq_cnt;
max_tx_queues = ifc->txq_cnt;
- nfb_nc_rxmac_init(internals->nfb,
- internals->rxmac,
- &internals->max_rxmac);
- nfb_nc_txmac_init(internals->nfb,
- internals->txmac,
- &internals->max_txmac);
+ ret = nfb_nc_rxmac_init(internals, params);
+ if (ret)
+ goto err_rxmac_init;
+ ret = nfb_nc_txmac_init(internals, params);
+ if (ret)
+ goto err_txmac_init;
/* Set rx, tx burst functions */
dev->rx_pkt_burst = nfb_eth_ndp_rx;
@@ -630,8 +643,10 @@ nfb_eth_dev_init(struct rte_eth_dev *dev, void *init_data)
err_malloc_mac_addrs:
rte_free(priv->queue_map_rx);
err_alloc_queue_map:
- nfb_nc_rxmac_deinit(internals->rxmac, internals->max_rxmac);
- nfb_nc_txmac_deinit(internals->txmac, internals->max_txmac);
+ nfb_nc_txmac_deinit(internals);
+err_txmac_init:
+ nfb_nc_rxmac_deinit(internals);
+err_rxmac_init:
nfb_close(internals->nfb);
err_nfb_open:
@@ -660,8 +675,8 @@ nfb_eth_dev_uninit(struct rte_eth_dev *dev)
TAILQ_REMOVE(&nfb_eth_dev_list, internals, eth_dev_list);
- nfb_nc_rxmac_deinit(internals->rxmac, internals->max_rxmac);
- nfb_nc_txmac_deinit(internals->txmac, internals->max_txmac);
+ nfb_nc_rxmac_deinit(internals);
+ nfb_nc_txmac_deinit(internals);
nfb_close(internals->nfb);
rte_free(internals);
--
2.52.0
^ permalink raw reply related [flat|nested] 131+ messages in thread
* [PATCH v4 6/8] net/nfb: add compatible cards to driver PCI ID table
2026-01-22 7:27 ` [PATCH v4 " spinler
` (4 preceding siblings ...)
2026-01-22 7:27 ` [PATCH v4 5/8] net/nfb: init only MACs associated with device spinler
@ 2026-01-22 7:27 ` spinler
2026-01-22 7:27 ` [PATCH v4 7/8] net/nfb: report firmware version spinler
` (4 subsequent siblings)
10 siblings, 0 replies; 131+ messages in thread
From: spinler @ 2026-01-22 7:27 UTC (permalink / raw)
To: dev; +Cc: Martin Spinler
From: Martin Spinler <spinler@cesnet.cz>
The nfb driver is compatible with:
- NFB-200G2QL card secondary PCI device
- FB2CGHH card
- COMBO-400G1 card
- Common CESNET-NDK cards
Signed-off-by: Martin Spinler <spinler@cesnet.cz>
---
drivers/net/nfb/nfb.h | 5 +++++
drivers/net/nfb/nfb_ethdev.c | 4 ++++
2 files changed, 9 insertions(+)
diff --git a/drivers/net/nfb/nfb.h b/drivers/net/nfb/nfb.h
index cfbc50251f..0e35e00d2e 100644
--- a/drivers/net/nfb/nfb.h
+++ b/drivers/net/nfb/nfb.h
@@ -24,13 +24,18 @@ extern int nfb_logtype;
/* PCI Vendor ID */
#define PCI_VENDOR_ID_NETCOPE 0x1b26
#define PCI_VENDOR_ID_SILICOM 0x1c2c
+#define PCI_VENDOR_ID_CESNET 0x18ec
/* PCI Device IDs */
#define PCI_DEVICE_ID_NFB_40G2 0xcb80
#define PCI_DEVICE_ID_NFB_100G2 0xc2c1
#define PCI_DEVICE_ID_NFB_200G2QL 0xc250
+#define PCI_DEVICE_ID_NFB_200G2QL_E1 0xc251
#define PCI_DEVICE_ID_FB2CGG3 0x00d0
+#define PCI_DEVICE_ID_FB2CGHH 0x00d2
#define PCI_DEVICE_ID_FB2CGG3D 0xc240
+#define PCI_DEVICE_ID_COMBO400G1 0xc400
+#define PCI_DEVICE_ID_CESNET_NDK_COMMON 0xc000
/* Max index of ndp rx/tx queues */
#define RTE_ETH_NDP_MAX_RX_QUEUES 32
diff --git a/drivers/net/nfb/nfb_ethdev.c b/drivers/net/nfb/nfb_ethdev.c
index 59e5a51b58..e2959c3a04 100644
--- a/drivers/net/nfb/nfb_ethdev.c
+++ b/drivers/net/nfb/nfb_ethdev.c
@@ -788,8 +788,12 @@ static const struct rte_pci_id nfb_pci_id_table[] = {
{ RTE_PCI_DEVICE(PCI_VENDOR_ID_NETCOPE, PCI_DEVICE_ID_NFB_40G2) },
{ RTE_PCI_DEVICE(PCI_VENDOR_ID_NETCOPE, PCI_DEVICE_ID_NFB_100G2) },
{ RTE_PCI_DEVICE(PCI_VENDOR_ID_NETCOPE, PCI_DEVICE_ID_NFB_200G2QL) },
+ { RTE_PCI_DEVICE(PCI_VENDOR_ID_NETCOPE, PCI_DEVICE_ID_NFB_200G2QL_E1) },
{ RTE_PCI_DEVICE(PCI_VENDOR_ID_SILICOM, PCI_DEVICE_ID_FB2CGG3) },
+ { RTE_PCI_DEVICE(PCI_VENDOR_ID_SILICOM, PCI_DEVICE_ID_FB2CGHH) },
{ RTE_PCI_DEVICE(PCI_VENDOR_ID_SILICOM, PCI_DEVICE_ID_FB2CGG3D) },
+ { RTE_PCI_DEVICE(PCI_VENDOR_ID_CESNET, PCI_DEVICE_ID_COMBO400G1) },
+ { RTE_PCI_DEVICE(PCI_VENDOR_ID_CESNET, PCI_DEVICE_ID_CESNET_NDK_COMMON) },
{ .vendor_id = 0, }
};
--
2.52.0
^ permalink raw reply related [flat|nested] 131+ messages in thread
* [PATCH v4 7/8] net/nfb: report firmware version
2026-01-22 7:27 ` [PATCH v4 " spinler
` (5 preceding siblings ...)
2026-01-22 7:27 ` [PATCH v4 6/8] net/nfb: add compatible cards to driver PCI ID table spinler
@ 2026-01-22 7:27 ` spinler
2026-01-22 7:27 ` [PATCH v4 8/8] doc/nfb: cleanup and update guide spinler
` (3 subsequent siblings)
10 siblings, 0 replies; 131+ messages in thread
From: spinler @ 2026-01-22 7:27 UTC (permalink / raw)
To: dev; +Cc: Martin Spinler
From: Martin Spinler <spinler@cesnet.cz>
The firmware is described by project name and project version.
Signed-off-by: Martin Spinler <spinler@cesnet.cz>
---
drivers/net/nfb/nfb_ethdev.c | 26 ++++++++++++++++++++++++++
1 file changed, 26 insertions(+)
diff --git a/drivers/net/nfb/nfb_ethdev.c b/drivers/net/nfb/nfb_ethdev.c
index e2959c3a04..85b05c99d0 100644
--- a/drivers/net/nfb/nfb_ethdev.c
+++ b/drivers/net/nfb/nfb_ethdev.c
@@ -498,6 +498,31 @@ nfb_eth_mac_addr_remove(struct rte_eth_dev *dev, uint32_t index)
nc_rxmac_set_mac(internals->rxmac[i], index, 0, 0);
}
+static int
+nfb_eth_fw_version_get(struct rte_eth_dev *dev, char *fw_version,
+ size_t fw_size)
+{
+ int ret;
+ const char *proj_name, *proj_vers;
+ struct pmd_internals *priv = (struct pmd_internals *)
+ dev->process_private;
+
+ proj_name = nc_info_get_fw_project_name(priv->nfb, NULL);
+ proj_vers = nc_info_get_fw_project_version(priv->nfb, NULL);
+
+ if (proj_name == NULL)
+ proj_name = "";
+ if (proj_vers == NULL)
+ proj_vers = "";
+
+ ret = snprintf(fw_version, fw_size, "%s;%s", proj_name, proj_vers);
+
+ if (ret >= (signed int)fw_size)
+ return strlen(proj_name) + 1 + strlen(proj_vers) + 1;
+
+ return 0;
+}
+
static const struct eth_dev_ops ops = {
.dev_start = nfb_eth_dev_start,
.dev_stop = nfb_eth_dev_stop,
@@ -524,6 +549,7 @@ static const struct eth_dev_ops ops = {
.mac_addr_set = nfb_eth_mac_addr_set,
.mac_addr_add = nfb_eth_mac_addr_add,
.mac_addr_remove = nfb_eth_mac_addr_remove,
+ .fw_version_get = nfb_eth_fw_version_get,
};
/**
--
2.52.0
^ permalink raw reply related [flat|nested] 131+ messages in thread
* [PATCH v4 8/8] doc/nfb: cleanup and update guide
2026-01-22 7:27 ` [PATCH v4 " spinler
` (6 preceding siblings ...)
2026-01-22 7:27 ` [PATCH v4 7/8] net/nfb: report firmware version spinler
@ 2026-01-22 7:27 ` spinler
2026-01-23 1:00 ` [PATCH v4 0/8] net/nfb: rework to real multiport Stephen Hemminger
` (2 subsequent siblings)
10 siblings, 0 replies; 131+ messages in thread
From: spinler @ 2026-01-22 7:27 UTC (permalink / raw)
To: dev; +Cc: Martin Spinler
From: Martin Spinler <spinler@cesnet.cz>
Clean up documentation and update the guide with:
- Current card list with firmware download links
- Updated port argument description
Signed-off-by: Martin Spinler <spinler@cesnet.cz>
---
doc/guides/nics/features/nfb.ini | 4 +
doc/guides/nics/nfb.rst | 187 +++++++++++--------------
doc/guides/rel_notes/release_26_03.rst | 9 ++
3 files changed, 98 insertions(+), 102 deletions(-)
diff --git a/doc/guides/nics/features/nfb.ini b/doc/guides/nics/features/nfb.ini
index c77b592789..7ac89fc15f 100644
--- a/doc/guides/nics/features/nfb.ini
+++ b/doc/guides/nics/features/nfb.ini
@@ -9,9 +9,13 @@ Link status = Y
Queue start/stop = Y
Promiscuous mode = Y
Allmulticast mode = Y
+Unicast MAC filter = Y
+Timestamp offload = P
Basic stats = Y
Extended stats = Y
Stats per queue = Y
+FW version = Y
+Multiprocess aware = Y
Linux = Y
x86-64 = Y
Usage doc = Y
diff --git a/doc/guides/nics/nfb.rst b/doc/guides/nics/nfb.rst
index a9b4049654..55a635d8d4 100644
--- a/doc/guides/nics/nfb.rst
+++ b/doc/guides/nics/nfb.rst
@@ -2,64 +2,101 @@
Copyright 2019 Cesnet
Copyright 2019 Netcope Technologies
-NFB poll mode driver library
-=================================
+NFB Poll Mode Driver
+====================
+
+The NFB PMD implements support for the FPGA-based
+programmable NICs running `CESNET-NDK <https://www.liberouter.org/ndk/>`_
+based firmware (formerly known as the NetCOPE platform). The CESNET Network Development Kit offers
+wide spectrum of supported cards, for example:
+N6010, FB2CGG3 (Silicom Denmark),
+IA-420F, IA-440i (BittWare),
+AGI-FH400G (ReflexCES),
+and `many more <https://github.com/CESNET/ndk-fpga/tree/devel/cards>`_.
+
+The CESNET-NDK framework is open source and
+can be found on `CESNET-NDK <https://github.com/CESNET/ndk-fpga>`_.
+Ready-to-use demo firmwares can be found on the `DYNANIC page <https://dyna-nic.com/try-now/>`_.
+
+Software compatibility and firmware for
+`historical cards <https://www.liberouter.org/technologies/cards/>`_
+are left unmaintained.
+
+Software prerequisites
+----------------------
-The NFB poll mode driver library implements support for the Netcope
-FPGA Boards (**NFB-40G2, NFB-100G2, NFB-200G2QL**) and Silicom **FB2CGG3** card,
-FPGA-based programmable NICs. The NFB PMD uses interface provided by the libnfb
-library to communicate with these cards over the nfb layer.
+This PMD requires a Linux kernel module, which is responsible for initialization and
+allocation of resources needed for the nfb layer function.
+Communication between PMD and kernel modules is mediated by the libnfb library.
+The kernel module and library are not part of DPDK and must be installed
+separately. Dependencies can be found on GitHub:
+`nfb-framework <https://github.com/CESNET/ndk-sw>`_ as source code,
+or for RPM-based distributions, the prebuilt `nfb-framework` package on
+`Fedora Copr <https://copr.fedorainfracloud.org/coprs/g/CESNET/nfb-framework/>`_.
-More information about the
-`NFB cards <https://www.liberouter.org/technologies/cards/>`_
-and used technology
-(`Network Development Kit <https://www.liberouter.org/ndk/>`_)
-can be found on the `Liberouter website <http://www.liberouter.org/>`_.
+Before starting the DPDK, make sure that the kernel module is loaded (`sudo modprobe nfb`)
+and the card is running the CESNET-NDK based firmware (`nfb-info -l`).
.. note::
- Currently the driver is supported only on x86_64 architectures.
- Only x86_64 versions of the external libraries are provided.
+ Currently, the driver is supported only on x86_64 architectures.
+
+NFB card architecture
+---------------------
+
+Ethernet Ports
+~~~~~~~~~~~~~~
+
+The NFB cards are multi-port multi-queue cards, where (generally) data from any
+Ethernet port may be sent by the firmware to any queue.
-Prerequisites
--------------
+The cards were historically represented in DPDK as a single port.
+Currently each Ethernet channel is represented as one DPDK port.
-This PMD requires kernel modules which are responsible for initialization and
-allocation of resources needed for nfb layer function.
-Communication between PMD and kernel modules is mediated by libnfb library.
-These kernel modules and library are not part of DPDK and must be installed
-separately:
+.. note::
-* **libnfb library**
+ Normally, one port corresponds to one channel, but ports can often be configured in a separate
+ manner. For example one 100G port can be used as 4x25G or 4x10G independent Ethernet channels.
- The library provides API for initialization of nfb transfers, receiving and
- transmitting data segments.
+By default, all ports are initialized and used for the allowed PCI device.
+When this behaviour is limiting (e.g., for multiple instances of DPDK app on different ports
+of the same PCI device), ports can be specified by the `port` item in the `allow` argument:
-* **Kernel modules**
+.. code-block:: console
- * nfb
+ -a 0000:01:00.0,port=0,port=3
- Kernel modules manage initialization of hardware, allocation and
- sharing of resources for user space applications.
+PCIe slots
+~~~~~~~~~~
-Dependencies can be found here:
-`Netcope common <https://github.com/CESNET/ndk-sw>`_.
+Some cards employ more than one PCIe device for better data throughput. This can be achieved by
+slot bifurcation (only a minor improvement) or by an add-on cable connected to another PCIe slot.
+Both improvements can work together, as is, for example, in the case of the AGI-FH400G card.
-Versions of the packages
-~~~~~~~~~~~~~~~~~~~~~~~~
+Because primary and secondary slot(s) can be attached to different NUMA nodes
+(also applies for bifurcation on some HW), the data structures need to be correctly allocated.
+(Device-aware allocation matters also on IOMMU-enabled systems.)
+The firmware already provides DMA queue to PCI device mapping. The DPDK application just needs to
+use all PCI devices, otherwise some queues will not be available; provide all PCI endpoints
+listed in the `nfb-info -v` in the `allow` argument.
-The minimum version of the provided packages:
+.. note::
-* for DPDK from 19.05
+ For cards where the number of Ethernet ports is less than the number of PCI devices
+ (e.g., AGI-FH400G: 1 port, up to 4 PCI devices), the virtual DPDK ports are
+ created to achieve the best NUMA-aware throughput
+ (virtual ports lack a lot of configuration features).
-Configuration
--------------
+Features
+--------
Timestamps
+~~~~~~~~~~
-The PMD supports hardware timestamps of frame receipt on physical network interface. In order to use
-the timestamps, the hardware timestamping unit must be enabled (follow the documentation of the NFB
-products). The standard `RTE_ETH_RX_OFFLOAD_TIMESTAMP` flag can be used for this feature.
+The PMD supports hardware timestamps of frame receipt on physical network interface.
+In order to use the timestamps, the hardware timestamping unit must be enabled
+(follow the documentation of the NFB products).
+The standard `RTE_ETH_RX_OFFLOAD_TIMESTAMP` flag can be used for this feature.
When the timestamps are enabled, a timestamp validity flag is set in the MBUFs
containing received frames and timestamp is inserted into the `rte_mbuf` struct.
@@ -67,72 +104,18 @@ containing received frames and timestamp is inserted into the `rte_mbuf` struct.
The timestamp is an `uint64_t` field and holds the number of nanoseconds
elapsed since 1.1.1970 00:00:00 UTC.
+Simulation
+~~~~~~~~~~
-Using the NFB PMD
-----------------------
-
-Kernel modules have to be loaded before running the DPDK application.
-
-NFB card architecture
----------------------
-
-The NFB cards are multi-port multi-queue cards, where (generally) data from any
-Ethernet port may be sent to any queue.
-They are represented in DPDK as a single port.
-
-NFB-200G2QL card employs an add-on cable which allows to connect it to two
-physical PCI-E slots at the same time (see the diagram below).
-This is done to allow 200 Gbps of traffic to be transferred through the PCI-E
-bus (note that a single PCI-E 3.0 x16 slot provides only 125 Gbps theoretical
-throughput).
-
-Although each slot may be connected to a different CPU and therefore to a different
-NUMA node, the card is represented as a single port in DPDK. To work with data
-from the individual queues on the right NUMA node, connection of NUMA nodes on
-first and last queue (each NUMA node has half of the queues) need to be checked.
-
-Limitations
------------
-
-Driver is usable only on Linux architecture, namely on CentOS.
-
-Since a card is always represented as a single port, but can be connected to two
-NUMA nodes, there is need for manual check where master/slave is connected.
-
-Example of usage
-----------------
-
-Read packets from 0. and 1. receive queue and write them to 0. and 1.
-transmit queue:
+The CESNET-NDK framework offers the possibility of simulating the firmware together with DPDK.
+This allows for easy debugging of a packet flow behaviour with a specific firmware configuration.
+The DPDK NFB driver can be connected to the simulator (Questa/ModelSim/nvc) via a virtual device:
.. code-block:: console
- ./<build_dir>/app/dpdk-testpmd -l 0-3 -n 2 \
- -- --port-topology=chained --rxq=2 --txq=2 --nb-cores=2 -i -a
-
-Example output:
-
-.. code-block:: console
+ dpdk-testpmd
+ --vdev=eth_vdev_nfb,dev=libnfb-ext-grpc.so:grpc+dma_vas:localhost:50051,queue_driver=native
+ --iova-mode=va -- -i
- [...]
- EAL: PCI device 0000:06:00.0 on NUMA socket -1
- EAL: probe driver: 1b26:c1c1 net_nfb
- PMD: Initializing NFB device (0000:06:00.0)
- PMD: Available DMA queues RX: 8 TX: 8
- PMD: NFB device (0000:06:00.0) successfully initialized
- Interactive-mode selected
- Auto-start selected
- Configuring Port 0 (socket 0)
- Port 0: 00:11:17:00:00:00
- Checking link statuses...
- Port 0 Link Up - speed 10000 Mbps - full-duplex
- Done
- Start automatic packet forwarding
- io packet forwarding - CRC stripping disabled - packets/burst=32
- nb forwarding cores=2 - nb forwarding ports=1
- RX queues=2 - RX desc=128 - RX free threshold=0
- RX threshold registers: pthresh=0 hthresh=0 wthresh=0
- TX queues=2 - TX desc=512 - TX free threshold=0
- TX threshold registers: pthresh=0 hthresh=0 wthresh=0
- TX RS bit threshold=0 - TXQ flags=0x0
- testpmd>
+More info about the simulation can be found int the CESNET-NDK `documentation
+<https://cesnet.github.io/ndk-fpga/devel/ndk_apps/minimal/tests/cocotb/readme.html>`_.
diff --git a/doc/guides/rel_notes/release_26_03.rst b/doc/guides/rel_notes/release_26_03.rst
index 52663580e3..d484ca98d2 100644
--- a/doc/guides/rel_notes/release_26_03.rst
+++ b/doc/guides/rel_notes/release_26_03.rst
@@ -55,6 +55,15 @@ New Features
Also, make sure to start the actual text at the margin.
=======================================================
+* **Updated CESNET NFB ethernet driver.**
+
+ * The timestamp value has been updated to make it usable.
+ * The DPDK port now represents just one Ethernet port instead of all Ethernet ports on the NIC.
+ * All ports are used by default, but a subset can be selected using the ``port`` argument.
+ * Report firmware version.
+ * Common CESNET-NDK-based adapters have been added,
+ including the FB2CGHH (Silicom Denmark) and XpressSX AGI-FH400G (Reflex CES).
+
Removed Items
-------------
--
2.52.0
^ permalink raw reply related [flat|nested] 131+ messages in thread
* Re: [PATCH v4 0/8] net/nfb: rework to real multiport
2026-01-22 7:27 ` [PATCH v4 " spinler
` (7 preceding siblings ...)
2026-01-22 7:27 ` [PATCH v4 8/8] doc/nfb: cleanup and update guide spinler
@ 2026-01-23 1:00 ` Stephen Hemminger
2026-01-23 1:01 ` Stephen Hemminger
2026-01-23 1:14 ` Stephen Hemminger
10 siblings, 0 replies; 131+ messages in thread
From: Stephen Hemminger @ 2026-01-23 1:00 UTC (permalink / raw)
To: spinler; +Cc: dev
On Thu, 22 Jan 2026 08:27:11 +0100
spinler@cesnet.cz wrote:
> From: Martin Spinler <spinler@cesnet.cz>
>
> This series implements real multiport for better user experience.
>
> The existing driver creates one ethdev/port for one PCI device.
> As the CESNET-NDK based cards aren't capable to represent each
> Ethernet port by own PCI device, new driver implementation
> processes real port configuration from firmware/card and switches
> from rte_eth_dev_pci_generic_probe to multiple rte_eth_dev_create calls.
>
> ---
> v4:
> * Cleaned usage of sprintf
> * Updated commit message for doc cleanup
=== Patch Review: bundle-1675-nfb-multiport.mbox (via Claude) ===
# DPDK Patch Review: NFB Multi-port Support
## ERRORS
### 1. Commit Message - Missing Fixes Tag (Patch 1/8)
**File:** Patch 1/8 - "net/nfb: prepare for indirect queue mapping scheme"
**Issue:** The commit body states "This prepares queue mapping for port-aware driver implementation" which suggests this is fixing or preparing for something, but there's no explanation of what problem this solves. While not strictly a bug fix requiring a `Fixes:` tag, the commit message should better explain the rationale.
**Recommendation:** Expand the commit body to explain why the indirect mapping is needed before implementing multi-port support.
---
### 2. Forbidden Token - RTE_EXPORT_INTERNAL_SYMBOL (Patch 2/8)
**File:** drivers/net/nfb/nfb_ethdev.c (Line after nfb_eth_common_probe definition)
**Location:** Lines 667, 780
```c
RTE_EXPORT_INTERNAL_SYMBOL(nfb_eth_common_probe)
int
nfb_eth_common_probe(struct rte_device *device,
...
RTE_EXPORT_INTERNAL_SYMBOL(nfb_eth_common_remove)
int
nfb_eth_common_remove(struct rte_device *dev)
```
**Issue:** According to AGENTS.md, `__rte_internal` must appear **alone on the line** immediately preceding the return type, and only in header files. `RTE_EXPORT_INTERNAL_SYMBOL` is the wrong way to mark internal APIs.
**Required Fix:** Move the `__rte_internal` attribute to the function declarations in `nfb.h`:
```c
// In nfb.h
__rte_internal
int nfb_eth_common_probe(struct rte_device *device,
ethdev_bus_specific_init specific_init, void *specific_device,
struct nfb_init_params *params, int ep_index);
__rte_internal
int nfb_eth_common_remove(struct rte_device *dev);
```
And remove the `RTE_EXPORT_INTERNAL_SYMBOL` macros from the .c file.
---
### 3. Memory Leak on Error Path (Patch 2/8)
**File:** drivers/net/nfb/nfb_ethdev.c
**Function:** nfb_eth_common_probe
**Location:** Lines 713-735
```c
nc_ifc_map_info_create_ordinary(nfb_dev, ¶ms->map_info);
if (params->args != NULL && strlen(params->args) > 0) {
kvlist = rte_kvargs_parse(params->args, VALID_KEYS);
if (kvlist == NULL) {
NFB_LOG(ERR, "Failed to parse device arguments %s", params->args);
return -EINVAL; // ← Leaks map_info and nfb_dev
}
// ... more code ...
if (port_mask == 0) {
NFB_LOG(ERR, "Failed to parse device port argument");
return -EINVAL; // ← Leaks map_info and nfb_dev
}
}
```
**Issue:** Early returns leak `params->map_info` (allocated by `nc_ifc_map_info_create_ordinary`) and don't close `nfb_dev`.
**Required Fix:**
```c
if (kvlist == NULL) {
NFB_LOG(ERR, "Failed to parse device arguments %s", params->args);
ret = -EINVAL;
goto err_parse_args;
}
// ...
if (port_mask == 0) {
NFB_LOG(ERR, "Failed to parse device port argument");
ret = -EINVAL;
goto err_parse_args;
}
// At end of function, before existing cleanup:
err_parse_args:
nc_map_info_destroy(¶ms->map_info);
nfb_close(nfb_dev);
return ret;
```
---
### 4. Incorrect Line Length (Patch 2/8)
**File:** drivers/net/nfb/nfb_ethdev.c
**Location:** Line 802
```c
return nfb_eth_common_probe(&pci_dev->device, eth_dev_pci_specific_init, pci_dev, ¶ms,
```
**Issue:** This line exceeds 100 characters (appears to be ~105 characters based on context). C code lines must not exceed 100 characters.
**Required Fix:** Break the line appropriately:
```c
return nfb_eth_common_probe(&pci_dev->device,
eth_dev_pci_specific_init, pci_dev, ¶ms,
comp_dev_info.ep_index);
```
---
### 5. Missing Release Notes for API Changes (Patches 2-8)
**File:** doc/guides/rel_notes/release_26_03.rst
**Location:** Only appears in Patch 8/8
**Issue:** Per AGENTS.md: "Changes to existing APIs require release notes" and "New drivers or subsystems must have release notes." The release notes are added in patch 8/8, but significant driver behavior changes occur in patches 2-5. According to AGENTS.md, "Code and documentation must be updated atomically in same patch."
**Required Fix:** Move relevant portions of the release notes update to the patches where the changes actually occur:
- Patch 2/8: Note about one ethdev per port
- Patch 4/8: Note about the `port` argument
- Patch 7/8: Note about firmware version reporting
---
## WARNINGS
### 1. Commit Message Body - Starts with "It" (Patch 1/8)
**Issue:** Per AGENTS.md, commit message bodies must not start with "It". While this patch doesn't explicitly start with "It", the body is minimal and doesn't provide sufficient context.
**Current:**
```
This prepares queue mapping for port-aware driver implementation.
```
**Recommendation:**
```
Introduce indirect queue mapping to support multiple ethdev ports.
The NFB driver is being enhanced to create one ethdev per physical
Ethernet port instead of one ethdev per PCI device. This requires
an indirect mapping layer because DPDK queue indices no longer
directly correspond to firmware queue indices.
This change adds queue_map_rx and queue_map_tx arrays to track the
mapping between DPDK queue indices and firmware queue IDs, preparing
for the multi-port implementation in subsequent patches.
```
---
### 2. Subject Line Length (Patch 2/8)
**File:** Patch 2/8 subject
**Current:** "net/nfb: create ethdev for every eth port/channel" (50 chars)
**Issue:** While under the 60-character limit, the subject could be more descriptive about the impact.
**Recommendation:** Consider:
```
net/nfb: create one ethdev per ethernet port
```
(More clear about the 1:1 relationship)
---
### 3. Inappropriate Use of rte_zmalloc (Patch 2/8)
**File:** drivers/net/nfb/nfb_ethdev.c
**Location:** Line 585
```c
priv->queue_map_rx = rte_calloc("NFB queue map", (max_rx_queues + max_tx_queues),
sizeof(*priv->queue_map_rx), 0);
```
**Issue:** Per AGENTS.md "Appropriate Use of rte_malloc()": Use `rte_malloc()` only for DMA-accessible memory or memory shared between processes. Control structures should use standard `malloc()`.
**Reasoning:** Queue mapping arrays are control plane data structures that don't require hugepage memory. Using `rte_calloc` wastes limited hugepage resources.
**Recommendation:**
```c
priv->queue_map_rx = calloc((max_rx_queues + max_tx_queues),
sizeof(*priv->queue_map_rx));
```
And use `free()` in cleanup path instead of `rte_free()`.
---
### 4. Unnecessary NULL Check Before free (Patches 1/8, 5/8)
**File:** drivers/net/nfb/nfb_ethdev.c
**Location:** Multiple locations
**Patch 1/8, Line 655:**
```c
if (rte_eal_process_type() == RTE_PROC_PRIMARY)
rte_free(priv->queue_map_rx);
```
**Issue:** The `rte_eal_process_type()` check is appropriate (different process model), but similar patterns elsewhere may be checking for NULL unnecessarily.
**Patch 5/8, Lines 118-122:**
```c
for (i = 0; i < priv->max_rxmac; i++)
nc_rxmac_close(priv->rxmac[i]);
rte_free(priv->rxmac);
```
**Potential Issue:** If any `priv->rxmac[i]` could be NULL, the code should check before calling `nc_rxmac_close`, but this depends on `nc_rxmac_close` behavior (not visible in patch).
**Recommendation:** Verify that `nc_rxmac_close` and `nc_txmac_close` handle NULL pointers safely, or add NULL checks:
```c
for (i = 0; i < priv->max_rxmac; i++) {
if (priv->rxmac[i] != NULL)
nc_rxmac_close(priv->rxmac[i]);
}
```
---
### 5. Missing Documentation for New Struct Member (Patch 1/8)
**File:** drivers/net/nfb/nfb.h
**Location:** Lines 68-69
```c
struct pmd_priv {
uint16_t max_rx_queues;
uint16_t max_tx_queues;
int *queue_map_rx;
int *queue_map_tx;
};
```
**Issue:** New fields lack Doxygen-style comments explaining their purpose, while the file shows other structures are documented.
**Recommendation:**
```c
struct pmd_priv {
uint16_t max_rx_queues;
uint16_t max_tx_queues;
/** Mapping from DPDK RX queue index to firmware queue ID */
int *queue_map_rx;
/** Mapping from DPDK TX queue index to firmware queue ID */
int *queue_map_tx;
};
```
---
### 6. Global Variable Without Unique Prefix (Patch 2/8)
**File:** drivers/net/nfb/nfb_ethdev.c
**Location:** Lines 28-29
```c
TAILQ_HEAD(pmd_internals_head, pmd_internals);
static struct pmd_internals_head nfb_eth_dev_list =
TAILQ_HEAD_INITIALIZER(nfb_eth_dev_list);
```
**Issue:** Per AGENTS.md "Symbol Naming for Static Linking": While this is a `static` variable (file scope) and doesn't strictly violate the guideline, the type name `pmd_internals_head` is generic and could clash if the static is removed in the future.
**Recommendation:** Use more specific names:
```c
TAILQ_HEAD(nfb_pmd_internals_head, pmd_internals);
static struct nfb_pmd_internals_head nfb_eth_dev_list =
TAILQ_HEAD_INITIALIZER(nfb_eth_dev_list);
```
---
### 7. Inconsistent Error Handling Pattern (Patch 4/8)
**File:** drivers/net/nfb/nfb_ethdev.c
**Function:** fill_port_mask
**Location:** Lines 672-686
```c
static int fill_port_mask(const char *key __rte_unused, const char *value, void *opaque)
{
char *end;
uint64_t *port_mask = opaque;
unsigned long port;
if (value == NULL || strlen(value) == 0 || !isdigit(*value))
return -1;
port = strtoul(value, &end, 0);
if (*end != '\0' || port >= ULONG_WIDTH)
return -1;
```
**Issue:** Function returns generic `-1` instead of standard error codes like `-EINVAL`. This is inconsistent with DPDK style where functions typically return `-errno` values.
**Recommendation:**
```c
if (value == NULL || strlen(value) == 0 || !isdigit(*value))
return -EINVAL;
port = strtoul(value, &end, 0);
if (*end != '\0' || port >= ULONG_WIDTH)
return -EINVAL;
```
---
### 8. Potential Integer Overflow (Patch 4/8)
**File:** drivers/net/nfb/nfb_ethdev.c
**Location:** Line 728
```c
if (ret || port_mask >= RTE_BIT64(params->map_info.ifc_cnt))
port_mask = 0;
```
**Issue:** If `params->map_info.ifc_cnt >= 64`, then `RTE_BIT64(params->map_info.ifc_cnt)` will overflow (undefined behavior for shift >= 64).
**Recommendation:**
```c
if (ret || params->map_info.ifc_cnt > 64 ||
(params->map_info.ifc_cnt < 64 && port_mask >= RTE_BIT64(params->map_info.ifc_cnt)))
port_mask = 0;
```
Or add a check earlier:
```c
if (params->map_info.ifc_cnt > 64) {
NFB_LOG(ERR, "Too many interfaces: %u", params->map_info.ifc_cnt);
return -EINVAL;
}
```
---
### 9. Documentation Not Matching Code (Patch 8/8)
**File:** doc/guides/nics/nfb.rst
**Location:** Lines 58-60
```rst
.. note::
Normally, one port corresponds to one channel, but ports can often be configured in a separate
manner. For example one 100G port can be used as 4x25G or 4x10G independent Ethernet channels.
```
**Issue:** The documentation states "Normally, one port corresponds to one channel" but doesn't explain what a "channel" is in the context of the firmware vs DPDK ports, which could confuse users.
**Recommendation:** Clarify the terminology:
```rst
.. note::
The driver creates one DPDK port for each firmware Ethernet interface. Some cards support
port breakout, where a physical port can be reconfigured (e.g., 1x100G to 4x25G). Each
resulting channel appears as a separate DPDK port.
```
---
### 10. Missing NULL Checks After rte_zmalloc (Patch 2/8)
**File:** drivers/net/nfb/nfb_ethdev.c
**Location:** Line 537
```c
internals = rte_zmalloc_socket("nfb_internals",
sizeof(struct pmd_internals), RTE_CACHE_LINE_SIZE,
rte_socket_id());
if (internals == NULL) {
NFB_LOG(ERR, "Failed to allocate private data");
ret = -ENOMEM;
goto err_nfb_open;
}
```
**Issue:** The error path `err_nfb_open` doesn't check if `internals` is NULL before potentially dereferencing it in cleanup. While this specific code path is safe, it's worth noting for consistency.
**Status:** Currently safe, but should be verified if cleanup code changes.
---
## INFO
### 1. Code Style - Variable Declaration at Point of Use (Multiple Patches)
**Observation:** The patches use both C89-style (declarations at start of block) and C99-style (declarations at point of use) variable declarations.
**Example (Patch 1/8):**
```c
int ret;
int qid;
struct ndp_rx_queue *rxq;
```
vs.
**Example (Patch 5/8):**
```c
for (i = 0, j = 0
^ permalink raw reply [flat|nested] 131+ messages in thread
* Re: [PATCH v4 0/8] net/nfb: rework to real multiport
2026-01-22 7:27 ` [PATCH v4 " spinler
` (8 preceding siblings ...)
2026-01-23 1:00 ` [PATCH v4 0/8] net/nfb: rework to real multiport Stephen Hemminger
@ 2026-01-23 1:01 ` Stephen Hemminger
2026-01-23 1:14 ` Stephen Hemminger
10 siblings, 0 replies; 131+ messages in thread
From: Stephen Hemminger @ 2026-01-23 1:01 UTC (permalink / raw)
To: spinler; +Cc: dev
On Thu, 22 Jan 2026 08:27:11 +0100
spinler@cesnet.cz wrote:
> From: Martin Spinler <spinler@cesnet.cz>
>
> This series implements real multiport for better user experience.
>
> The existing driver creates one ethdev/port for one PCI device.
> As the CESNET-NDK based cards aren't capable to represent each
> Ethernet port by own PCI device, new driver implementation
> processes real port configuration from firmware/card and switches
> from rte_eth_dev_pci_generic_probe to multiple rte_eth_dev_create calls.
>
> ---
> v4:
> * Cleaned usage of sprintf
> * Updated commit message for doc cleanup
Second AI opinion:
=== Patch Review: bundle-1675-nfb-multiport.mbox (via Grok) ===
### Review of DPDK Patch Series 'bundle-1675-nfb-multiport.mbox'
Below is a detailed review of the patch series against the DPDK coding standards and contribution guidelines as outlined in `AGENTS.md`. The feedback is organized by severity level: **Error**, **Warning**, and **Info**.
---
### **Errors (Must Fix)**
1. **Commit Message Body Missing for All Patches (All 8 Patches)**
- **Issue**: The commit messages for all patches lack a detailed body describing the issue being fixed or the feature being added. According to the guidelines, the commit body must provide context for reviewers, describe the changes, and should not start with "It". Currently, the body only contains the `Signed-off-by` line after a brief one-liner in some cases.
- **Location**: All patches (1/8 to 8/8).
- **Action**: Add a detailed commit message body for each patch explaining the purpose, context, and impact of the changes. Ensure the body is wrapped at 75 characters.
2. **Missing Release Notes for Significant Driver Changes (Patches 2/8, 3/8, 4/8, 5/8)**
- **Issue**: Patches introducing significant driver changes (multi-port support, VDEV support, port argument, MAC initialization per device) do not update the release notes adequately except for patch 8/8. The guidelines require release notes for API changes, new drivers, or significant subsystem updates to be updated in the current release notes file.
- **Location**: Patches 2/8, 3/8, 4/8, 5/8.
- **Action**: Ensure that significant changes are documented in the release notes file for the current release. Patch 8/8 updates `release_26_03.rst`, which is good, but earlier patches should also reference or contribute to this update if they are part of the same series.
3. **Missing Tests for New Features (Patches 2/8, 3/8, 4/8, 7/8)**
- **Issue**: New features such as multi-port support, VDEV probing, port selection argument, and firmware version reporting do not mention accompanying tests in the `app/test` directory or hooks in `app/testpmd`. The guidelines mandate that new APIs and significant features must have corresponding tests and `testpmd` hooks.
- **Location**: Patches 2/8, 3/8, 4/8, 7/8.
- **Action**: Add unit tests in the `app/test` directory using the `TEST_ASSERT` macros and `unit_test_suite_runner` infrastructure. Ensure `testpmd` hooks are implemented for new features like port selection and firmware version reporting.
---
### **Warnings (Should Fix)**
1. **Documentation Gaps and Mismatches (Patches 2/8, 3/8, 4/8, 7/8)**
- **Issue**: While patch 8/8 updates the documentation, earlier patches introducing new features (multi-port, VDEV, port argument, firmware version) do not update documentation atomically. The guidelines require that code and documentation updates be in the same patch. Additionally, ensure that PMD features match the `doc/guides/nics/features/` matrix.
- **Location**: Patches 2/8, 3/8, 4/8, 7/8.
- **Action**: Move relevant documentation updates into the respective patches introducing the features. Verify that `doc/guides/nics/features/nfb.ini` reflects all new features (e.g., multi-port support, VDEV) as introduced. Ensure device operations are documented per `features.rst` mappings.
2. **New APIs Not Marked as `__rte_experimental` (Patches 2/8, 3/8, 4/8, 7/8)**
- **Issue**: New functions such as `nfb_eth_common_probe`, `nfb_eth_common_remove`, `nfb_eth_fw_version_get`, and others introduced in the patches are not marked as `__rte_experimental` in header files. The guidelines require new APIs to be marked as experimental.
- **Location**: `drivers/net/nfb/nfb.h` and related patches (2/8, 3/8, 4/8, 7/8).
- **Action**: Mark new public APIs as `__rte_experimental` in header files, ensuring the tag is alone on the line preceding the return type.
3. **Inappropriate Use of `rte_calloc` and `rte_zmalloc` (All Patches)**
- **Issue**: The patches use `rte_calloc` and `rte_zmalloc` for allocations that do not necessarily require hugepage memory (e.g., `priv->queue_map_rx` in patch 1/8, `internals` in patch 2/8). The guidelines state that `rte_malloc` should only be used for DMA-accessible memory or shared memory between processes.
- **Location**: Multiple locations across patches (e.g., `drivers/net/nfb/nfb_ethdev.c` in patch 1/8, line 579; patch 2/8, line 542).
- **Action**: Replace `rte_calloc` and `rte_zmalloc` with standard `calloc` or `malloc` for control path allocations unless hugepage memory is explicitly required for DMA or multi-process sharing.
4. **Driver Global Variables Without Unique Prefixes (All Patches)**
- **Issue**: Global variables and structures (e.g., `nfb_eth_dev_list` in patch 2/8) do not use a unique driver-specific prefix (e.g., `nfb_`). The guidelines require unique prefixes for global symbols to avoid clashes during static linking.
- **Location**: `drivers/net/nfb/nfb_ethdev.c` (patch 2/8, line 29).
- **Action**: Add a driver-specific prefix (e.g., `nfb_`) to global variables and structures to prevent namespace collisions.
5. **Missing `Cc: stable@dpdk.org` for Potential Backports (Patches 1/8, 6/8)**
- **Issue**: Patches that could be candidates for stable backports (e.g., queue mapping preparation in 1/8, PCI ID updates in 6/8) do not include `Cc: stable@dpdk.org` as required by the guidelines.
- **Location**: Commit messages of patches 1/8 and 6/8.
- **Action**: Add `Cc: stable@dpdk.org` to commit messages for patches that fix issues or add compatibility and are suitable for backporting.
6. **Use of `snprintf` Return Value Without Proper Check (Patches 2/8, 3/8, 4/8)**
- **Issue**: The return value of `snprintf` is checked for overflow in some cases but not consistently handled for errors (e.g., negative return values). The guidelines encourage explicit error handling.
- **Location**: `drivers/net/nfb/nfb_ethdev.c` (patch 2/8, line 781); `drivers/net/nfb/nfb_vdev.c` (patch 3/8, line 67).
- **Action**: Ensure `snprintf` return values are checked for both overflow and error conditions (< 0).
---
### **Info (Consider)**
1. **Minor Style Preferences for Commit Messages (All Patches)**
- **Issue**: While the subject lines are generally within 60 characters and follow the correct prefix (`net/nfb:`), some could be more descriptive (e.g., patch 6/8: "add compatible cards to driver PCI ID table" could specify key cards like "add FB2CGHH and COMBO-400G1 to PCI ID table").
- **Location**: Commit messages of all patches.
- **Action**: Consider refining subject lines for clarity and specificity while maintaining the length limit and imperative mood.
2. **Optimization Suggestion for Queue Mapping Initialization (Patch 1/8)**
- **Issue**: The initialization of `queue_map_rx` and `queue_map_tx` uses a simple 1:1 mapping loop. This could potentially be optimized using `memcpy` if the mapping is always linear in the default case.
- **Location**: `drivers/net/nfb/nfb_ethdev.c` (patch 1/8, lines 587-590).
- **Action**: Consider using `memcpy` or a similar approach for initializing large arrays if performance is a concern in this path (though this is a control path, so impact may be negligible).
3. **Alternative Approach for Port Selection Logic (Patch 4/8)**
- **Issue**: The port selection logic using a bitmask (`port_mask`) is functional but could potentially be simplified by using a dynamic array or list for selected ports if the number of ports grows significantly in future hardware.
- **Location**: `drivers/net/nfb/nfb_ethdev.c` (patch 4/8, lines 694-718).
- **Action**: Consider if a more flexible data structure (e.g., a dynamic array) would be better for future scalability, though the current bitmask approach is acceptable for the current scope.
4. **Code Duplication in MAC Initialization (Patch 5/8)**
- **Issue**: The `nfb_nc_rxmac_init` and `nfb_nc_txmac_init` functions have similar logic for iterating over interfaces and initializing MACs. This could be refactored into a common helper function to reduce duplication.
- **Location**: `drivers/net/nfb/nfb_ethdev.c` (patch 5/8, lines 54-75 and 79-100).
- **Action**: Consider refactoring the common logic into a helper function to improve maintainability.
---
### **Summary of Compliance with Guidelines**
- **Commit Message Format**: Non-compliant due to missing detailed bodies (Error). Subject lines are mostly compliant with minor improvements suggested (Info).
- **License/Copyright Compliance**: Compliant. All new files (e.g., `nfb_vdev.c` in patch 3/8) include proper SPDX identifiers and copyright notices.
- **C Coding Style Issues**: Mostly compliant with minor issues around `rte_malloc` usage and global variable naming (Warnings).
- **API and Documentation Requirements**: Non-compliant due to missing `__rte_experimental` tags, incomplete documentation updates in individual patches, and missing tests (Errors and Warnings).
- **Other Guideline Violations**: Missing release notes updates in relevant patches and potential backport tags (Errors and Warnings).
---
### **Action Plan for Contributors**
1. **Immediate Fixes (Errors)**:
- Add detailed commit message bodies to all patches.
- Ensure release notes are updated atomically with feature patches or reference the final documentation patch (8/8) explicitly.
- Add tests and `testpmd` hooks for new features.
2. **High-Priority Improvements (Warnings)**:
- Update documentation in the same patch as code changes.
- Mark new APIs as `__rte_experimental`.
- Replace inappropriate `rte_malloc`/`rte_calloc` usage with standard `malloc`/`calloc`.
- Add driver-specific prefixes to global variables.
- Add `Cc: stable@dpdk.org` where applicable.
3. **Optional Enhancements (Info)**:
- Refine commit message subject lines for clarity.
- Consider optimization and refactoring suggestions for maintainability and scalability.
Please address the **Errors** and **Warnings** before resubmission to ensure compliance with DPDK guidelines. If you have any questions or need further clarification on specific issues, feel free to ask.
^ permalink raw reply [flat|nested] 131+ messages in thread
* Re: [PATCH v4 0/8] net/nfb: rework to real multiport
2026-01-22 7:27 ` [PATCH v4 " spinler
` (9 preceding siblings ...)
2026-01-23 1:01 ` Stephen Hemminger
@ 2026-01-23 1:14 ` Stephen Hemminger
2026-01-23 17:34 ` Martin Spinler
10 siblings, 1 reply; 131+ messages in thread
From: Stephen Hemminger @ 2026-01-23 1:14 UTC (permalink / raw)
To: spinler; +Cc: dev
On Thu, 22 Jan 2026 08:27:11 +0100
spinler@cesnet.cz wrote:
> From: Martin Spinler <spinler@cesnet.cz>
>
> This series implements real multiport for better user experience.
>
> The existing driver creates one ethdev/port for one PCI device.
> As the CESNET-NDK based cards aren't capable to represent each
> Ethernet port by own PCI device, new driver implementation
> processes real port configuration from firmware/card and switches
> from rte_eth_dev_pci_generic_probe to multiple rte_eth_dev_create calls.
>
> ---
Wading through all the AI review...
Things you should fix:
- make sure all calls to snprintf() have overflow checks
- clarity about what should be backported (Fixes/stable)
- make sure all calls to rte_malloc et al have checks for NULL
Things you could fix:
- using malloc vs rte_malloc for data structures not shared
not a big issue; but feel free to change.
- avoiding code duplication
- check prefix of global variables
- mark driver only API's as __rte_internal
Things I can fix when merging:
- am willing to reword commit messages for readability as needed
Things I don't care about:
- release notes only have to be right after series, no need for per-patch
- looks like all new functions are internal only, not sure why review wants tests.
- assume 64 bit port mask is a hardware limit, you won't go over in future.
- driver internal structures do not need doxygen comments
- don't care about any AI warnings like "if you change X in the future it will break"
^ permalink raw reply [flat|nested] 131+ messages in thread
* [PATCH v5 0/8] net/nfb: rework to real multiport
2026-01-15 15:16 [PATCH 0/8] net/nfb: rework to real multiport spinler
` (11 preceding siblings ...)
2026-01-22 7:27 ` [PATCH v4 " spinler
@ 2026-01-23 17:22 ` spinler
2026-01-23 17:22 ` [PATCH v5 1/8] net/nfb: prepare for indirect queue mapping scheme spinler
` (8 more replies)
2026-02-02 15:34 ` [PATCH v6 " spinler
` (4 subsequent siblings)
17 siblings, 9 replies; 131+ messages in thread
From: spinler @ 2026-01-23 17:22 UTC (permalink / raw)
To: dev; +Cc: Martin Spinler
From: Martin Spinler <spinler@cesnet.cz>
This series implements real multiport for better user experience.
The existing driver creates one ethdev/port for one PCI device.
As the CESNET-NDK based cards aren't capable to represent each
Ethernet port by own PCI device, new driver implementation
processes real port configuration from firmware/card and switches
from rte_eth_dev_pci_generic_probe to multiple rte_eth_dev_create calls.
---
v5:
* Fixed memory leak on error path.
* Separated nfb_eth_dev_create_for_ifc() from nfb_eth_common_probe().
* Reworked port matching to eliminate the port mask / 64b width and potential integer overflow.
The nfb_eth_dev_create_for_ifc() is now called directly from rte_kvargs_process().
* Renamed nfb_init_params to nfb_probe_params and updated the nfb_eth_common_probe() header.
* Unified Rx+Tx MAC init and deinit functions to avoid code duplication.
* calloc/free is now used instead of rte_calloc in Rx+Tx MAC init/deinit.
* The return values of snprintf are now handled properly.
* The return value of Rx+Tx MAC init is now handled properly.
* The name of the struct type is now prefixed with nfb_.
* Used a consistent error handling pattern (-EINVAL instead of -1).
* Added descriptions to new struct members.
* Some commit messages have been updated with better descriptions.
Depends-on: series-37064 ("net/nfb: code cleanup")
Martin Spinler (8):
net/nfb: prepare for indirect queue mapping scheme
net/nfb: create one ethdev per ethernet port
net/nfb: add vdev as alternative device probe method
net/nfb: add device argument "port" to limit used ports
net/nfb: init only MACs associated with device
net/nfb: add compatible cards to driver PCI ID table
net/nfb: report firmware version
doc/nfb: cleanup and update guide
doc/guides/nics/features/nfb.ini | 4 +
doc/guides/nics/nfb.rst | 187 +++++------
doc/guides/rel_notes/release_26_03.rst | 9 +
drivers/net/nfb/meson.build | 1 +
drivers/net/nfb/nfb.h | 56 +++-
drivers/net/nfb/nfb_ethdev.c | 444 ++++++++++++++++++-------
drivers/net/nfb/nfb_rx.c | 30 +-
drivers/net/nfb/nfb_rx.h | 9 +-
drivers/net/nfb/nfb_tx.c | 27 +-
drivers/net/nfb/nfb_tx.h | 7 +-
drivers/net/nfb/nfb_vdev.c | 107 ++++++
11 files changed, 624 insertions(+), 257 deletions(-)
create mode 100644 drivers/net/nfb/nfb_vdev.c
--
2.52.0
^ permalink raw reply [flat|nested] 131+ messages in thread
* [PATCH v5 1/8] net/nfb: prepare for indirect queue mapping scheme
2026-01-23 17:22 ` [PATCH v5 " spinler
@ 2026-01-23 17:22 ` spinler
2026-01-23 17:22 ` [PATCH v5 2/8] net/nfb: create one ethdev per ethernet port spinler
` (7 subsequent siblings)
8 siblings, 0 replies; 131+ messages in thread
From: spinler @ 2026-01-23 17:22 UTC (permalink / raw)
To: dev; +Cc: Martin Spinler
From: Martin Spinler <spinler@cesnet.cz>
The NFB driver is being enhanced to create one ethdev per physical
Ethernet port instead of one ethdev per PCI device. This requires
an indirect mapping layer because DPDK queue indices no longer
directly correspond to firmware queue indices.
This change adds queue_map_rx and queue_map_tx arrays to track the
mapping between DPDK queue indices and firmware queue IDs, preparing
for the multi-port implementation in subsequent patches.
Signed-off-by: Martin Spinler <spinler@cesnet.cz>
---
drivers/net/nfb/nfb.h | 5 +++++
drivers/net/nfb/nfb_ethdev.c | 21 +++++++++++++++++++++
drivers/net/nfb/nfb_rx.c | 30 +++++++++++++++---------------
drivers/net/nfb/nfb_rx.h | 9 ++++-----
drivers/net/nfb/nfb_tx.c | 27 +++++++++++++++------------
drivers/net/nfb/nfb_tx.h | 7 +++----
6 files changed, 63 insertions(+), 36 deletions(-)
diff --git a/drivers/net/nfb/nfb.h b/drivers/net/nfb/nfb.h
index 90b04c6151..6b74b3e6ec 100644
--- a/drivers/net/nfb/nfb.h
+++ b/drivers/net/nfb/nfb.h
@@ -60,6 +60,11 @@ struct pmd_internals {
struct pmd_priv {
uint16_t max_rx_queues;
uint16_t max_tx_queues;
+
+ /** Mapping from DPDK RX queue index to firmware queue ID */
+ int *queue_map_rx;
+ /** Mapping from DPDK TX queue index to firmware queue ID */
+ int *queue_map_tx;
};
#endif /* _NFB_H_ */
diff --git a/drivers/net/nfb/nfb_ethdev.c b/drivers/net/nfb/nfb_ethdev.c
index 947ee9e21d..803815138c 100644
--- a/drivers/net/nfb/nfb_ethdev.c
+++ b/drivers/net/nfb/nfb_ethdev.c
@@ -509,6 +509,7 @@ static const struct eth_dev_ops ops = {
static int
nfb_eth_dev_init(struct rte_eth_dev *dev)
{
+ int i;
int ret;
uint32_t mac_count;
struct rte_eth_dev_data *data = dev->data;
@@ -577,6 +578,20 @@ nfb_eth_dev_init(struct rte_eth_dev *dev)
priv->max_rx_queues = max_rx_queues;
priv->max_tx_queues = max_tx_queues;
+ priv->queue_map_rx = rte_calloc("NFB queue map", (max_rx_queues + max_tx_queues),
+ sizeof(*priv->queue_map_rx), 0);
+ if (priv->queue_map_rx == NULL) {
+ ret = -ENOMEM;
+ goto err_alloc_queue_map;
+ }
+ priv->queue_map_tx = priv->queue_map_rx + max_rx_queues;
+
+ /* default queue mapping is 1:1 */
+ for (i = 0; i < max_rx_queues; i++)
+ priv->queue_map_rx[i] = i;
+ for (i = 0; i < max_tx_queues; i++)
+ priv->queue_map_tx[i] = i;
+
/* Allocate space for MAC addresses */
mac_count = nfb_eth_get_max_mac_address_count(dev);
data->mac_addrs = rte_zmalloc(data->name,
@@ -608,6 +623,8 @@ nfb_eth_dev_init(struct rte_eth_dev *dev)
return 0;
err_malloc_mac_addrs:
+ rte_free(priv->queue_map_rx);
+err_alloc_queue_map:
nfb_nc_rxmac_deinit(internals->rxmac, internals->max_rxmac);
nfb_nc_txmac_deinit(internals->txmac, internals->max_txmac);
nfb_close(internals->nfb);
@@ -633,6 +650,10 @@ nfb_eth_dev_uninit(struct rte_eth_dev *dev)
struct rte_pci_device *pci_dev = RTE_ETH_DEV_TO_PCI(dev);
struct rte_pci_addr *pci_addr = &pci_dev->addr;
struct pmd_internals *internals = dev->process_private;
+ struct pmd_priv *priv = dev->data->dev_private;
+
+ if (rte_eal_process_type() == RTE_PROC_PRIMARY)
+ rte_free(priv->queue_map_rx);
nfb_nc_rxmac_deinit(internals->rxmac, internals->max_rxmac);
nfb_nc_txmac_deinit(internals->txmac, internals->max_txmac);
diff --git a/drivers/net/nfb/nfb_rx.c b/drivers/net/nfb/nfb_rx.c
index 413d275853..ff630f32f4 100644
--- a/drivers/net/nfb/nfb_rx.c
+++ b/drivers/net/nfb/nfb_rx.c
@@ -61,12 +61,13 @@ nfb_eth_rx_queue_setup(struct rte_eth_dev *dev,
struct rte_mempool *mb_pool)
{
struct pmd_internals *internals = dev->process_private;
+ struct pmd_priv *priv = dev->data->dev_private;
- struct ndp_rx_queue *rxq;
int ret;
+ int qid;
+ struct ndp_rx_queue *rxq;
- rxq = rte_zmalloc_socket("ndp rx queue",
- sizeof(struct ndp_rx_queue),
+ rxq = rte_zmalloc_socket("ndp rx queue", sizeof(struct ndp_rx_queue),
RTE_CACHE_LINE_SIZE, socket_id);
if (rxq == NULL) {
@@ -77,23 +78,23 @@ nfb_eth_rx_queue_setup(struct rte_eth_dev *dev,
rxq->flags = 0;
- ret = nfb_eth_rx_queue_init(internals->nfb,
- rx_queue_id,
- dev->data->port_id,
- mb_pool,
- rxq);
+ qid = priv->queue_map_rx[rx_queue_id];
- if (ret == 0)
- dev->data->rx_queues[rx_queue_id] = rxq;
- else
- rte_free(rxq);
+ ret = nfb_eth_rx_queue_init(internals->nfb, qid, dev->data->port_id, mb_pool, rxq);
+ if (ret)
+ goto err_queue_init;
+ dev->data->rx_queues[rx_queue_id] = rxq;
+ return 0;
+
+err_queue_init:
+ rte_free(rxq);
return ret;
}
int
nfb_eth_rx_queue_init(struct nfb_device *nfb,
- uint16_t rx_queue_id,
+ int qid,
uint16_t port_id,
struct rte_mempool *mb_pool,
struct ndp_rx_queue *rxq)
@@ -104,12 +105,11 @@ nfb_eth_rx_queue_init(struct nfb_device *nfb,
if (nfb == NULL)
return -EINVAL;
- rxq->queue = ndp_open_rx_queue(nfb, rx_queue_id);
+ rxq->queue = ndp_open_rx_queue(nfb, qid);
if (rxq->queue == NULL)
return -EINVAL;
rxq->nfb = nfb;
- rxq->rx_queue_id = rx_queue_id;
rxq->in_port = port_id;
rxq->mb_pool = mb_pool;
rxq->buf_size = (uint16_t)(mbp_priv->mbuf_data_room_size -
diff --git a/drivers/net/nfb/nfb_rx.h b/drivers/net/nfb/nfb_rx.h
index 67b3b00e2a..831945c577 100644
--- a/drivers/net/nfb/nfb_rx.h
+++ b/drivers/net/nfb/nfb_rx.h
@@ -30,7 +30,6 @@ nfb_timestamp_dynfield(struct rte_mbuf *mbuf)
struct ndp_rx_queue {
struct nfb_device *nfb; /* nfb dev structure */
struct ndp_queue *queue; /* rx queue */
- uint16_t rx_queue_id; /* index */
uint8_t in_port; /* port */
uint8_t flags; /* setup flags */
@@ -47,8 +46,8 @@ struct ndp_rx_queue {
*
* @param nfb
* Pointer to nfb device structure.
- * @param rx_queue_id
- * RX queue index.
+ * @param qid
+ * RX queue ID.
* @param port_id
* Device [external] port identifier.
* @param mb_pool
@@ -60,7 +59,7 @@ struct ndp_rx_queue {
*/
int
nfb_eth_rx_queue_init(struct nfb_device *nfb,
- uint16_t rx_queue_id,
+ int qid,
uint16_t port_id,
struct rte_mempool *mb_pool,
struct ndp_rx_queue *rxq);
@@ -70,7 +69,7 @@ nfb_eth_rx_queue_init(struct nfb_device *nfb,
*
* @param dev
* Pointer to Ethernet device structure.
- * @param idx
+ * @param rx_queue_id
* RX queue index.
* @param desc
* Number of descriptors to configure in queue.
diff --git a/drivers/net/nfb/nfb_tx.c b/drivers/net/nfb/nfb_tx.c
index 1f997ce22f..b5cbedeaf6 100644
--- a/drivers/net/nfb/nfb_tx.c
+++ b/drivers/net/nfb/nfb_tx.c
@@ -54,11 +54,13 @@ nfb_eth_tx_queue_setup(struct rte_eth_dev *dev,
const struct rte_eth_txconf *tx_conf __rte_unused)
{
struct pmd_internals *internals = dev->process_private;
+ struct pmd_priv *priv = dev->data->dev_private;
+
int ret;
+ int qid;
struct ndp_tx_queue *txq;
- txq = rte_zmalloc_socket("ndp tx queue",
- sizeof(struct ndp_tx_queue),
+ txq = rte_zmalloc_socket("ndp tx queue", sizeof(struct ndp_tx_queue),
RTE_CACHE_LINE_SIZE, socket_id);
if (txq == NULL) {
@@ -67,32 +69,33 @@ nfb_eth_tx_queue_setup(struct rte_eth_dev *dev,
return -ENOMEM;
}
- ret = nfb_eth_tx_queue_init(internals->nfb,
- tx_queue_id,
- txq);
+ qid = priv->queue_map_tx[tx_queue_id];
- if (ret == 0)
- dev->data->tx_queues[tx_queue_id] = txq;
- else
- rte_free(txq);
+ ret = nfb_eth_tx_queue_init(internals->nfb, qid, txq);
+ if (ret)
+ goto err_queue_init;
+ dev->data->tx_queues[tx_queue_id] = txq;
+ return 0;
+
+err_queue_init:
+ rte_free(txq);
return ret;
}
int
nfb_eth_tx_queue_init(struct nfb_device *nfb,
- uint16_t tx_queue_id,
+ int qid,
struct ndp_tx_queue *txq)
{
if (nfb == NULL)
return -EINVAL;
- txq->queue = ndp_open_tx_queue(nfb, tx_queue_id);
+ txq->queue = ndp_open_tx_queue(nfb, qid);
if (txq->queue == NULL)
return -EINVAL;
txq->nfb = nfb;
- txq->tx_queue_id = tx_queue_id;
txq->tx_pkts = 0;
txq->tx_bytes = 0;
diff --git a/drivers/net/nfb/nfb_tx.h b/drivers/net/nfb/nfb_tx.h
index f107cf914b..c253af1a86 100644
--- a/drivers/net/nfb/nfb_tx.h
+++ b/drivers/net/nfb/nfb_tx.h
@@ -17,7 +17,6 @@
struct ndp_tx_queue {
struct nfb_device *nfb; /* nfb dev structure */
struct ndp_queue *queue; /* tx queue */
- uint16_t tx_queue_id; /* index */
volatile uint64_t tx_pkts; /* packets transmitted */
volatile uint64_t tx_bytes; /* bytes transmitted */
volatile uint64_t err_pkts; /* erroneous packets */
@@ -54,8 +53,8 @@ nfb_eth_tx_queue_setup(struct rte_eth_dev *dev,
*
* @param nfb
* Pointer to nfb device structure.
- * @param tx_queue_id
- * TX queue index.
+ * @param qid
+ * TX queue ID.
* @param[out] txq
* Pointer to ndp_tx_queue output structure
*
@@ -64,7 +63,7 @@ nfb_eth_tx_queue_setup(struct rte_eth_dev *dev,
*/
int
nfb_eth_tx_queue_init(struct nfb_device *nfb,
- uint16_t tx_queue_id,
+ int qid,
struct ndp_tx_queue *txq);
/**
--
2.52.0
^ permalink raw reply related [flat|nested] 131+ messages in thread
* [PATCH v5 2/8] net/nfb: create one ethdev per ethernet port
2026-01-23 17:22 ` [PATCH v5 " spinler
2026-01-23 17:22 ` [PATCH v5 1/8] net/nfb: prepare for indirect queue mapping scheme spinler
@ 2026-01-23 17:22 ` spinler
2026-01-27 0:37 ` Stephen Hemminger
2026-01-23 17:22 ` [PATCH v5 3/8] net/nfb: add vdev as alternative device probe method spinler
` (6 subsequent siblings)
8 siblings, 1 reply; 131+ messages in thread
From: spinler @ 2026-01-23 17:22 UTC (permalink / raw)
To: dev; +Cc: Martin Spinler
From: Martin Spinler <spinler@cesnet.cz>
The existing driver creates one ethdev/port for one PCI device.
As the CESNET-NDK-based cards are mostly unable to represent
each physical port with one PCI device, this atypical behaviour
often limits the user.
The nfb-framework comes with generic port representation API,
which provides information about ports and their mapping to firmware
resource (physical ports, DMA queues, processing pipelines).
This driver design switches from one rte_eth_dev_pci_generic_probe call
to multiple rte_eth_dev_create calls, while parses the firmware mapping
information.
Signed-off-by: Martin Spinler <spinler@cesnet.cz>
---
drivers/net/nfb/nfb.h | 27 +++++
drivers/net/nfb/nfb_ethdev.c | 215 ++++++++++++++++++++++++++++-------
2 files changed, 201 insertions(+), 41 deletions(-)
diff --git a/drivers/net/nfb/nfb.h b/drivers/net/nfb/nfb.h
index 6b74b3e6ec..a1134d7786 100644
--- a/drivers/net/nfb/nfb.h
+++ b/drivers/net/nfb/nfb.h
@@ -11,6 +11,7 @@
#include <nfb/ndp.h>
#include <netcope/rxmac.h>
#include <netcope/txmac.h>
+#include <netcope/info.h>
extern int nfb_logtype;
#define RTE_LOGTYPE_NFB nfb_logtype
@@ -51,6 +52,9 @@ struct pmd_internals {
struct nc_rxmac *rxmac[RTE_MAX_NC_RXMAC];
struct nc_txmac *txmac[RTE_MAX_NC_TXMAC];
struct nfb_device *nfb;
+
+ TAILQ_ENTRY(pmd_internals) eth_dev_list; /**< Item in list of all devices */
+ struct rte_eth_dev *eth_dev; /**< Handle for matching the device being removed */
};
/*
@@ -67,4 +71,27 @@ struct pmd_priv {
int *queue_map_tx;
};
+/* Data for common device probing */
+struct nfb_probe_params {
+ /** Generic device information */
+ struct rte_device *device;
+ /** Port bus specific initialisation callback function */
+ ethdev_bus_specific_init specific_init;
+ /** Port bus specific initialisation parameters */
+ void *specific_device;
+
+ const char *path; /**< libnfb device string */
+ const char *args; /**< Device arguments */
+ int nfb_id; /**< ID of NFB device in system */
+ int ep_index; /**< (PCIe) endpoint index for matching assigned interfaces */
+
+ char name[RTE_DEV_NAME_MAX_LEN]; /**< Probed name (e.g. PCI device name) */
+};
+
+
+__rte_internal
+int nfb_eth_common_probe(struct nfb_probe_params *params);
+__rte_internal
+int nfb_eth_common_remove(struct rte_device *dev);
+
#endif /* _NFB_H_ */
diff --git a/drivers/net/nfb/nfb_ethdev.c b/drivers/net/nfb/nfb_ethdev.c
index 803815138c..6eb0cd08fd 100644
--- a/drivers/net/nfb/nfb_ethdev.c
+++ b/drivers/net/nfb/nfb_ethdev.c
@@ -4,6 +4,10 @@
* All rights reserved.
*/
+#include <sys/queue.h>
+#include <eal_export.h>
+#include <rte_tailq.h>
+
#include <nfb/nfb.h>
#include <nfb/ndp.h>
#include <netcope/rxmac.h>
@@ -18,6 +22,21 @@
#include "nfb_rxmode.h"
#include "nfb.h"
+struct nfb_ifc_create_params {
+ struct nfb_probe_params *probe_params;
+ struct nc_ifc_map_info map_info;
+ struct nc_ifc_info *ifc_info;
+
+ int basename_len; /* Cached real length of original probe_params->name */
+};
+
+/* The TAILQ entries are used for cleanup of allocated resources
+ * in situations, where dev_close is not called.
+ */
+TAILQ_HEAD(nfb_pmd_internals_head, pmd_internals);
+static struct nfb_pmd_internals_head nfb_eth_dev_list =
+ TAILQ_HEAD_INITIALIZER(nfb_eth_dev_list);
+
static int nfb_eth_dev_uninit(struct rte_eth_dev *dev);
/**
@@ -507,23 +526,20 @@ static const struct eth_dev_ops ops = {
* 0 on success, a negative errno value otherwise.
*/
static int
-nfb_eth_dev_init(struct rte_eth_dev *dev)
+nfb_eth_dev_init(struct rte_eth_dev *dev, void *init_data)
{
int i;
+ int cnt;
int ret;
uint32_t mac_count;
struct rte_eth_dev_data *data = dev->data;
struct pmd_internals *internals;
+ struct nfb_ifc_create_params *params = init_data;
+ struct nc_ifc_info *ifc = params->ifc_info;
+ struct nc_ifc_map_info *mi = ¶ms->map_info;
struct pmd_priv *priv = data->dev_private;
- struct rte_pci_device *pci_dev = RTE_ETH_DEV_TO_PCI(dev);
- struct rte_pci_addr *pci_addr = &pci_dev->addr;
struct rte_ether_addr eth_addr_init;
uint16_t max_rx_queues, max_tx_queues;
- char nfb_dev[PATH_MAX];
-
- NFB_LOG(INFO, "Initializing NFB device (" PCI_PRI_FMT ")",
- pci_addr->domain, pci_addr->bus, pci_addr->devid,
- pci_addr->function);
internals = rte_zmalloc_socket("nfb_internals",
sizeof(struct pmd_internals), RTE_CACHE_LINE_SIZE,
@@ -535,27 +551,17 @@ nfb_eth_dev_init(struct rte_eth_dev *dev)
dev->process_private = internals;
- snprintf(nfb_dev, sizeof(nfb_dev),
- "/dev/nfb/by-pci-slot/" PCI_PRI_FMT,
- pci_addr->domain, pci_addr->bus, pci_addr->devid,
- pci_addr->function);
-
- /*
- * Get number of available DMA RX and TX queues, which is maximum
- * number of queues that can be created and store it in private device
- * data structure.
- */
- internals->nfb = nfb_open(nfb_dev);
+ /* Open device handle */
+ internals->nfb = nfb_open(params->probe_params->path);
if (internals->nfb == NULL) {
- NFB_LOG(ERR, "nfb_open(): failed to open %s", nfb_dev);
+ NFB_LOG(ERR, "nfb_open(): failed to open %s", params->probe_params->path);
ret = -EINVAL;
goto err_nfb_open;
}
- max_rx_queues = ndp_get_rx_queue_available_count(internals->nfb);
- max_tx_queues = ndp_get_tx_queue_available_count(internals->nfb);
- NFB_LOG(INFO, "Available NDP queues RX: %u TX: %u",
- max_rx_queues, max_tx_queues);
+ /* Get number of available DMA RX and TX queues */
+ max_rx_queues = ifc->rxq_cnt;
+ max_tx_queues = ifc->txq_cnt;
nfb_nc_rxmac_init(internals->nfb,
internals->rxmac,
@@ -586,11 +592,18 @@ nfb_eth_dev_init(struct rte_eth_dev *dev)
}
priv->queue_map_tx = priv->queue_map_rx + max_rx_queues;
- /* default queue mapping is 1:1 */
- for (i = 0; i < max_rx_queues; i++)
- priv->queue_map_rx[i] = i;
- for (i = 0; i < max_tx_queues; i++)
- priv->queue_map_tx[i] = i;
+ /* Use queue mapping provided by libnfb */
+ cnt = 0;
+ for (i = 0; i < mi->rxq_cnt; i++) {
+ if (mi->rxq[i].ifc == ifc->id)
+ priv->queue_map_rx[cnt++] = mi->rxq[i].id;
+ }
+
+ cnt = 0;
+ for (i = 0; i < mi->txq_cnt; i++) {
+ if (mi->txq[i].ifc == ifc->id)
+ priv->queue_map_tx[cnt++] = mi->txq[i].id;
+ }
/* Allocate space for MAC addresses */
mac_count = nfb_eth_get_max_mac_address_count(dev);
@@ -616,10 +629,6 @@ nfb_eth_dev_init(struct rte_eth_dev *dev)
data->dev_flags |= RTE_ETH_DEV_AUTOFILL_QUEUE_XSTATS;
}
- NFB_LOG(INFO, "NFB device (" PCI_PRI_FMT ") successfully initialized",
- pci_addr->domain, pci_addr->bus, pci_addr->devid,
- pci_addr->function);
-
return 0;
err_malloc_mac_addrs:
@@ -647,25 +656,104 @@ nfb_eth_dev_init(struct rte_eth_dev *dev)
static int
nfb_eth_dev_uninit(struct rte_eth_dev *dev)
{
- struct rte_pci_device *pci_dev = RTE_ETH_DEV_TO_PCI(dev);
- struct rte_pci_addr *pci_addr = &pci_dev->addr;
struct pmd_internals *internals = dev->process_private;
struct pmd_priv *priv = dev->data->dev_private;
if (rte_eal_process_type() == RTE_PROC_PRIMARY)
rte_free(priv->queue_map_rx);
+ TAILQ_REMOVE(&nfb_eth_dev_list, internals, eth_dev_list);
+
nfb_nc_rxmac_deinit(internals->rxmac, internals->max_rxmac);
nfb_nc_txmac_deinit(internals->txmac, internals->max_txmac);
nfb_close(internals->nfb);
rte_free(internals);
- NFB_LOG(INFO, "NFB device (" PCI_PRI_FMT ") successfully uninitialized",
- pci_addr->domain, pci_addr->bus, pci_addr->devid,
- pci_addr->function);
+ return 0;
+}
+
+static int
+nfb_eth_dev_create_for_ifc(struct nfb_ifc_create_params *cp)
+{
+ int ret;
+ struct nc_ifc_info *ifc;
+ struct nfb_probe_params *pp;
+ struct rte_eth_dev *eth_dev;
+ struct pmd_internals *p;
+
+ ifc = cp->ifc_info;
+ pp = cp->probe_params;
+
+ /* Skip interfaces which doesn't belong to the probed PCI device */
+ if ((pp->ep_index != -1 && ifc->ep != pp->ep_index) ||
+ (ifc->flags & NC_IFC_INFO_FLAG_ACTIVE) == 0)
+ return 0;
+
+ ret = snprintf(pp->name + cp->basename_len, sizeof(pp->name) - cp->basename_len,
+ "_eth%d", ifc->id);
+ if (ret < 0 || ret >= (signed int)sizeof(pp->name) - cp->basename_len)
+ return -EINVAL;
+
+ ret = rte_eth_dev_create(pp->device, pp->name,
+ sizeof(struct pmd_priv),
+ pp->specific_init, pp->specific_device,
+ nfb_eth_dev_init, cp);
+ if (ret)
+ goto out;
+
+ eth_dev = rte_eth_dev_get_by_name(pp->name);
+ p = eth_dev->process_private;
+ p->eth_dev = eth_dev;
+ TAILQ_INSERT_TAIL(&nfb_eth_dev_list, p, eth_dev_list);
+
+out:
+ /* return to original name (just for clarity) */
+ pp->name[cp->basename_len] = '\0';
+ return ret;
+}
+
+RTE_EXPORT_INTERNAL_SYMBOL(nfb_eth_common_probe)
+int
+nfb_eth_common_probe(struct nfb_probe_params *params)
+{
+ int i;
+ int ret;
+
+ struct nfb_device *nfb_dev;
+ struct nfb_ifc_create_params ifc_params;
+
+ nfb_dev = nfb_open(params->path);
+ if (nfb_dev == NULL) {
+ NFB_LOG(ERR, "nfb_open(): failed to open %s", params->path);
+ return -EINVAL;
+ }
+
+ ret = nc_ifc_map_info_create_ordinary(nfb_dev, &ifc_params.map_info);
+ if (ret)
+ goto err_map_info_create;
+
+ ifc_params.probe_params = params;
+ ifc_params.basename_len = strlen(params->name);
+
+ for (i = 0; i < ifc_params.map_info.ifc_cnt; i++) {
+ ifc_params.ifc_info = &ifc_params.map_info.ifc[i];
+ ret = nfb_eth_dev_create_for_ifc(&ifc_params);
+ if (ret)
+ goto err_dev_create;
+ }
+
+ nc_map_info_destroy(&ifc_params.map_info);
+ nfb_close(nfb_dev);
return 0;
+
+err_dev_create:
+ nfb_eth_common_remove(params->device);
+ nc_map_info_destroy(&ifc_params.map_info);
+err_map_info_create:
+ nfb_close(nfb_dev);
+ return ret;
}
static const struct rte_pci_id nfb_pci_id_table[] = {
@@ -694,8 +782,53 @@ static int
nfb_eth_pci_probe(struct rte_pci_driver *pci_drv __rte_unused,
struct rte_pci_device *pci_dev)
{
- return rte_eth_dev_pci_generic_probe(pci_dev,
- sizeof(struct pmd_priv), nfb_eth_dev_init);
+ int ret;
+ char path[PATH_MAX];
+
+ struct nc_composed_device_info comp_dev_info;
+ struct nfb_probe_params params = {0};
+
+ rte_pci_device_name(&pci_dev->addr, params.name, sizeof(params.name));
+
+ /*
+ * NFB device can be composed from multiple PCI devices,
+ * find the base char device ID for the current PCI device
+ */
+ ret = nc_get_composed_device_info_by_pci(NULL, params.name, &comp_dev_info);
+ if (ret) {
+ NFB_LOG(ERR, "Could not find NFB device for %s", params.name);
+ return -ENODEV;
+ }
+
+ ret = snprintf(path, sizeof(path), NFB_BASE_DEV_PATH "%d", comp_dev_info.nfb_id);
+ if (ret < 0 || ret >= (signed int)sizeof(path))
+ return -EINVAL;
+
+ params.args = pci_dev->device.devargs ? pci_dev->device.devargs->args : NULL;
+ params.path = path;
+ params.nfb_id = comp_dev_info.nfb_id;
+ params.ep_index = comp_dev_info.ep_index;
+
+ params.device = &pci_dev->device;
+ params.specific_init = eth_dev_pci_specific_init;
+ params.specific_device = pci_dev;
+
+ return nfb_eth_common_probe(¶ms);
+}
+
+RTE_EXPORT_INTERNAL_SYMBOL(nfb_eth_common_remove)
+int
+nfb_eth_common_remove(struct rte_device *dev)
+{
+ struct pmd_internals *entry, *temp;
+
+ RTE_TAILQ_FOREACH_SAFE(entry, &nfb_eth_dev_list, eth_dev_list, temp) {
+ if (dev == entry->eth_dev->device) {
+ TAILQ_REMOVE(&nfb_eth_dev_list, entry, eth_dev_list);
+ rte_eth_dev_destroy(entry->eth_dev, nfb_eth_dev_uninit);
+ }
+ }
+ return 0;
}
/**
@@ -712,7 +845,7 @@ nfb_eth_pci_probe(struct rte_pci_driver *pci_drv __rte_unused,
static int
nfb_eth_pci_remove(struct rte_pci_device *pci_dev)
{
- return rte_eth_dev_pci_generic_remove(pci_dev, nfb_eth_dev_uninit);
+ return nfb_eth_common_remove(&pci_dev->device);
}
static struct rte_pci_driver nfb_eth_driver = {
--
2.52.0
^ permalink raw reply related [flat|nested] 131+ messages in thread
* [PATCH v5 3/8] net/nfb: add vdev as alternative device probe method
2026-01-23 17:22 ` [PATCH v5 " spinler
2026-01-23 17:22 ` [PATCH v5 1/8] net/nfb: prepare for indirect queue mapping scheme spinler
2026-01-23 17:22 ` [PATCH v5 2/8] net/nfb: create one ethdev per ethernet port spinler
@ 2026-01-23 17:22 ` spinler
2026-01-23 17:22 ` [PATCH v5 4/8] net/nfb: add device argument "port" to limit used ports spinler
` (5 subsequent siblings)
8 siblings, 0 replies; 131+ messages in thread
From: spinler @ 2026-01-23 17:22 UTC (permalink / raw)
To: dev; +Cc: Martin Spinler
From: Martin Spinler <spinler@cesnet.cz>
VDEV are useful for NFB devices not represented by PCI device,
e.g. virtual or simulated devices.
Signed-off-by: Martin Spinler <spinler@cesnet.cz>
---
drivers/net/nfb/meson.build | 1 +
drivers/net/nfb/nfb_vdev.c | 106 ++++++++++++++++++++++++++++++++++++
2 files changed, 107 insertions(+)
create mode 100644 drivers/net/nfb/nfb_vdev.c
diff --git a/drivers/net/nfb/meson.build b/drivers/net/nfb/meson.build
index d7a255c928..9e458dfb4a 100644
--- a/drivers/net/nfb/meson.build
+++ b/drivers/net/nfb/meson.build
@@ -16,6 +16,7 @@ ext_deps += dep
sources = files(
'nfb_ethdev.c',
+ 'nfb_vdev.c',
'nfb_rx.c',
'nfb_rxmode.c',
'nfb_stats.c',
diff --git a/drivers/net/nfb/nfb_vdev.c b/drivers/net/nfb/nfb_vdev.c
new file mode 100644
index 0000000000..b79f7ac416
--- /dev/null
+++ b/drivers/net/nfb/nfb_vdev.c
@@ -0,0 +1,106 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2026 Cesnet
+ */
+
+#include <rte_kvargs.h>
+#include <ethdev_vdev.h>
+
+#include "nfb.h"
+
+#define VDEV_NFB_DRIVER net_vdev_nfb
+#define VDEV_NFB_ARG_DEV "dev"
+
+static int
+vdev_nfb_vdev_probe(struct rte_vdev_device *dev)
+{
+ unsigned int count;
+ int ret = 0;
+ size_t len, pos;
+
+ struct nfb_probe_params params;
+
+ const char *vdev_name = rte_vdev_device_name(dev);
+ const char *vdev_args = rte_vdev_device_args(dev);
+ char *dev_params, *dev_params_mod;
+ struct rte_kvargs *kvargs;
+
+ kvargs = rte_kvargs_parse(vdev_args, NULL);
+ if (kvargs == NULL) {
+ NFB_LOG(ERR, "Failed to parse device arguments %s", vdev_args);
+ ret = -EINVAL;
+ goto err_parse_args;
+ }
+
+ dev_params = strdup(vdev_args);
+ if (dev_params == NULL) {
+ ret = -ENOMEM;
+ goto err_strdup_params;
+ }
+
+ params.device = &dev->device;
+ params.specific_init = NULL;
+ params.specific_device = NULL;
+ params.path = nfb_default_dev_path();
+ params.args = dev_params;
+ params.nfb_id = 0;
+ params.ep_index = -1;
+
+ len = strlen(dev_params) + 1;
+ pos = 0;
+ dev_params[pos] = '\0';
+
+ /* Parse parameters for virtual device */
+ for (count = 0; count != kvargs->count; ++count) {
+ const struct rte_kvargs_pair *pair = &kvargs->pairs[count];
+
+ if (!strcmp(pair->key, VDEV_NFB_ARG_DEV)) {
+ params.path = pair->value;
+ } else {
+ /* Clone non-vdev arguments, result is shorter or equal length */
+ dev_params_mod = dev_params + pos;
+ ret = snprintf(dev_params_mod, len, "%s%s=%s",
+ pos == 0 ? "" : ",", pair->key, pair->value);
+ if (ret < 0 || ret >= (signed int)len)
+ goto err_clone_args;
+ pos += ret;
+ len -= ret;
+ }
+ }
+
+ strlcpy(params.name, vdev_name, sizeof(params.name));
+
+ ret = nfb_eth_common_probe(¶ms);
+ if (ret)
+ goto err_nfb_common_probe;
+
+ free(dev_params);
+ rte_kvargs_free(kvargs);
+
+ return ret;
+
+err_nfb_common_probe:
+err_clone_args:
+ free(dev_params);
+err_strdup_params:
+ rte_kvargs_free(kvargs);
+err_parse_args:
+ return ret;
+}
+
+static int
+vdev_nfb_vdev_remove(struct rte_vdev_device *dev)
+{
+ return nfb_eth_common_remove(&dev->device);
+}
+
+/** Virtual device descriptor. */
+static struct rte_vdev_driver vdev_nfb_vdev = {
+ .probe = vdev_nfb_vdev_probe,
+ .remove = vdev_nfb_vdev_remove,
+};
+
+RTE_PMD_REGISTER_VDEV(VDEV_NFB_DRIVER, vdev_nfb_vdev);
+RTE_PMD_REGISTER_ALIAS(VDEV_NFB_DRIVER, eth_vdev_nfb);
+RTE_PMD_REGISTER_PARAM_STRING(net_vdev_nfb,
+ VDEV_NFB_ARG_DEV "=<string>"
+ );
--
2.52.0
^ permalink raw reply related [flat|nested] 131+ messages in thread
* [PATCH v5 4/8] net/nfb: add device argument "port" to limit used ports
2026-01-23 17:22 ` [PATCH v5 " spinler
` (2 preceding siblings ...)
2026-01-23 17:22 ` [PATCH v5 3/8] net/nfb: add vdev as alternative device probe method spinler
@ 2026-01-23 17:22 ` spinler
2026-01-23 17:22 ` [PATCH v5 5/8] net/nfb: init only MACs associated with device spinler
` (4 subsequent siblings)
8 siblings, 0 replies; 131+ messages in thread
From: spinler @ 2026-01-23 17:22 UTC (permalink / raw)
To: dev; +Cc: Martin Spinler
From: Martin Spinler <spinler@cesnet.cz>
NFB devices do not use separate PCI device for each port; one PCI device
contains handles for all ports instead. For some application this approach
can be limiting.
The "port" argument can be used to select only desired ports.
It can be used multiple times. When is not used at all, the driver
selects all ports.
Signed-off-by: Martin Spinler <spinler@cesnet.cz>
---
drivers/net/nfb/nfb.h | 7 +++++
drivers/net/nfb/nfb_ethdev.c | 54 ++++++++++++++++++++++++++++++++++--
drivers/net/nfb/nfb_vdev.c | 3 +-
3 files changed, 60 insertions(+), 4 deletions(-)
diff --git a/drivers/net/nfb/nfb.h b/drivers/net/nfb/nfb.h
index a1134d7786..10d74eb49c 100644
--- a/drivers/net/nfb/nfb.h
+++ b/drivers/net/nfb/nfb.h
@@ -42,6 +42,13 @@ extern int nfb_logtype;
#define RTE_NFB_DRIVER_NAME net_nfb
+/* Device arguments */
+#define NFB_ARG_PORT "port"
+
+#define NFB_COMMON_ARGS \
+ NFB_ARG_PORT "=<number>" \
+ ""
+
/*
* Handles obtained from the libnfb: each process must use own instance.
* Stored inside dev->process_private.
diff --git a/drivers/net/nfb/nfb_ethdev.c b/drivers/net/nfb/nfb_ethdev.c
index 6eb0cd08fd..3961f82a3d 100644
--- a/drivers/net/nfb/nfb_ethdev.c
+++ b/drivers/net/nfb/nfb_ethdev.c
@@ -22,6 +22,11 @@
#include "nfb_rxmode.h"
#include "nfb.h"
+static const char * const VALID_KEYS[] = {
+ NFB_ARG_PORT,
+ NULL
+};
+
struct nfb_ifc_create_params {
struct nfb_probe_params *probe_params;
struct nc_ifc_map_info map_info;
@@ -713,6 +718,24 @@ nfb_eth_dev_create_for_ifc(struct nfb_ifc_create_params *cp)
return ret;
}
+static int nfb_eth_dev_create_for_ifc_by_port(const char *key __rte_unused,
+ const char *value, void *opaque)
+{
+ char *end;
+ unsigned long port;
+ struct nfb_ifc_create_params *ifc_params = opaque;
+
+ if (value == NULL || strlen(value) == 0 || !isdigit(*value))
+ return -EINVAL;
+
+ port = strtoul(value, &end, 0);
+ if (*end != '\0' || port >= (unsigned long)ifc_params->map_info.ifc_cnt)
+ return -EINVAL;
+
+ ifc_params->ifc_info = &ifc_params->map_info.ifc[port];
+ return nfb_eth_dev_create_for_ifc(ifc_params);
+}
+
RTE_EXPORT_INTERNAL_SYMBOL(nfb_eth_common_probe)
int
nfb_eth_common_probe(struct nfb_probe_params *params)
@@ -722,6 +745,7 @@ nfb_eth_common_probe(struct nfb_probe_params *params)
struct nfb_device *nfb_dev;
struct nfb_ifc_create_params ifc_params;
+ struct rte_kvargs *kvlist = NULL;
nfb_dev = nfb_open(params->path);
if (nfb_dev == NULL) {
@@ -733,16 +757,35 @@ nfb_eth_common_probe(struct nfb_probe_params *params)
if (ret)
goto err_map_info_create;
+ if (params->args != NULL && strlen(params->args) > 0) {
+ kvlist = rte_kvargs_parse(params->args, VALID_KEYS);
+ if (kvlist == NULL) {
+ NFB_LOG(ERR, "Failed to parse device arguments %s", params->args);
+ ret = -EINVAL;
+ goto err_parse_args;
+ }
+ }
+
ifc_params.probe_params = params;
ifc_params.basename_len = strlen(params->name);
- for (i = 0; i < ifc_params.map_info.ifc_cnt; i++) {
- ifc_params.ifc_info = &ifc_params.map_info.ifc[i];
- ret = nfb_eth_dev_create_for_ifc(&ifc_params);
+ /* When at least one port argument is specified, create only selected ports */
+ if (kvlist && rte_kvargs_count(kvlist, NFB_ARG_PORT)) {
+ ret = rte_kvargs_process(kvlist, NFB_ARG_PORT,
+ nfb_eth_dev_create_for_ifc_by_port, (void *)&ifc_params);
if (ret)
goto err_dev_create;
+ } else {
+ for (i = 0; i < ifc_params.map_info.ifc_cnt; i++) {
+ ifc_params.ifc_info = &ifc_params.map_info.ifc[i];
+ ret = nfb_eth_dev_create_for_ifc(&ifc_params);
+ if (ret)
+ goto err_dev_create;
+ }
}
+ rte_kvargs_free(kvlist);
+
nc_map_info_destroy(&ifc_params.map_info);
nfb_close(nfb_dev);
@@ -750,6 +793,8 @@ nfb_eth_common_probe(struct nfb_probe_params *params)
err_dev_create:
nfb_eth_common_remove(params->device);
+ rte_kvargs_free(kvlist);
+err_parse_args:
nc_map_info_destroy(&ifc_params.map_info);
err_map_info_create:
nfb_close(nfb_dev);
@@ -858,3 +903,6 @@ RTE_PMD_REGISTER_PCI(RTE_NFB_DRIVER_NAME, nfb_eth_driver);
RTE_PMD_REGISTER_PCI_TABLE(RTE_NFB_DRIVER_NAME, nfb_pci_id_table);
RTE_PMD_REGISTER_KMOD_DEP(RTE_NFB_DRIVER_NAME, "* nfb");
RTE_LOG_REGISTER_DEFAULT(nfb_logtype, NOTICE);
+RTE_PMD_REGISTER_PARAM_STRING(RTE_NFB_DRIVER_NAME,
+ NFB_COMMON_ARGS
+ );
diff --git a/drivers/net/nfb/nfb_vdev.c b/drivers/net/nfb/nfb_vdev.c
index b79f7ac416..57f10d2068 100644
--- a/drivers/net/nfb/nfb_vdev.c
+++ b/drivers/net/nfb/nfb_vdev.c
@@ -102,5 +102,6 @@ static struct rte_vdev_driver vdev_nfb_vdev = {
RTE_PMD_REGISTER_VDEV(VDEV_NFB_DRIVER, vdev_nfb_vdev);
RTE_PMD_REGISTER_ALIAS(VDEV_NFB_DRIVER, eth_vdev_nfb);
RTE_PMD_REGISTER_PARAM_STRING(net_vdev_nfb,
- VDEV_NFB_ARG_DEV "=<string>"
+ VDEV_NFB_ARG_DEV "=<string> "
+ NFB_COMMON_ARGS
);
--
2.52.0
^ permalink raw reply related [flat|nested] 131+ messages in thread
* [PATCH v5 5/8] net/nfb: init only MACs associated with device
2026-01-23 17:22 ` [PATCH v5 " spinler
` (3 preceding siblings ...)
2026-01-23 17:22 ` [PATCH v5 4/8] net/nfb: add device argument "port" to limit used ports spinler
@ 2026-01-23 17:22 ` spinler
2026-01-23 17:22 ` [PATCH v5 6/8] net/nfb: add compatible cards to driver PCI ID table spinler
` (3 subsequent siblings)
8 siblings, 0 replies; 131+ messages in thread
From: spinler @ 2026-01-23 17:22 UTC (permalink / raw)
To: dev; +Cc: Martin Spinler
From: Martin Spinler <spinler@cesnet.cz>
Use information obtained from the new manner of creating NFB ethdevs
and initialize only RxMACs / TxMACs associated with actual ethdev.
This enables separated management and configuration of interface ports.
Signed-off-by: Martin Spinler <spinler@cesnet.cz>
---
drivers/net/nfb/nfb.h | 12 +--
drivers/net/nfb/nfb_ethdev.c | 137 ++++++++++++++++-------------------
2 files changed, 67 insertions(+), 82 deletions(-)
diff --git a/drivers/net/nfb/nfb.h b/drivers/net/nfb/nfb.h
index 10d74eb49c..01241860b1 100644
--- a/drivers/net/nfb/nfb.h
+++ b/drivers/net/nfb/nfb.h
@@ -36,10 +36,6 @@ extern int nfb_logtype;
#define RTE_ETH_NDP_MAX_RX_QUEUES 32
#define RTE_ETH_NDP_MAX_TX_QUEUES 32
-/* Max index of rx/tx dmas */
-#define RTE_MAX_NC_RXMAC 256
-#define RTE_MAX_NC_TXMAC 256
-
#define RTE_NFB_DRIVER_NAME net_nfb
/* Device arguments */
@@ -54,10 +50,10 @@ extern int nfb_logtype;
* Stored inside dev->process_private.
*/
struct pmd_internals {
- uint16_t max_rxmac;
- uint16_t max_txmac;
- struct nc_rxmac *rxmac[RTE_MAX_NC_RXMAC];
- struct nc_txmac *txmac[RTE_MAX_NC_TXMAC];
+ uint16_t max_rxmac; /**< Count of valid rxmac items */
+ uint16_t max_txmac; /**< Count of valid txmac items */
+ struct nc_rxmac **rxmac; /**< Array of Rx MAC handles */
+ struct nc_txmac **txmac; /**< Array of Tx MAC handles */
struct nfb_device *nfb;
TAILQ_ENTRY(pmd_internals) eth_dev_list; /**< Item in list of all devices */
diff --git a/drivers/net/nfb/nfb_ethdev.c b/drivers/net/nfb/nfb_ethdev.c
index 3961f82a3d..5c489fcb74 100644
--- a/drivers/net/nfb/nfb_ethdev.c
+++ b/drivers/net/nfb/nfb_ethdev.c
@@ -52,81 +52,75 @@ static const struct rte_ether_addr eth_addr = {
};
/**
- * Open all RX DMA queues
+ * Open Rx and Tx MAC components associated with current ifc
*
- * @param dev
- * Pointer to nfb device.
- * @param[out] rxmac
- * Pointer to output array of nc_rxmac
- * @param[out] max_rxmac
- * Pointer to output max index of rxmac
+ * @param intl
+ * Pointer to driver internal structure
+ * @param params
+ * Pointer to init parameters structure
*/
-static void
-nfb_nc_rxmac_init(struct nfb_device *nfb,
- struct nc_rxmac *rxmac[RTE_MAX_NC_RXMAC],
- uint16_t *max_rxmac)
+static int
+nfb_nc_eth_init(struct pmd_internals *intl, struct nfb_ifc_create_params *params)
{
- *max_rxmac = 0;
- while ((rxmac[*max_rxmac] = nc_rxmac_open_index(nfb, *max_rxmac)))
- ++(*max_rxmac);
-}
+ int ret;
+ int i, rxm, txm;
+ struct nc_ifc_info *ifc = params->ifc_info;
+ struct nc_ifc_map_info *mi = ¶ms->map_info;
-/**
- * Open all TX DMA queues
- *
- * @param dev
- * Pointer to nfb device.
- * @param[out] txmac
- * Pointer to output array of nc_txmac
- * @param[out] max_rxmac
- * Pointer to output max index of txmac
- */
-static void
-nfb_nc_txmac_init(struct nfb_device *nfb,
- struct nc_txmac *txmac[RTE_MAX_NC_TXMAC],
- uint16_t *max_txmac)
-{
- *max_txmac = 0;
- while ((txmac[*max_txmac] = nc_txmac_open_index(nfb, *max_txmac)))
- ++(*max_txmac);
-}
+ ret = -ENOMEM;
+ if (ifc->eth_cnt == 0)
+ return 0;
-/**
- * Close all RX DMA queues
- *
- * @param rxmac
- * Pointer to array of nc_rxmac
- * @param max_rxmac
- * Maximum index of rxmac
- */
-static void
-nfb_nc_rxmac_deinit(struct nc_rxmac *rxmac[RTE_MAX_NC_RXMAC],
- uint16_t max_rxmac)
-{
- uint16_t i;
- for (i = 0; i < max_rxmac; i++) {
- nc_rxmac_close(rxmac[i]);
- rxmac[i] = NULL;
+ intl->rxmac = calloc(ifc->eth_cnt, sizeof(*intl->rxmac));
+ if (intl->rxmac == NULL)
+ goto err_alloc_rxmac;
+
+ intl->txmac = calloc(ifc->eth_cnt, sizeof(*intl->txmac));
+ if (intl->txmac == NULL)
+ goto err_alloc_txmac;
+
+ /* Some eths may not have assigned MAC nodes, hence use separate var for indexing */
+ rxm = 0;
+ txm = 0;
+ for (i = 0; i < mi->eth_cnt; i++) {
+ if (mi->eth[i].ifc != ifc->id)
+ continue;
+
+ intl->rxmac[rxm] = nc_rxmac_open(intl->nfb, mi->eth[i].node_rxmac);
+ if (intl->rxmac[rxm])
+ rxm++;
+
+ intl->txmac[txm] = nc_txmac_open(intl->nfb, mi->eth[i].node_txmac);
+ if (intl->txmac[txm])
+ txm++;
}
+
+ intl->max_rxmac = rxm;
+ intl->max_txmac = txm;
+ return 0;
+
+err_alloc_txmac:
+ free(intl->rxmac);
+err_alloc_rxmac:
+ return ret;
}
/**
- * Close all TX DMA queues
- *
- * @param txmac
- * Pointer to array of nc_txmac
- * @param max_txmac
- * Maximum index of txmac
+ * Close all Rx+Tx MAC components
+ * @param intl
+ * Pointer to internal driver structure
*/
static void
-nfb_nc_txmac_deinit(struct nc_txmac *txmac[RTE_MAX_NC_TXMAC],
- uint16_t max_txmac)
+nfb_nc_eth_deinit(struct pmd_internals *intl)
{
uint16_t i;
- for (i = 0; i < max_txmac; i++) {
- nc_txmac_close(txmac[i]);
- txmac[i] = NULL;
- }
+ for (i = 0; i < intl->max_txmac; i++)
+ nc_txmac_close(intl->txmac[i]);
+ for (i = 0; i < intl->max_rxmac; i++)
+ nc_rxmac_close(intl->rxmac[i]);
+
+ free(intl->txmac);
+ free(intl->rxmac);
}
/**
@@ -343,7 +337,7 @@ nfb_eth_link_update(struct rte_eth_dev *dev,
link.link_duplex = RTE_ETH_LINK_FULL_DUPLEX;
link.link_autoneg = RTE_ETH_LINK_SPEED_FIXED;
- if (internals->rxmac[0] != NULL) {
+ if (internals->max_rxmac) {
nc_rxmac_read_status(internals->rxmac[0], &status);
switch (status.speed) {
@@ -568,12 +562,9 @@ nfb_eth_dev_init(struct rte_eth_dev *dev, void *init_data)
max_rx_queues = ifc->rxq_cnt;
max_tx_queues = ifc->txq_cnt;
- nfb_nc_rxmac_init(internals->nfb,
- internals->rxmac,
- &internals->max_rxmac);
- nfb_nc_txmac_init(internals->nfb,
- internals->txmac,
- &internals->max_txmac);
+ ret = nfb_nc_eth_init(internals, params);
+ if (ret)
+ goto err_nc_eth_init;
/* Set rx, tx burst functions */
dev->rx_pkt_burst = nfb_eth_ndp_rx;
@@ -639,10 +630,9 @@ nfb_eth_dev_init(struct rte_eth_dev *dev, void *init_data)
err_malloc_mac_addrs:
rte_free(priv->queue_map_rx);
err_alloc_queue_map:
- nfb_nc_rxmac_deinit(internals->rxmac, internals->max_rxmac);
- nfb_nc_txmac_deinit(internals->txmac, internals->max_txmac);
+ nfb_nc_eth_deinit(internals);
+err_nc_eth_init:
nfb_close(internals->nfb);
-
err_nfb_open:
rte_free(internals);
err_alloc_internals:
@@ -669,8 +659,7 @@ nfb_eth_dev_uninit(struct rte_eth_dev *dev)
TAILQ_REMOVE(&nfb_eth_dev_list, internals, eth_dev_list);
- nfb_nc_rxmac_deinit(internals->rxmac, internals->max_rxmac);
- nfb_nc_txmac_deinit(internals->txmac, internals->max_txmac);
+ nfb_nc_eth_deinit(internals);
nfb_close(internals->nfb);
rte_free(internals);
--
2.52.0
^ permalink raw reply related [flat|nested] 131+ messages in thread
* [PATCH v5 6/8] net/nfb: add compatible cards to driver PCI ID table
2026-01-23 17:22 ` [PATCH v5 " spinler
` (4 preceding siblings ...)
2026-01-23 17:22 ` [PATCH v5 5/8] net/nfb: init only MACs associated with device spinler
@ 2026-01-23 17:22 ` spinler
2026-01-23 17:22 ` [PATCH v5 7/8] net/nfb: report firmware version spinler
` (2 subsequent siblings)
8 siblings, 0 replies; 131+ messages in thread
From: spinler @ 2026-01-23 17:22 UTC (permalink / raw)
To: dev; +Cc: Martin Spinler
From: Martin Spinler <spinler@cesnet.cz>
The nfb driver is compatible with:
- NFB-200G2QL card secondary PCI device
- FB2CGHH card
- COMBO-400G1 card
- Common CESNET-NDK cards
Signed-off-by: Martin Spinler <spinler@cesnet.cz>
---
drivers/net/nfb/nfb.h | 5 +++++
drivers/net/nfb/nfb_ethdev.c | 4 ++++
2 files changed, 9 insertions(+)
diff --git a/drivers/net/nfb/nfb.h b/drivers/net/nfb/nfb.h
index 01241860b1..2bce71ac89 100644
--- a/drivers/net/nfb/nfb.h
+++ b/drivers/net/nfb/nfb.h
@@ -24,13 +24,18 @@ extern int nfb_logtype;
/* PCI Vendor ID */
#define PCI_VENDOR_ID_NETCOPE 0x1b26
#define PCI_VENDOR_ID_SILICOM 0x1c2c
+#define PCI_VENDOR_ID_CESNET 0x18ec
/* PCI Device IDs */
#define PCI_DEVICE_ID_NFB_40G2 0xcb80
#define PCI_DEVICE_ID_NFB_100G2 0xc2c1
#define PCI_DEVICE_ID_NFB_200G2QL 0xc250
+#define PCI_DEVICE_ID_NFB_200G2QL_E1 0xc251
#define PCI_DEVICE_ID_FB2CGG3 0x00d0
+#define PCI_DEVICE_ID_FB2CGHH 0x00d2
#define PCI_DEVICE_ID_FB2CGG3D 0xc240
+#define PCI_DEVICE_ID_COMBO400G1 0xc400
+#define PCI_DEVICE_ID_CESNET_NDK_COMMON 0xc000
/* Max index of ndp rx/tx queues */
#define RTE_ETH_NDP_MAX_RX_QUEUES 32
diff --git a/drivers/net/nfb/nfb_ethdev.c b/drivers/net/nfb/nfb_ethdev.c
index 5c489fcb74..4da00915dd 100644
--- a/drivers/net/nfb/nfb_ethdev.c
+++ b/drivers/net/nfb/nfb_ethdev.c
@@ -794,8 +794,12 @@ static const struct rte_pci_id nfb_pci_id_table[] = {
{ RTE_PCI_DEVICE(PCI_VENDOR_ID_NETCOPE, PCI_DEVICE_ID_NFB_40G2) },
{ RTE_PCI_DEVICE(PCI_VENDOR_ID_NETCOPE, PCI_DEVICE_ID_NFB_100G2) },
{ RTE_PCI_DEVICE(PCI_VENDOR_ID_NETCOPE, PCI_DEVICE_ID_NFB_200G2QL) },
+ { RTE_PCI_DEVICE(PCI_VENDOR_ID_NETCOPE, PCI_DEVICE_ID_NFB_200G2QL_E1) },
{ RTE_PCI_DEVICE(PCI_VENDOR_ID_SILICOM, PCI_DEVICE_ID_FB2CGG3) },
+ { RTE_PCI_DEVICE(PCI_VENDOR_ID_SILICOM, PCI_DEVICE_ID_FB2CGHH) },
{ RTE_PCI_DEVICE(PCI_VENDOR_ID_SILICOM, PCI_DEVICE_ID_FB2CGG3D) },
+ { RTE_PCI_DEVICE(PCI_VENDOR_ID_CESNET, PCI_DEVICE_ID_COMBO400G1) },
+ { RTE_PCI_DEVICE(PCI_VENDOR_ID_CESNET, PCI_DEVICE_ID_CESNET_NDK_COMMON) },
{ .vendor_id = 0, }
};
--
2.52.0
^ permalink raw reply related [flat|nested] 131+ messages in thread
* [PATCH v5 7/8] net/nfb: report firmware version
2026-01-23 17:22 ` [PATCH v5 " spinler
` (5 preceding siblings ...)
2026-01-23 17:22 ` [PATCH v5 6/8] net/nfb: add compatible cards to driver PCI ID table spinler
@ 2026-01-23 17:22 ` spinler
2026-01-23 17:22 ` [PATCH v5 8/8] doc/nfb: cleanup and update guide spinler
2026-01-24 19:19 ` [PATCH v5 0/8] net/nfb: rework to real multiport Stephen Hemminger
8 siblings, 0 replies; 131+ messages in thread
From: spinler @ 2026-01-23 17:22 UTC (permalink / raw)
To: dev; +Cc: Martin Spinler
From: Martin Spinler <spinler@cesnet.cz>
Report the firmware version, which is described
by project name and project version.
Signed-off-by: Martin Spinler <spinler@cesnet.cz>
---
drivers/net/nfb/nfb_ethdev.c | 27 +++++++++++++++++++++++++++
1 file changed, 27 insertions(+)
diff --git a/drivers/net/nfb/nfb_ethdev.c b/drivers/net/nfb/nfb_ethdev.c
index 4da00915dd..9eabe18fd0 100644
--- a/drivers/net/nfb/nfb_ethdev.c
+++ b/drivers/net/nfb/nfb_ethdev.c
@@ -487,6 +487,32 @@ nfb_eth_mac_addr_remove(struct rte_eth_dev *dev, uint32_t index)
nc_rxmac_set_mac(internals->rxmac[i], index, 0, 0);
}
+static int
+nfb_eth_fw_version_get(struct rte_eth_dev *dev, char *fw_version,
+ size_t fw_size)
+{
+ int ret;
+ const char *proj_name, *proj_vers;
+ struct pmd_internals *intl = dev->process_private;
+
+ proj_name = nc_info_get_fw_project_name(intl->nfb, NULL);
+ proj_vers = nc_info_get_fw_project_version(intl->nfb, NULL);
+
+ if (proj_name == NULL)
+ proj_name = "";
+ if (proj_vers == NULL)
+ proj_vers = "";
+
+ ret = snprintf(fw_version, fw_size, "%s;%s", proj_name, proj_vers);
+ if (ret < 0)
+ return ret;
+
+ if (ret >= (signed int)fw_size)
+ return strlen(proj_name) + 1 + strlen(proj_vers) + 1;
+
+ return 0;
+}
+
static const struct eth_dev_ops ops = {
.dev_start = nfb_eth_dev_start,
.dev_stop = nfb_eth_dev_stop,
@@ -513,6 +539,7 @@ static const struct eth_dev_ops ops = {
.mac_addr_set = nfb_eth_mac_addr_set,
.mac_addr_add = nfb_eth_mac_addr_add,
.mac_addr_remove = nfb_eth_mac_addr_remove,
+ .fw_version_get = nfb_eth_fw_version_get,
};
/**
--
2.52.0
^ permalink raw reply related [flat|nested] 131+ messages in thread
* [PATCH v5 8/8] doc/nfb: cleanup and update guide
2026-01-23 17:22 ` [PATCH v5 " spinler
` (6 preceding siblings ...)
2026-01-23 17:22 ` [PATCH v5 7/8] net/nfb: report firmware version spinler
@ 2026-01-23 17:22 ` spinler
2026-01-24 19:10 ` [REVIEW] " Stephen Hemminger
2026-01-24 19:19 ` [PATCH v5 0/8] net/nfb: rework to real multiport Stephen Hemminger
8 siblings, 1 reply; 131+ messages in thread
From: spinler @ 2026-01-23 17:22 UTC (permalink / raw)
To: dev; +Cc: Martin Spinler
From: Martin Spinler <spinler@cesnet.cz>
Clean up documentation and update the guide with:
- Current card list with firmware download links
- Updated port argument description
Signed-off-by: Martin Spinler <spinler@cesnet.cz>
---
doc/guides/nics/features/nfb.ini | 4 +
doc/guides/nics/nfb.rst | 187 +++++++++++--------------
doc/guides/rel_notes/release_26_03.rst | 9 ++
3 files changed, 98 insertions(+), 102 deletions(-)
diff --git a/doc/guides/nics/features/nfb.ini b/doc/guides/nics/features/nfb.ini
index c77b592789..7ac89fc15f 100644
--- a/doc/guides/nics/features/nfb.ini
+++ b/doc/guides/nics/features/nfb.ini
@@ -9,9 +9,13 @@ Link status = Y
Queue start/stop = Y
Promiscuous mode = Y
Allmulticast mode = Y
+Unicast MAC filter = Y
+Timestamp offload = P
Basic stats = Y
Extended stats = Y
Stats per queue = Y
+FW version = Y
+Multiprocess aware = Y
Linux = Y
x86-64 = Y
Usage doc = Y
diff --git a/doc/guides/nics/nfb.rst b/doc/guides/nics/nfb.rst
index a9b4049654..55a635d8d4 100644
--- a/doc/guides/nics/nfb.rst
+++ b/doc/guides/nics/nfb.rst
@@ -2,64 +2,101 @@
Copyright 2019 Cesnet
Copyright 2019 Netcope Technologies
-NFB poll mode driver library
-=================================
+NFB Poll Mode Driver
+====================
+
+The NFB PMD implements support for the FPGA-based
+programmable NICs running `CESNET-NDK <https://www.liberouter.org/ndk/>`_
+based firmware (formerly known as the NetCOPE platform). The CESNET Network Development Kit offers
+wide spectrum of supported cards, for example:
+N6010, FB2CGG3 (Silicom Denmark),
+IA-420F, IA-440i (BittWare),
+AGI-FH400G (ReflexCES),
+and `many more <https://github.com/CESNET/ndk-fpga/tree/devel/cards>`_.
+
+The CESNET-NDK framework is open source and
+can be found on `CESNET-NDK <https://github.com/CESNET/ndk-fpga>`_.
+Ready-to-use demo firmwares can be found on the `DYNANIC page <https://dyna-nic.com/try-now/>`_.
+
+Software compatibility and firmware for
+`historical cards <https://www.liberouter.org/technologies/cards/>`_
+are left unmaintained.
+
+Software prerequisites
+----------------------
-The NFB poll mode driver library implements support for the Netcope
-FPGA Boards (**NFB-40G2, NFB-100G2, NFB-200G2QL**) and Silicom **FB2CGG3** card,
-FPGA-based programmable NICs. The NFB PMD uses interface provided by the libnfb
-library to communicate with these cards over the nfb layer.
+This PMD requires a Linux kernel module, which is responsible for initialization and
+allocation of resources needed for the nfb layer function.
+Communication between PMD and kernel modules is mediated by the libnfb library.
+The kernel module and library are not part of DPDK and must be installed
+separately. Dependencies can be found on GitHub:
+`nfb-framework <https://github.com/CESNET/ndk-sw>`_ as source code,
+or for RPM-based distributions, the prebuilt `nfb-framework` package on
+`Fedora Copr <https://copr.fedorainfracloud.org/coprs/g/CESNET/nfb-framework/>`_.
-More information about the
-`NFB cards <https://www.liberouter.org/technologies/cards/>`_
-and used technology
-(`Network Development Kit <https://www.liberouter.org/ndk/>`_)
-can be found on the `Liberouter website <http://www.liberouter.org/>`_.
+Before starting the DPDK, make sure that the kernel module is loaded (`sudo modprobe nfb`)
+and the card is running the CESNET-NDK based firmware (`nfb-info -l`).
.. note::
- Currently the driver is supported only on x86_64 architectures.
- Only x86_64 versions of the external libraries are provided.
+ Currently, the driver is supported only on x86_64 architectures.
+
+NFB card architecture
+---------------------
+
+Ethernet Ports
+~~~~~~~~~~~~~~
+
+The NFB cards are multi-port multi-queue cards, where (generally) data from any
+Ethernet port may be sent by the firmware to any queue.
-Prerequisites
--------------
+The cards were historically represented in DPDK as a single port.
+Currently each Ethernet channel is represented as one DPDK port.
-This PMD requires kernel modules which are responsible for initialization and
-allocation of resources needed for nfb layer function.
-Communication between PMD and kernel modules is mediated by libnfb library.
-These kernel modules and library are not part of DPDK and must be installed
-separately:
+.. note::
-* **libnfb library**
+ Normally, one port corresponds to one channel, but ports can often be configured in a separate
+ manner. For example one 100G port can be used as 4x25G or 4x10G independent Ethernet channels.
- The library provides API for initialization of nfb transfers, receiving and
- transmitting data segments.
+By default, all ports are initialized and used for the allowed PCI device.
+When this behaviour is limiting (e.g., for multiple instances of DPDK app on different ports
+of the same PCI device), ports can be specified by the `port` item in the `allow` argument:
-* **Kernel modules**
+.. code-block:: console
- * nfb
+ -a 0000:01:00.0,port=0,port=3
- Kernel modules manage initialization of hardware, allocation and
- sharing of resources for user space applications.
+PCIe slots
+~~~~~~~~~~
-Dependencies can be found here:
-`Netcope common <https://github.com/CESNET/ndk-sw>`_.
+Some cards employ more than one PCIe device for better data throughput. This can be achieved by
+slot bifurcation (only a minor improvement) or by an add-on cable connected to another PCIe slot.
+Both improvements can work together, as is, for example, in the case of the AGI-FH400G card.
-Versions of the packages
-~~~~~~~~~~~~~~~~~~~~~~~~
+Because primary and secondary slot(s) can be attached to different NUMA nodes
+(also applies for bifurcation on some HW), the data structures need to be correctly allocated.
+(Device-aware allocation matters also on IOMMU-enabled systems.)
+The firmware already provides DMA queue to PCI device mapping. The DPDK application just needs to
+use all PCI devices, otherwise some queues will not be available; provide all PCI endpoints
+listed in the `nfb-info -v` in the `allow` argument.
-The minimum version of the provided packages:
+.. note::
-* for DPDK from 19.05
+ For cards where the number of Ethernet ports is less than the number of PCI devices
+ (e.g., AGI-FH400G: 1 port, up to 4 PCI devices), the virtual DPDK ports are
+ created to achieve the best NUMA-aware throughput
+ (virtual ports lack a lot of configuration features).
-Configuration
--------------
+Features
+--------
Timestamps
+~~~~~~~~~~
-The PMD supports hardware timestamps of frame receipt on physical network interface. In order to use
-the timestamps, the hardware timestamping unit must be enabled (follow the documentation of the NFB
-products). The standard `RTE_ETH_RX_OFFLOAD_TIMESTAMP` flag can be used for this feature.
+The PMD supports hardware timestamps of frame receipt on physical network interface.
+In order to use the timestamps, the hardware timestamping unit must be enabled
+(follow the documentation of the NFB products).
+The standard `RTE_ETH_RX_OFFLOAD_TIMESTAMP` flag can be used for this feature.
When the timestamps are enabled, a timestamp validity flag is set in the MBUFs
containing received frames and timestamp is inserted into the `rte_mbuf` struct.
@@ -67,72 +104,18 @@ containing received frames and timestamp is inserted into the `rte_mbuf` struct.
The timestamp is an `uint64_t` field and holds the number of nanoseconds
elapsed since 1.1.1970 00:00:00 UTC.
+Simulation
+~~~~~~~~~~
-Using the NFB PMD
-----------------------
-
-Kernel modules have to be loaded before running the DPDK application.
-
-NFB card architecture
----------------------
-
-The NFB cards are multi-port multi-queue cards, where (generally) data from any
-Ethernet port may be sent to any queue.
-They are represented in DPDK as a single port.
-
-NFB-200G2QL card employs an add-on cable which allows to connect it to two
-physical PCI-E slots at the same time (see the diagram below).
-This is done to allow 200 Gbps of traffic to be transferred through the PCI-E
-bus (note that a single PCI-E 3.0 x16 slot provides only 125 Gbps theoretical
-throughput).
-
-Although each slot may be connected to a different CPU and therefore to a different
-NUMA node, the card is represented as a single port in DPDK. To work with data
-from the individual queues on the right NUMA node, connection of NUMA nodes on
-first and last queue (each NUMA node has half of the queues) need to be checked.
-
-Limitations
------------
-
-Driver is usable only on Linux architecture, namely on CentOS.
-
-Since a card is always represented as a single port, but can be connected to two
-NUMA nodes, there is need for manual check where master/slave is connected.
-
-Example of usage
-----------------
-
-Read packets from 0. and 1. receive queue and write them to 0. and 1.
-transmit queue:
+The CESNET-NDK framework offers the possibility of simulating the firmware together with DPDK.
+This allows for easy debugging of a packet flow behaviour with a specific firmware configuration.
+The DPDK NFB driver can be connected to the simulator (Questa/ModelSim/nvc) via a virtual device:
.. code-block:: console
- ./<build_dir>/app/dpdk-testpmd -l 0-3 -n 2 \
- -- --port-topology=chained --rxq=2 --txq=2 --nb-cores=2 -i -a
-
-Example output:
-
-.. code-block:: console
+ dpdk-testpmd
+ --vdev=eth_vdev_nfb,dev=libnfb-ext-grpc.so:grpc+dma_vas:localhost:50051,queue_driver=native
+ --iova-mode=va -- -i
- [...]
- EAL: PCI device 0000:06:00.0 on NUMA socket -1
- EAL: probe driver: 1b26:c1c1 net_nfb
- PMD: Initializing NFB device (0000:06:00.0)
- PMD: Available DMA queues RX: 8 TX: 8
- PMD: NFB device (0000:06:00.0) successfully initialized
- Interactive-mode selected
- Auto-start selected
- Configuring Port 0 (socket 0)
- Port 0: 00:11:17:00:00:00
- Checking link statuses...
- Port 0 Link Up - speed 10000 Mbps - full-duplex
- Done
- Start automatic packet forwarding
- io packet forwarding - CRC stripping disabled - packets/burst=32
- nb forwarding cores=2 - nb forwarding ports=1
- RX queues=2 - RX desc=128 - RX free threshold=0
- RX threshold registers: pthresh=0 hthresh=0 wthresh=0
- TX queues=2 - TX desc=512 - TX free threshold=0
- TX threshold registers: pthresh=0 hthresh=0 wthresh=0
- TX RS bit threshold=0 - TXQ flags=0x0
- testpmd>
+More info about the simulation can be found int the CESNET-NDK `documentation
+<https://cesnet.github.io/ndk-fpga/devel/ndk_apps/minimal/tests/cocotb/readme.html>`_.
diff --git a/doc/guides/rel_notes/release_26_03.rst b/doc/guides/rel_notes/release_26_03.rst
index 52663580e3..d484ca98d2 100644
--- a/doc/guides/rel_notes/release_26_03.rst
+++ b/doc/guides/rel_notes/release_26_03.rst
@@ -55,6 +55,15 @@ New Features
Also, make sure to start the actual text at the margin.
=======================================================
+* **Updated CESNET NFB ethernet driver.**
+
+ * The timestamp value has been updated to make it usable.
+ * The DPDK port now represents just one Ethernet port instead of all Ethernet ports on the NIC.
+ * All ports are used by default, but a subset can be selected using the ``port`` argument.
+ * Report firmware version.
+ * Common CESNET-NDK-based adapters have been added,
+ including the FB2CGHH (Silicom Denmark) and XpressSX AGI-FH400G (Reflex CES).
+
Removed Items
-------------
--
2.52.0
^ permalink raw reply related [flat|nested] 131+ messages in thread
* Re: [PATCH v4 0/8] net/nfb: rework to real multiport
2026-01-23 1:14 ` Stephen Hemminger
@ 2026-01-23 17:34 ` Martin Spinler
0 siblings, 0 replies; 131+ messages in thread
From: Martin Spinler @ 2026-01-23 17:34 UTC (permalink / raw)
To: Stephen Hemminger; +Cc: dev
Thanks for the review, my v5 changes are described in the cover letter.
In contrast (what is not changed):
- no patch is suitable for backporting
- NULL checks was just imagination of AI I think
- 64 ports ought to be enough for anybody -> reworked,
imho much cleaner code now
My reaction to Claude:
- AI is quiet good for commit messages generated from context
when I'm stuck (1/8) as well for doc (8/8).
- Memory leak is in patch 4/8, not in 2/8. But yes,
it was here and it was my fault.
- RTE_EXPORT_INTERNAL_SYMBOL seems necessary (sym_chk fails without it)
and the __rte_internal was already there.
- Prefixing in the TAILQ_HEAD(pmd_internals_head, pmd_internals):
it is bit misleading to me as it just defines struct
inside .cfile, anyway, I added the nfb_ prefix.
- Went through the rte_mallocs and they are used in
process-shared contexts, changed to calloc just in one case.
My reaction to Grok:
- I think just the Code duplication hint was relevant,
- as well as the Port selection logic.
On Thu, 2026-01-22 at 17:14 -0800, Stephen Hemminger wrote:
> On Thu, 22 Jan 2026 08:27:11 +0100
> spinler@cesnet.cz wrote:
>
> > From: Martin Spinler <spinler@cesnet.cz>
> >
> > This series implements real multiport for better user experience.
> >
> > The existing driver creates one ethdev/port for one PCI device.
> > As the CESNET-NDK based cards aren't capable to represent each
> > Ethernet port by own PCI device, new driver implementation
> > processes real port configuration from firmware/card and switches
> > from rte_eth_dev_pci_generic_probe to multiple rte_eth_dev_create calls.
> >
> > ---
>
> Wading through all the AI review...
>
> Things you should fix:
> - make sure all calls to snprintf() have overflow checks
> - clarity about what should be backported (Fixes/stable)
> - make sure all calls to rte_malloc et al have checks for NULL
>
> Things you could fix:
> - using malloc vs rte_malloc for data structures not shared
> not a big issue; but feel free to change.
> - avoiding code duplication
> - check prefix of global variables
> - mark driver only API's as __rte_internal
>
> Things I can fix when merging:
> - am willing to reword commit messages for readability as needed
>
> Things I don't care about:
> - release notes only have to be right after series, no need for per-patch
> - looks like all new functions are internal only, not sure why review wants tests.
> - assume 64 bit port mask is a hardware limit, you won't go over in future.
> - driver internal structures do not need doxygen comments
> - don't care about any AI warnings like "if you change X in the future it will break"
>
>
^ permalink raw reply [flat|nested] 131+ messages in thread
* [REVIEW] doc/nfb: cleanup and update guide
2026-01-23 17:22 ` [PATCH v5 8/8] doc/nfb: cleanup and update guide spinler
@ 2026-01-24 19:10 ` Stephen Hemminger
0 siblings, 0 replies; 131+ messages in thread
From: Stephen Hemminger @ 2026-01-24 19:10 UTC (permalink / raw)
To: dev; +Cc: Stephen Hemminger
AI-generated review of bundle-1688-nfb-multiport.mbox
Reviewed using Claude (claude-opus-4-5-20251101)
This is an automated review. Please verify all suggestions.
---
# DPDK Patch Review: bundle-1688-nfb-multiport.mbox
This patch series (8 patches) enhances the NFB driver to create one ethdev per physical Ethernet port instead of one ethdev per PCI device.
---
## Patch 1/8: net/nfb: prepare for indirect queue mapping scheme
### Commit Message
- **Subject**: `net/nfb: prepare for indirect queue mapping scheme` (49 chars) ✓
- **Format**: Correct prefix, lowercase, imperative mood ✓
- **Signed-off-by**: Present ✓
### Code Review
**Warning: Unnecessary cast of void * (rte_calloc returns void *)**
```c
priv->queue_map_rx = rte_calloc("NFB queue map", (max_rx_queues + max_tx_queues),
sizeof(*priv->queue_map_rx), 0);
```
This is actually fine - no cast is present. ✓
**Info: Variable declaration style**
```c
int i;
int ret;
```
The declaration at the top of the function is acceptable.
**Warning: Inconsistent loop variable types**
```c
for (i = 0; i < max_rx_queues; i++)
```
`i` is `int` but `max_rx_queues` is `uint16_t`. This works but could be cleaner with matching types.
---
## Patch 2/8: net/nfb: create one ethdev per ethernet port
### Commit Message
- **Subject**: `net/nfb: create one ethdev per ethernet port` (46 chars) ✓
- **Signed-off-by**: Present ✓
### Code Review
**Error: `__rte_internal` must be alone on the line preceding the return type**
In `drivers/net/nfb/nfb.h`:
```c
__rte_internal
int nfb_eth_common_probe(struct nfb_probe_params *params);
__rte_internal
int nfb_eth_common_remove(struct rte_device *dev);
```
These should be:
```c
__rte_internal
int
nfb_eth_common_probe(struct nfb_probe_params *params);
__rte_internal
int
nfb_eth_common_remove(struct rte_device *dev);
```
**Warning: Use of `snprintf` return value check pattern**
```c
ret = snprintf(pp->name + cp->basename_len, sizeof(pp->name) - cp->basename_len,
"_eth%d", ifc->id);
if (ret < 0 || ret >= (signed int)sizeof(pp->name) - cp->basename_len)
```
The cast to `(signed int)` is unnecessary and the calculation on the right side could overflow. Consider using a clearer pattern.
**Warning: Empty line before new struct member**
```c
struct pmd_internals {
...
struct nfb_device *nfb;
TAILQ_ENTRY(pmd_internals) eth_dev_list; /**< Item in list of all devices */
```
Inconsistent blank line usage within struct definition.
**Info: Static global initialization**
```c
static struct nfb_pmd_internals_head nfb_eth_dev_list =
TAILQ_HEAD_INITIALIZER(nfb_eth_dev_list);
```
This is acceptable.
---
## Patch 3/8: net/nfb: add vdev as alternative device probe method
### Commit Message
- **Subject**: `net/nfb: add vdev as alternative device probe method` (50 chars) ✓
- **Signed-off-by**: Present ✓
### Code Review
**Error: Missing SPDX on line 1 - actually present and correct** ✓
**Warning: Use of standard library `strdup` instead of DPDK equivalent**
```c
dev_params = strdup(vdev_args);
```
Consider using `rte_strdup()` or documenting why standard `strdup` is appropriate here.
**Warning: Use of standard library `free` instead of DPDK equivalent**
```c
free(dev_params);
```
Should use `rte_free()` if `rte_malloc()` family was used, but since `strdup` was used, `free` is correct.
**Info: Consistent error handling pattern** ✓
---
## Patch 4/8: net/nfb: add device argument "port" to limit used ports
### Commit Message
- **Subject**: `net/nfb: add device argument "port" to limit used ports` (53 chars) ✓
- **Signed-off-by**: Present ✓
### Code Review
**Warning: Missing include for `isdigit`**
```c
if (value == NULL || strlen(value) == 0 || !isdigit(*value))
```
Needs `#include <ctype.h>` for `isdigit()`.
**Warning: Implicit integer conversion**
```c
if (*end != '\0' || port >= (unsigned long)ifc_params->map_info.ifc_cnt)
```
The cast to `unsigned long` is explicit, which is good.
---
## Patch 5/8: net/nfb: init only MACs associated with device
### Commit Message
- **Subject**: `net/nfb: init only MACs associated with device` (46 chars) ✓
- **Signed-off-by**: Present ✓
### Code Review
**Warning: Use of standard library `calloc` and `free`**
```c
intl->rxmac = calloc(ifc->eth_cnt, sizeof(*intl->rxmac));
...
free(intl->txmac);
free(intl->rxmac);
```
Should consider using `rte_calloc()` and `rte_free()` for consistency with DPDK memory management, especially if these structures may be accessed in data path or shared between processes.
**Info: Loop variable type consistency**
```c
int i, rxm, txm;
...
for (i = 0; i < mi->eth_cnt; i++) {
```
Types are consistent within the function.
---
## Patch 6/8: net/nfb: add compatible cards to driver PCI ID table
### Commit Message
- **Subject**: `net/nfb: add compatible cards to driver PCI ID table` (52 chars) ✓
- **Signed-off-by**: Present ✓
### Code Review
No issues found. Simple addition of PCI IDs. ✓
---
## Patch 7/8: net/nfb: report firmware version
### Commit Message
- **Subject**: `net/nfb: report firmware version` (33 chars) ✓
- **Signed-off-by**: Present ✓
### Code Review
**Info: Good error handling pattern**
```c
if (proj_name == NULL)
proj_name = "";
if (proj_vers == NULL)
proj_vers = "";
```
Proper NULL pointer handling. ✓
**Info: Return value calculation**
```c
if (ret >= (signed int)fw_size)
return strlen(proj_name) + 1 + strlen(proj_vers) + 1;
```
Returns required buffer size when buffer is too small - follows DPDK convention. ✓
---
## Patch 8/8: doc/nfb: cleanup and update guide
### Commit Message
- **Subject**: `doc/nfb: cleanup and update guide` (35 chars) ✓
- **Signed-off-by**: Present ✓
### Documentation Review
**Info: Good documentation update**
- Added features to `features/nfb.ini` ✓
- Updated `nfb.rst` with current information ✓
- Added release notes entry ✓
**Warning: Release notes mention "timestamp value has been updated" but no timestamp changes in this series**
The release notes state:
```
* The timestamp value has been updated to make it usable.
```
But there are no timestamp-related code changes in this patch series. This may be referring to changes in a previous version or should be removed.
---
## Summary
### Errors (Must Fix)
1. **Patch 2**: `__rte_internal` functions in header file must have return type on separate line
### Warnings (Should Fix)
1. **Patch 2**: Empty line inconsistency in struct definition
2. **Patch 3**: Consider documenting use of standard `strdup`/`free`
3. **Patch 4**: Missing `#include <ctype.h>` for `isdigit()`
4. **Patch 5**: Consider using `rte_calloc()`/`rte_free()` instead of standard library functions
5. **Patch 8**: Release notes mention timestamp changes not present in this series
### Info (Consider)
1. Loop variable type consistency could be improved in some places
2. Overall code organization and error handling patterns are good
3. Documentation updates are comprehensive
### Overall Assessment
The patch series is well-structured and addresses a clear functional enhancement. The main blocking issue is the `__rte_internal` formatting in patch 2. Other warnings should be addressed for better code quality and DPDK consistency.
^ permalink raw reply [flat|nested] 131+ messages in thread
* Re: [PATCH v5 0/8] net/nfb: rework to real multiport
2026-01-23 17:22 ` [PATCH v5 " spinler
` (7 preceding siblings ...)
2026-01-23 17:22 ` [PATCH v5 8/8] doc/nfb: cleanup and update guide spinler
@ 2026-01-24 19:19 ` Stephen Hemminger
8 siblings, 0 replies; 131+ messages in thread
From: Stephen Hemminger @ 2026-01-24 19:19 UTC (permalink / raw)
To: spinler; +Cc: dev
On Fri, 23 Jan 2026 18:22:16 +0100
spinler@cesnet.cz wrote:
> From: Martin Spinler <spinler@cesnet.cz>
>
> This series implements real multiport for better user experience.
>
> The existing driver creates one ethdev/port for one PCI device.
> As the CESNET-NDK based cards aren't capable to represent each
> Ethernet port by own PCI device, new driver implementation
> processes real port configuration from firmware/card and switches
> from rte_eth_dev_pci_generic_probe to multiple rte_eth_dev_create calls.
>
> ---
> v5:
> * Fixed memory leak on error path.
> * Separated nfb_eth_dev_create_for_ifc() from nfb_eth_common_probe().
> * Reworked port matching to eliminate the port mask / 64b width and potential integer overflow.
> The nfb_eth_dev_create_for_ifc() is now called directly from rte_kvargs_process().
> * Renamed nfb_init_params to nfb_probe_params and updated the nfb_eth_common_probe() header.
> * Unified Rx+Tx MAC init and deinit functions to avoid code duplication.
> * calloc/free is now used instead of rte_calloc in Rx+Tx MAC init/deinit.
> * The return values of snprintf are now handled properly.
> * The return value of Rx+Tx MAC init is now handled properly.
> * The name of the struct type is now prefixed with nfb_.
> * Used a consistent error handling pattern (-EINVAL instead of -1).
> * Added descriptions to new struct members.
> * Some commit messages have been updated with better descriptions.
>
> Depends-on: series-37064 ("net/nfb: code cleanup")
>
> Martin Spinler (8):
> net/nfb: prepare for indirect queue mapping scheme
> net/nfb: create one ethdev per ethernet port
> net/nfb: add vdev as alternative device probe method
> net/nfb: add device argument "port" to limit used ports
> net/nfb: init only MACs associated with device
> net/nfb: add compatible cards to driver PCI ID table
> net/nfb: report firmware version
> doc/nfb: cleanup and update guide
>
> doc/guides/nics/features/nfb.ini | 4 +
> doc/guides/nics/nfb.rst | 187 +++++------
> doc/guides/rel_notes/release_26_03.rst | 9 +
> drivers/net/nfb/meson.build | 1 +
> drivers/net/nfb/nfb.h | 56 +++-
> drivers/net/nfb/nfb_ethdev.c | 444 ++++++++++++++++++-------
> drivers/net/nfb/nfb_rx.c | 30 +-
> drivers/net/nfb/nfb_rx.h | 9 +-
> drivers/net/nfb/nfb_tx.c | 27 +-
> drivers/net/nfb/nfb_tx.h | 7 +-
> drivers/net/nfb/nfb_vdev.c | 107 ++++++
> 11 files changed, 624 insertions(+), 257 deletions(-)
> create mode 100644 drivers/net/nfb/nfb_vdev.c
>
The AI review warnings are mostly false positive.
### Errors (Must Fix)
1. **Patch 2**: `__rte_internal` functions in header file must have return type on separate line
This is just the tool not understanding syntax and having two functions back to back
is confusing it.
### Warnings (Should Fix)
1. **Patch 2**: Empty line inconsistency in struct definition
meh, dont care
2. **Patch 3**: Consider documenting use of standard `strdup`/`free`
seems fine to me
3. **Patch 4**: Missing `#include <ctype.h>` for `isdigit()`
OK, should change but not a big deal. Do it if you want.
4. **Patch 5**: Consider using `rte_calloc()`/`rte_free()` instead of standard library functions
As long as not shared, standard heap is file.
5. **Patch 8**: Release notes mention timestamp changes not present in this series
Good to address.
^ permalink raw reply [flat|nested] 131+ messages in thread
* Re: [PATCH v5 2/8] net/nfb: create one ethdev per ethernet port
2026-01-23 17:22 ` [PATCH v5 2/8] net/nfb: create one ethdev per ethernet port spinler
@ 2026-01-27 0:37 ` Stephen Hemminger
2026-01-27 8:12 ` Martin Spinler
0 siblings, 1 reply; 131+ messages in thread
From: Stephen Hemminger @ 2026-01-27 0:37 UTC (permalink / raw)
To: spinler; +Cc: dev
On Fri, 23 Jan 2026 18:22:18 +0100
spinler@cesnet.cz wrote:
> +struct nfb_ifc_create_params {
> + struct nfb_probe_params *probe_params;
> + struct nc_ifc_map_info map_info;
> + struct nc_ifc_info *ifc_info;
> +
> + int basename_len; /* Cached real length of original probe_params->name */
> +};
> +
> +/* The TAILQ entries are used for cleanup of allocated resources
> + * in situations, where dev_close is not called.
> + */
> +TAILQ_HEAD(nfb_pmd_internals_head, pmd_internals);
You should put static in front of TAILQ_HEAD so that the head is not
global if you don't need it to be.
> +static struct nfb_pmd_internals_head nfb_eth_dev_list =
> + TAILQ_HEAD_INITIALIZER(nfb_eth_dev_list);
> +
^ permalink raw reply [flat|nested] 131+ messages in thread
* Re: [PATCH v5 2/8] net/nfb: create one ethdev per ethernet port
2026-01-27 0:37 ` Stephen Hemminger
@ 2026-01-27 8:12 ` Martin Spinler
2026-01-27 14:09 ` Stephen Hemminger
0 siblings, 1 reply; 131+ messages in thread
From: Martin Spinler @ 2026-01-27 8:12 UTC (permalink / raw)
To: Stephen Hemminger; +Cc: dev
On Mon, 2026-01-26 at 16:37 -0800, Stephen Hemminger wrote:
> > +TAILQ_HEAD(nfb_pmd_internals_head, pmd_internals);
>
> You should put static in front of TAILQ_HEAD so that the head is not
> global if you don't need it to be.
>
> > +static struct nfb_pmd_internals_head nfb_eth_dev_list =
> > + TAILQ_HEAD_INITIALIZER(nfb_eth_dev_list);
> > +
I don't understand this. The static is already in front of the
nfb_eth_dev_list global variable.
The TAILQ_HEAD() just defines a struct (not a variable), which is
visible just in the one .c file.
When I use static, as you suggest (or as I think you suggest):
static TAILQ_HEAD(nfb_pmd_internals_head, pmd_internals);
$ ninja
[2/30] Compiling C object ... net_nfb_nfb_ethdev.c.o
...nfb_ethdev.c:49:1: warning: useless storage class specifier in empty
declaration
^ permalink raw reply [flat|nested] 131+ messages in thread
* Re: [PATCH v5 2/8] net/nfb: create one ethdev per ethernet port
2026-01-27 8:12 ` Martin Spinler
@ 2026-01-27 14:09 ` Stephen Hemminger
0 siblings, 0 replies; 131+ messages in thread
From: Stephen Hemminger @ 2026-01-27 14:09 UTC (permalink / raw)
To: Martin Spinler; +Cc: dev
On Tue, 27 Jan 2026 09:12:21 +0100
Martin Spinler <spinler@cesnet.cz> wrote:
> On Mon, 2026-01-26 at 16:37 -0800, Stephen Hemminger wrote:
> > > +TAILQ_HEAD(nfb_pmd_internals_head, pmd_internals);
> >
> > You should put static in front of TAILQ_HEAD so that the head is not
> > global if you don't need it to be.
> >
> > > +static struct nfb_pmd_internals_head nfb_eth_dev_list =
> > > + TAILQ_HEAD_INITIALIZER(nfb_eth_dev_list);
> > > +
>
> I don't understand this. The static is already in front of the
> nfb_eth_dev_list global variable.
>
>
> The TAILQ_HEAD() just defines a struct (not a variable), which is
> visible just in the one .c file.
>
> When I use static, as you suggest (or as I think you suggest):
> static TAILQ_HEAD(nfb_pmd_internals_head, pmd_internals);
> $ ninja
> [2/30] Compiling C object ... net_nfb_nfb_ethdev.c.o
> ...nfb_ethdev.c:49:1: warning: useless storage class specifier in empty
> declaration
Your right, I just saw that in other drivers.
^ permalink raw reply [flat|nested] 131+ messages in thread
* [PATCH v6 0/8] net/nfb: rework to real multiport
2026-01-15 15:16 [PATCH 0/8] net/nfb: rework to real multiport spinler
` (12 preceding siblings ...)
2026-01-23 17:22 ` [PATCH v5 " spinler
@ 2026-02-02 15:34 ` spinler
2026-02-02 15:34 ` [PATCH v6 1/8] net/nfb: prepare for indirect queue mapping scheme spinler
` (7 more replies)
2026-02-04 12:31 ` [PATCH v7 0/8] net/nfb: rework to real multiport spinler
` (3 subsequent siblings)
17 siblings, 8 replies; 131+ messages in thread
From: spinler @ 2026-02-02 15:34 UTC (permalink / raw)
To: dev; +Cc: Martin Spinler
From: Martin Spinler <spinler@cesnet.cz>
This series implements real multiport for better user experience.
The existing driver creates one ethdev/port for one PCI device.
As the CESNET-NDK based cards aren't capable to represent each
Ethernet port by own PCI device, new driver implementation
processes real port configuration from firmware/card and switches
from rte_eth_dev_pci_generic_probe to multiple rte_eth_dev_create calls.
---
Depends-on: patch-37240 ("doc/nfb: update release notes for nfb driver")
v6:
* Rebased on patch-37240
* Timestamp changes removed from release log changes for this series.
* Fixed missing include for isdigit()
Martin Spinler (8):
net/nfb: prepare for indirect queue mapping scheme
net/nfb: create one ethdev per ethernet port
net/nfb: add vdev as alternative device probe method
net/nfb: add device argument "port" to limit used ports
net/nfb: init only MACs associated with device
net/nfb: add compatible cards to driver PCI ID table
net/nfb: report firmware version
doc/nfb: cleanup and update guide
doc/guides/nics/features/nfb.ini | 4 +
doc/guides/nics/nfb.rst | 187 +++++------
doc/guides/rel_notes/release_26_03.rst | 6 +
drivers/net/nfb/meson.build | 1 +
drivers/net/nfb/nfb.h | 56 +++-
drivers/net/nfb/nfb_ethdev.c | 445 +++++++++++++++++++------
drivers/net/nfb/nfb_rx.c | 30 +-
drivers/net/nfb/nfb_rx.h | 9 +-
drivers/net/nfb/nfb_tx.c | 27 +-
drivers/net/nfb/nfb_tx.h | 7 +-
drivers/net/nfb/nfb_vdev.c | 107 ++++++
11 files changed, 622 insertions(+), 257 deletions(-)
create mode 100644 drivers/net/nfb/nfb_vdev.c
--
2.52.0
^ permalink raw reply [flat|nested] 131+ messages in thread
* [PATCH v6 1/8] net/nfb: prepare for indirect queue mapping scheme
2026-02-02 15:34 ` [PATCH v6 " spinler
@ 2026-02-02 15:34 ` spinler
2026-02-02 17:50 ` Stephen Hemminger
2026-02-02 15:34 ` [PATCH v6 2/8] net/nfb: create one ethdev per ethernet port spinler
` (6 subsequent siblings)
7 siblings, 1 reply; 131+ messages in thread
From: spinler @ 2026-02-02 15:34 UTC (permalink / raw)
To: dev; +Cc: Martin Spinler
From: Martin Spinler <spinler@cesnet.cz>
The NFB driver is being enhanced to create one ethdev per physical
Ethernet port instead of one ethdev per PCI device. This requires
an indirect mapping layer because DPDK queue indices no longer
directly correspond to firmware queue indices.
This change adds queue_map_rx and queue_map_tx arrays to track the
mapping between DPDK queue indices and firmware queue IDs, preparing
for the multi-port implementation in subsequent patches.
Signed-off-by: Martin Spinler <spinler@cesnet.cz>
---
drivers/net/nfb/nfb.h | 5 +++++
drivers/net/nfb/nfb_ethdev.c | 21 +++++++++++++++++++++
drivers/net/nfb/nfb_rx.c | 30 +++++++++++++++---------------
drivers/net/nfb/nfb_rx.h | 9 ++++-----
drivers/net/nfb/nfb_tx.c | 27 +++++++++++++++------------
drivers/net/nfb/nfb_tx.h | 7 +++----
6 files changed, 63 insertions(+), 36 deletions(-)
diff --git a/drivers/net/nfb/nfb.h b/drivers/net/nfb/nfb.h
index 90b04c6151..6b74b3e6ec 100644
--- a/drivers/net/nfb/nfb.h
+++ b/drivers/net/nfb/nfb.h
@@ -60,6 +60,11 @@ struct pmd_internals {
struct pmd_priv {
uint16_t max_rx_queues;
uint16_t max_tx_queues;
+
+ /** Mapping from DPDK RX queue index to firmware queue ID */
+ int *queue_map_rx;
+ /** Mapping from DPDK TX queue index to firmware queue ID */
+ int *queue_map_tx;
};
#endif /* _NFB_H_ */
diff --git a/drivers/net/nfb/nfb_ethdev.c b/drivers/net/nfb/nfb_ethdev.c
index 947ee9e21d..803815138c 100644
--- a/drivers/net/nfb/nfb_ethdev.c
+++ b/drivers/net/nfb/nfb_ethdev.c
@@ -509,6 +509,7 @@ static const struct eth_dev_ops ops = {
static int
nfb_eth_dev_init(struct rte_eth_dev *dev)
{
+ int i;
int ret;
uint32_t mac_count;
struct rte_eth_dev_data *data = dev->data;
@@ -577,6 +578,20 @@ nfb_eth_dev_init(struct rte_eth_dev *dev)
priv->max_rx_queues = max_rx_queues;
priv->max_tx_queues = max_tx_queues;
+ priv->queue_map_rx = rte_calloc("NFB queue map", (max_rx_queues + max_tx_queues),
+ sizeof(*priv->queue_map_rx), 0);
+ if (priv->queue_map_rx == NULL) {
+ ret = -ENOMEM;
+ goto err_alloc_queue_map;
+ }
+ priv->queue_map_tx = priv->queue_map_rx + max_rx_queues;
+
+ /* default queue mapping is 1:1 */
+ for (i = 0; i < max_rx_queues; i++)
+ priv->queue_map_rx[i] = i;
+ for (i = 0; i < max_tx_queues; i++)
+ priv->queue_map_tx[i] = i;
+
/* Allocate space for MAC addresses */
mac_count = nfb_eth_get_max_mac_address_count(dev);
data->mac_addrs = rte_zmalloc(data->name,
@@ -608,6 +623,8 @@ nfb_eth_dev_init(struct rte_eth_dev *dev)
return 0;
err_malloc_mac_addrs:
+ rte_free(priv->queue_map_rx);
+err_alloc_queue_map:
nfb_nc_rxmac_deinit(internals->rxmac, internals->max_rxmac);
nfb_nc_txmac_deinit(internals->txmac, internals->max_txmac);
nfb_close(internals->nfb);
@@ -633,6 +650,10 @@ nfb_eth_dev_uninit(struct rte_eth_dev *dev)
struct rte_pci_device *pci_dev = RTE_ETH_DEV_TO_PCI(dev);
struct rte_pci_addr *pci_addr = &pci_dev->addr;
struct pmd_internals *internals = dev->process_private;
+ struct pmd_priv *priv = dev->data->dev_private;
+
+ if (rte_eal_process_type() == RTE_PROC_PRIMARY)
+ rte_free(priv->queue_map_rx);
nfb_nc_rxmac_deinit(internals->rxmac, internals->max_rxmac);
nfb_nc_txmac_deinit(internals->txmac, internals->max_txmac);
diff --git a/drivers/net/nfb/nfb_rx.c b/drivers/net/nfb/nfb_rx.c
index 413d275853..ff630f32f4 100644
--- a/drivers/net/nfb/nfb_rx.c
+++ b/drivers/net/nfb/nfb_rx.c
@@ -61,12 +61,13 @@ nfb_eth_rx_queue_setup(struct rte_eth_dev *dev,
struct rte_mempool *mb_pool)
{
struct pmd_internals *internals = dev->process_private;
+ struct pmd_priv *priv = dev->data->dev_private;
- struct ndp_rx_queue *rxq;
int ret;
+ int qid;
+ struct ndp_rx_queue *rxq;
- rxq = rte_zmalloc_socket("ndp rx queue",
- sizeof(struct ndp_rx_queue),
+ rxq = rte_zmalloc_socket("ndp rx queue", sizeof(struct ndp_rx_queue),
RTE_CACHE_LINE_SIZE, socket_id);
if (rxq == NULL) {
@@ -77,23 +78,23 @@ nfb_eth_rx_queue_setup(struct rte_eth_dev *dev,
rxq->flags = 0;
- ret = nfb_eth_rx_queue_init(internals->nfb,
- rx_queue_id,
- dev->data->port_id,
- mb_pool,
- rxq);
+ qid = priv->queue_map_rx[rx_queue_id];
- if (ret == 0)
- dev->data->rx_queues[rx_queue_id] = rxq;
- else
- rte_free(rxq);
+ ret = nfb_eth_rx_queue_init(internals->nfb, qid, dev->data->port_id, mb_pool, rxq);
+ if (ret)
+ goto err_queue_init;
+ dev->data->rx_queues[rx_queue_id] = rxq;
+ return 0;
+
+err_queue_init:
+ rte_free(rxq);
return ret;
}
int
nfb_eth_rx_queue_init(struct nfb_device *nfb,
- uint16_t rx_queue_id,
+ int qid,
uint16_t port_id,
struct rte_mempool *mb_pool,
struct ndp_rx_queue *rxq)
@@ -104,12 +105,11 @@ nfb_eth_rx_queue_init(struct nfb_device *nfb,
if (nfb == NULL)
return -EINVAL;
- rxq->queue = ndp_open_rx_queue(nfb, rx_queue_id);
+ rxq->queue = ndp_open_rx_queue(nfb, qid);
if (rxq->queue == NULL)
return -EINVAL;
rxq->nfb = nfb;
- rxq->rx_queue_id = rx_queue_id;
rxq->in_port = port_id;
rxq->mb_pool = mb_pool;
rxq->buf_size = (uint16_t)(mbp_priv->mbuf_data_room_size -
diff --git a/drivers/net/nfb/nfb_rx.h b/drivers/net/nfb/nfb_rx.h
index 67b3b00e2a..831945c577 100644
--- a/drivers/net/nfb/nfb_rx.h
+++ b/drivers/net/nfb/nfb_rx.h
@@ -30,7 +30,6 @@ nfb_timestamp_dynfield(struct rte_mbuf *mbuf)
struct ndp_rx_queue {
struct nfb_device *nfb; /* nfb dev structure */
struct ndp_queue *queue; /* rx queue */
- uint16_t rx_queue_id; /* index */
uint8_t in_port; /* port */
uint8_t flags; /* setup flags */
@@ -47,8 +46,8 @@ struct ndp_rx_queue {
*
* @param nfb
* Pointer to nfb device structure.
- * @param rx_queue_id
- * RX queue index.
+ * @param qid
+ * RX queue ID.
* @param port_id
* Device [external] port identifier.
* @param mb_pool
@@ -60,7 +59,7 @@ struct ndp_rx_queue {
*/
int
nfb_eth_rx_queue_init(struct nfb_device *nfb,
- uint16_t rx_queue_id,
+ int qid,
uint16_t port_id,
struct rte_mempool *mb_pool,
struct ndp_rx_queue *rxq);
@@ -70,7 +69,7 @@ nfb_eth_rx_queue_init(struct nfb_device *nfb,
*
* @param dev
* Pointer to Ethernet device structure.
- * @param idx
+ * @param rx_queue_id
* RX queue index.
* @param desc
* Number of descriptors to configure in queue.
diff --git a/drivers/net/nfb/nfb_tx.c b/drivers/net/nfb/nfb_tx.c
index 1f997ce22f..b5cbedeaf6 100644
--- a/drivers/net/nfb/nfb_tx.c
+++ b/drivers/net/nfb/nfb_tx.c
@@ -54,11 +54,13 @@ nfb_eth_tx_queue_setup(struct rte_eth_dev *dev,
const struct rte_eth_txconf *tx_conf __rte_unused)
{
struct pmd_internals *internals = dev->process_private;
+ struct pmd_priv *priv = dev->data->dev_private;
+
int ret;
+ int qid;
struct ndp_tx_queue *txq;
- txq = rte_zmalloc_socket("ndp tx queue",
- sizeof(struct ndp_tx_queue),
+ txq = rte_zmalloc_socket("ndp tx queue", sizeof(struct ndp_tx_queue),
RTE_CACHE_LINE_SIZE, socket_id);
if (txq == NULL) {
@@ -67,32 +69,33 @@ nfb_eth_tx_queue_setup(struct rte_eth_dev *dev,
return -ENOMEM;
}
- ret = nfb_eth_tx_queue_init(internals->nfb,
- tx_queue_id,
- txq);
+ qid = priv->queue_map_tx[tx_queue_id];
- if (ret == 0)
- dev->data->tx_queues[tx_queue_id] = txq;
- else
- rte_free(txq);
+ ret = nfb_eth_tx_queue_init(internals->nfb, qid, txq);
+ if (ret)
+ goto err_queue_init;
+ dev->data->tx_queues[tx_queue_id] = txq;
+ return 0;
+
+err_queue_init:
+ rte_free(txq);
return ret;
}
int
nfb_eth_tx_queue_init(struct nfb_device *nfb,
- uint16_t tx_queue_id,
+ int qid,
struct ndp_tx_queue *txq)
{
if (nfb == NULL)
return -EINVAL;
- txq->queue = ndp_open_tx_queue(nfb, tx_queue_id);
+ txq->queue = ndp_open_tx_queue(nfb, qid);
if (txq->queue == NULL)
return -EINVAL;
txq->nfb = nfb;
- txq->tx_queue_id = tx_queue_id;
txq->tx_pkts = 0;
txq->tx_bytes = 0;
diff --git a/drivers/net/nfb/nfb_tx.h b/drivers/net/nfb/nfb_tx.h
index f107cf914b..c253af1a86 100644
--- a/drivers/net/nfb/nfb_tx.h
+++ b/drivers/net/nfb/nfb_tx.h
@@ -17,7 +17,6 @@
struct ndp_tx_queue {
struct nfb_device *nfb; /* nfb dev structure */
struct ndp_queue *queue; /* tx queue */
- uint16_t tx_queue_id; /* index */
volatile uint64_t tx_pkts; /* packets transmitted */
volatile uint64_t tx_bytes; /* bytes transmitted */
volatile uint64_t err_pkts; /* erroneous packets */
@@ -54,8 +53,8 @@ nfb_eth_tx_queue_setup(struct rte_eth_dev *dev,
*
* @param nfb
* Pointer to nfb device structure.
- * @param tx_queue_id
- * TX queue index.
+ * @param qid
+ * TX queue ID.
* @param[out] txq
* Pointer to ndp_tx_queue output structure
*
@@ -64,7 +63,7 @@ nfb_eth_tx_queue_setup(struct rte_eth_dev *dev,
*/
int
nfb_eth_tx_queue_init(struct nfb_device *nfb,
- uint16_t tx_queue_id,
+ int qid,
struct ndp_tx_queue *txq);
/**
--
2.52.0
^ permalink raw reply related [flat|nested] 131+ messages in thread
* [PATCH v6 2/8] net/nfb: create one ethdev per ethernet port
2026-02-02 15:34 ` [PATCH v6 " spinler
2026-02-02 15:34 ` [PATCH v6 1/8] net/nfb: prepare for indirect queue mapping scheme spinler
@ 2026-02-02 15:34 ` spinler
2026-02-02 15:34 ` [PATCH v6 3/8] net/nfb: add vdev as alternative device probe method spinler
` (5 subsequent siblings)
7 siblings, 0 replies; 131+ messages in thread
From: spinler @ 2026-02-02 15:34 UTC (permalink / raw)
To: dev; +Cc: Martin Spinler
From: Martin Spinler <spinler@cesnet.cz>
The existing driver creates one ethdev/port for one PCI device.
As the CESNET-NDK-based cards are mostly unable to represent
each physical port with one PCI device, this atypical behaviour
often limits the user.
The nfb-framework comes with generic port representation API,
which provides information about ports and their mapping to firmware
resource (physical ports, DMA queues, processing pipelines).
This driver design switches from one rte_eth_dev_pci_generic_probe call
to multiple rte_eth_dev_create calls, while parses the firmware mapping
information.
Signed-off-by: Martin Spinler <spinler@cesnet.cz>
---
drivers/net/nfb/nfb.h | 27 +++++
drivers/net/nfb/nfb_ethdev.c | 215 ++++++++++++++++++++++++++++-------
2 files changed, 201 insertions(+), 41 deletions(-)
diff --git a/drivers/net/nfb/nfb.h b/drivers/net/nfb/nfb.h
index 6b74b3e6ec..a1134d7786 100644
--- a/drivers/net/nfb/nfb.h
+++ b/drivers/net/nfb/nfb.h
@@ -11,6 +11,7 @@
#include <nfb/ndp.h>
#include <netcope/rxmac.h>
#include <netcope/txmac.h>
+#include <netcope/info.h>
extern int nfb_logtype;
#define RTE_LOGTYPE_NFB nfb_logtype
@@ -51,6 +52,9 @@ struct pmd_internals {
struct nc_rxmac *rxmac[RTE_MAX_NC_RXMAC];
struct nc_txmac *txmac[RTE_MAX_NC_TXMAC];
struct nfb_device *nfb;
+
+ TAILQ_ENTRY(pmd_internals) eth_dev_list; /**< Item in list of all devices */
+ struct rte_eth_dev *eth_dev; /**< Handle for matching the device being removed */
};
/*
@@ -67,4 +71,27 @@ struct pmd_priv {
int *queue_map_tx;
};
+/* Data for common device probing */
+struct nfb_probe_params {
+ /** Generic device information */
+ struct rte_device *device;
+ /** Port bus specific initialisation callback function */
+ ethdev_bus_specific_init specific_init;
+ /** Port bus specific initialisation parameters */
+ void *specific_device;
+
+ const char *path; /**< libnfb device string */
+ const char *args; /**< Device arguments */
+ int nfb_id; /**< ID of NFB device in system */
+ int ep_index; /**< (PCIe) endpoint index for matching assigned interfaces */
+
+ char name[RTE_DEV_NAME_MAX_LEN]; /**< Probed name (e.g. PCI device name) */
+};
+
+
+__rte_internal
+int nfb_eth_common_probe(struct nfb_probe_params *params);
+__rte_internal
+int nfb_eth_common_remove(struct rte_device *dev);
+
#endif /* _NFB_H_ */
diff --git a/drivers/net/nfb/nfb_ethdev.c b/drivers/net/nfb/nfb_ethdev.c
index 803815138c..6eb0cd08fd 100644
--- a/drivers/net/nfb/nfb_ethdev.c
+++ b/drivers/net/nfb/nfb_ethdev.c
@@ -4,6 +4,10 @@
* All rights reserved.
*/
+#include <sys/queue.h>
+#include <eal_export.h>
+#include <rte_tailq.h>
+
#include <nfb/nfb.h>
#include <nfb/ndp.h>
#include <netcope/rxmac.h>
@@ -18,6 +22,21 @@
#include "nfb_rxmode.h"
#include "nfb.h"
+struct nfb_ifc_create_params {
+ struct nfb_probe_params *probe_params;
+ struct nc_ifc_map_info map_info;
+ struct nc_ifc_info *ifc_info;
+
+ int basename_len; /* Cached real length of original probe_params->name */
+};
+
+/* The TAILQ entries are used for cleanup of allocated resources
+ * in situations, where dev_close is not called.
+ */
+TAILQ_HEAD(nfb_pmd_internals_head, pmd_internals);
+static struct nfb_pmd_internals_head nfb_eth_dev_list =
+ TAILQ_HEAD_INITIALIZER(nfb_eth_dev_list);
+
static int nfb_eth_dev_uninit(struct rte_eth_dev *dev);
/**
@@ -507,23 +526,20 @@ static const struct eth_dev_ops ops = {
* 0 on success, a negative errno value otherwise.
*/
static int
-nfb_eth_dev_init(struct rte_eth_dev *dev)
+nfb_eth_dev_init(struct rte_eth_dev *dev, void *init_data)
{
int i;
+ int cnt;
int ret;
uint32_t mac_count;
struct rte_eth_dev_data *data = dev->data;
struct pmd_internals *internals;
+ struct nfb_ifc_create_params *params = init_data;
+ struct nc_ifc_info *ifc = params->ifc_info;
+ struct nc_ifc_map_info *mi = ¶ms->map_info;
struct pmd_priv *priv = data->dev_private;
- struct rte_pci_device *pci_dev = RTE_ETH_DEV_TO_PCI(dev);
- struct rte_pci_addr *pci_addr = &pci_dev->addr;
struct rte_ether_addr eth_addr_init;
uint16_t max_rx_queues, max_tx_queues;
- char nfb_dev[PATH_MAX];
-
- NFB_LOG(INFO, "Initializing NFB device (" PCI_PRI_FMT ")",
- pci_addr->domain, pci_addr->bus, pci_addr->devid,
- pci_addr->function);
internals = rte_zmalloc_socket("nfb_internals",
sizeof(struct pmd_internals), RTE_CACHE_LINE_SIZE,
@@ -535,27 +551,17 @@ nfb_eth_dev_init(struct rte_eth_dev *dev)
dev->process_private = internals;
- snprintf(nfb_dev, sizeof(nfb_dev),
- "/dev/nfb/by-pci-slot/" PCI_PRI_FMT,
- pci_addr->domain, pci_addr->bus, pci_addr->devid,
- pci_addr->function);
-
- /*
- * Get number of available DMA RX and TX queues, which is maximum
- * number of queues that can be created and store it in private device
- * data structure.
- */
- internals->nfb = nfb_open(nfb_dev);
+ /* Open device handle */
+ internals->nfb = nfb_open(params->probe_params->path);
if (internals->nfb == NULL) {
- NFB_LOG(ERR, "nfb_open(): failed to open %s", nfb_dev);
+ NFB_LOG(ERR, "nfb_open(): failed to open %s", params->probe_params->path);
ret = -EINVAL;
goto err_nfb_open;
}
- max_rx_queues = ndp_get_rx_queue_available_count(internals->nfb);
- max_tx_queues = ndp_get_tx_queue_available_count(internals->nfb);
- NFB_LOG(INFO, "Available NDP queues RX: %u TX: %u",
- max_rx_queues, max_tx_queues);
+ /* Get number of available DMA RX and TX queues */
+ max_rx_queues = ifc->rxq_cnt;
+ max_tx_queues = ifc->txq_cnt;
nfb_nc_rxmac_init(internals->nfb,
internals->rxmac,
@@ -586,11 +592,18 @@ nfb_eth_dev_init(struct rte_eth_dev *dev)
}
priv->queue_map_tx = priv->queue_map_rx + max_rx_queues;
- /* default queue mapping is 1:1 */
- for (i = 0; i < max_rx_queues; i++)
- priv->queue_map_rx[i] = i;
- for (i = 0; i < max_tx_queues; i++)
- priv->queue_map_tx[i] = i;
+ /* Use queue mapping provided by libnfb */
+ cnt = 0;
+ for (i = 0; i < mi->rxq_cnt; i++) {
+ if (mi->rxq[i].ifc == ifc->id)
+ priv->queue_map_rx[cnt++] = mi->rxq[i].id;
+ }
+
+ cnt = 0;
+ for (i = 0; i < mi->txq_cnt; i++) {
+ if (mi->txq[i].ifc == ifc->id)
+ priv->queue_map_tx[cnt++] = mi->txq[i].id;
+ }
/* Allocate space for MAC addresses */
mac_count = nfb_eth_get_max_mac_address_count(dev);
@@ -616,10 +629,6 @@ nfb_eth_dev_init(struct rte_eth_dev *dev)
data->dev_flags |= RTE_ETH_DEV_AUTOFILL_QUEUE_XSTATS;
}
- NFB_LOG(INFO, "NFB device (" PCI_PRI_FMT ") successfully initialized",
- pci_addr->domain, pci_addr->bus, pci_addr->devid,
- pci_addr->function);
-
return 0;
err_malloc_mac_addrs:
@@ -647,25 +656,104 @@ nfb_eth_dev_init(struct rte_eth_dev *dev)
static int
nfb_eth_dev_uninit(struct rte_eth_dev *dev)
{
- struct rte_pci_device *pci_dev = RTE_ETH_DEV_TO_PCI(dev);
- struct rte_pci_addr *pci_addr = &pci_dev->addr;
struct pmd_internals *internals = dev->process_private;
struct pmd_priv *priv = dev->data->dev_private;
if (rte_eal_process_type() == RTE_PROC_PRIMARY)
rte_free(priv->queue_map_rx);
+ TAILQ_REMOVE(&nfb_eth_dev_list, internals, eth_dev_list);
+
nfb_nc_rxmac_deinit(internals->rxmac, internals->max_rxmac);
nfb_nc_txmac_deinit(internals->txmac, internals->max_txmac);
nfb_close(internals->nfb);
rte_free(internals);
- NFB_LOG(INFO, "NFB device (" PCI_PRI_FMT ") successfully uninitialized",
- pci_addr->domain, pci_addr->bus, pci_addr->devid,
- pci_addr->function);
+ return 0;
+}
+
+static int
+nfb_eth_dev_create_for_ifc(struct nfb_ifc_create_params *cp)
+{
+ int ret;
+ struct nc_ifc_info *ifc;
+ struct nfb_probe_params *pp;
+ struct rte_eth_dev *eth_dev;
+ struct pmd_internals *p;
+
+ ifc = cp->ifc_info;
+ pp = cp->probe_params;
+
+ /* Skip interfaces which doesn't belong to the probed PCI device */
+ if ((pp->ep_index != -1 && ifc->ep != pp->ep_index) ||
+ (ifc->flags & NC_IFC_INFO_FLAG_ACTIVE) == 0)
+ return 0;
+
+ ret = snprintf(pp->name + cp->basename_len, sizeof(pp->name) - cp->basename_len,
+ "_eth%d", ifc->id);
+ if (ret < 0 || ret >= (signed int)sizeof(pp->name) - cp->basename_len)
+ return -EINVAL;
+
+ ret = rte_eth_dev_create(pp->device, pp->name,
+ sizeof(struct pmd_priv),
+ pp->specific_init, pp->specific_device,
+ nfb_eth_dev_init, cp);
+ if (ret)
+ goto out;
+
+ eth_dev = rte_eth_dev_get_by_name(pp->name);
+ p = eth_dev->process_private;
+ p->eth_dev = eth_dev;
+ TAILQ_INSERT_TAIL(&nfb_eth_dev_list, p, eth_dev_list);
+
+out:
+ /* return to original name (just for clarity) */
+ pp->name[cp->basename_len] = '\0';
+ return ret;
+}
+
+RTE_EXPORT_INTERNAL_SYMBOL(nfb_eth_common_probe)
+int
+nfb_eth_common_probe(struct nfb_probe_params *params)
+{
+ int i;
+ int ret;
+
+ struct nfb_device *nfb_dev;
+ struct nfb_ifc_create_params ifc_params;
+
+ nfb_dev = nfb_open(params->path);
+ if (nfb_dev == NULL) {
+ NFB_LOG(ERR, "nfb_open(): failed to open %s", params->path);
+ return -EINVAL;
+ }
+
+ ret = nc_ifc_map_info_create_ordinary(nfb_dev, &ifc_params.map_info);
+ if (ret)
+ goto err_map_info_create;
+
+ ifc_params.probe_params = params;
+ ifc_params.basename_len = strlen(params->name);
+
+ for (i = 0; i < ifc_params.map_info.ifc_cnt; i++) {
+ ifc_params.ifc_info = &ifc_params.map_info.ifc[i];
+ ret = nfb_eth_dev_create_for_ifc(&ifc_params);
+ if (ret)
+ goto err_dev_create;
+ }
+
+ nc_map_info_destroy(&ifc_params.map_info);
+ nfb_close(nfb_dev);
return 0;
+
+err_dev_create:
+ nfb_eth_common_remove(params->device);
+ nc_map_info_destroy(&ifc_params.map_info);
+err_map_info_create:
+ nfb_close(nfb_dev);
+ return ret;
}
static const struct rte_pci_id nfb_pci_id_table[] = {
@@ -694,8 +782,53 @@ static int
nfb_eth_pci_probe(struct rte_pci_driver *pci_drv __rte_unused,
struct rte_pci_device *pci_dev)
{
- return rte_eth_dev_pci_generic_probe(pci_dev,
- sizeof(struct pmd_priv), nfb_eth_dev_init);
+ int ret;
+ char path[PATH_MAX];
+
+ struct nc_composed_device_info comp_dev_info;
+ struct nfb_probe_params params = {0};
+
+ rte_pci_device_name(&pci_dev->addr, params.name, sizeof(params.name));
+
+ /*
+ * NFB device can be composed from multiple PCI devices,
+ * find the base char device ID for the current PCI device
+ */
+ ret = nc_get_composed_device_info_by_pci(NULL, params.name, &comp_dev_info);
+ if (ret) {
+ NFB_LOG(ERR, "Could not find NFB device for %s", params.name);
+ return -ENODEV;
+ }
+
+ ret = snprintf(path, sizeof(path), NFB_BASE_DEV_PATH "%d", comp_dev_info.nfb_id);
+ if (ret < 0 || ret >= (signed int)sizeof(path))
+ return -EINVAL;
+
+ params.args = pci_dev->device.devargs ? pci_dev->device.devargs->args : NULL;
+ params.path = path;
+ params.nfb_id = comp_dev_info.nfb_id;
+ params.ep_index = comp_dev_info.ep_index;
+
+ params.device = &pci_dev->device;
+ params.specific_init = eth_dev_pci_specific_init;
+ params.specific_device = pci_dev;
+
+ return nfb_eth_common_probe(¶ms);
+}
+
+RTE_EXPORT_INTERNAL_SYMBOL(nfb_eth_common_remove)
+int
+nfb_eth_common_remove(struct rte_device *dev)
+{
+ struct pmd_internals *entry, *temp;
+
+ RTE_TAILQ_FOREACH_SAFE(entry, &nfb_eth_dev_list, eth_dev_list, temp) {
+ if (dev == entry->eth_dev->device) {
+ TAILQ_REMOVE(&nfb_eth_dev_list, entry, eth_dev_list);
+ rte_eth_dev_destroy(entry->eth_dev, nfb_eth_dev_uninit);
+ }
+ }
+ return 0;
}
/**
@@ -712,7 +845,7 @@ nfb_eth_pci_probe(struct rte_pci_driver *pci_drv __rte_unused,
static int
nfb_eth_pci_remove(struct rte_pci_device *pci_dev)
{
- return rte_eth_dev_pci_generic_remove(pci_dev, nfb_eth_dev_uninit);
+ return nfb_eth_common_remove(&pci_dev->device);
}
static struct rte_pci_driver nfb_eth_driver = {
--
2.52.0
^ permalink raw reply related [flat|nested] 131+ messages in thread
* [PATCH v6 3/8] net/nfb: add vdev as alternative device probe method
2026-02-02 15:34 ` [PATCH v6 " spinler
2026-02-02 15:34 ` [PATCH v6 1/8] net/nfb: prepare for indirect queue mapping scheme spinler
2026-02-02 15:34 ` [PATCH v6 2/8] net/nfb: create one ethdev per ethernet port spinler
@ 2026-02-02 15:34 ` spinler
2026-02-02 15:34 ` [PATCH v6 4/8] net/nfb: add device argument "port" to limit used ports spinler
` (4 subsequent siblings)
7 siblings, 0 replies; 131+ messages in thread
From: spinler @ 2026-02-02 15:34 UTC (permalink / raw)
To: dev; +Cc: Martin Spinler
From: Martin Spinler <spinler@cesnet.cz>
VDEV are useful for NFB devices not represented by PCI device,
e.g. virtual or simulated devices.
Signed-off-by: Martin Spinler <spinler@cesnet.cz>
---
drivers/net/nfb/meson.build | 1 +
drivers/net/nfb/nfb_vdev.c | 106 ++++++++++++++++++++++++++++++++++++
2 files changed, 107 insertions(+)
create mode 100644 drivers/net/nfb/nfb_vdev.c
diff --git a/drivers/net/nfb/meson.build b/drivers/net/nfb/meson.build
index d7a255c928..9e458dfb4a 100644
--- a/drivers/net/nfb/meson.build
+++ b/drivers/net/nfb/meson.build
@@ -16,6 +16,7 @@ ext_deps += dep
sources = files(
'nfb_ethdev.c',
+ 'nfb_vdev.c',
'nfb_rx.c',
'nfb_rxmode.c',
'nfb_stats.c',
diff --git a/drivers/net/nfb/nfb_vdev.c b/drivers/net/nfb/nfb_vdev.c
new file mode 100644
index 0000000000..b79f7ac416
--- /dev/null
+++ b/drivers/net/nfb/nfb_vdev.c
@@ -0,0 +1,106 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2026 Cesnet
+ */
+
+#include <rte_kvargs.h>
+#include <ethdev_vdev.h>
+
+#include "nfb.h"
+
+#define VDEV_NFB_DRIVER net_vdev_nfb
+#define VDEV_NFB_ARG_DEV "dev"
+
+static int
+vdev_nfb_vdev_probe(struct rte_vdev_device *dev)
+{
+ unsigned int count;
+ int ret = 0;
+ size_t len, pos;
+
+ struct nfb_probe_params params;
+
+ const char *vdev_name = rte_vdev_device_name(dev);
+ const char *vdev_args = rte_vdev_device_args(dev);
+ char *dev_params, *dev_params_mod;
+ struct rte_kvargs *kvargs;
+
+ kvargs = rte_kvargs_parse(vdev_args, NULL);
+ if (kvargs == NULL) {
+ NFB_LOG(ERR, "Failed to parse device arguments %s", vdev_args);
+ ret = -EINVAL;
+ goto err_parse_args;
+ }
+
+ dev_params = strdup(vdev_args);
+ if (dev_params == NULL) {
+ ret = -ENOMEM;
+ goto err_strdup_params;
+ }
+
+ params.device = &dev->device;
+ params.specific_init = NULL;
+ params.specific_device = NULL;
+ params.path = nfb_default_dev_path();
+ params.args = dev_params;
+ params.nfb_id = 0;
+ params.ep_index = -1;
+
+ len = strlen(dev_params) + 1;
+ pos = 0;
+ dev_params[pos] = '\0';
+
+ /* Parse parameters for virtual device */
+ for (count = 0; count != kvargs->count; ++count) {
+ const struct rte_kvargs_pair *pair = &kvargs->pairs[count];
+
+ if (!strcmp(pair->key, VDEV_NFB_ARG_DEV)) {
+ params.path = pair->value;
+ } else {
+ /* Clone non-vdev arguments, result is shorter or equal length */
+ dev_params_mod = dev_params + pos;
+ ret = snprintf(dev_params_mod, len, "%s%s=%s",
+ pos == 0 ? "" : ",", pair->key, pair->value);
+ if (ret < 0 || ret >= (signed int)len)
+ goto err_clone_args;
+ pos += ret;
+ len -= ret;
+ }
+ }
+
+ strlcpy(params.name, vdev_name, sizeof(params.name));
+
+ ret = nfb_eth_common_probe(¶ms);
+ if (ret)
+ goto err_nfb_common_probe;
+
+ free(dev_params);
+ rte_kvargs_free(kvargs);
+
+ return ret;
+
+err_nfb_common_probe:
+err_clone_args:
+ free(dev_params);
+err_strdup_params:
+ rte_kvargs_free(kvargs);
+err_parse_args:
+ return ret;
+}
+
+static int
+vdev_nfb_vdev_remove(struct rte_vdev_device *dev)
+{
+ return nfb_eth_common_remove(&dev->device);
+}
+
+/** Virtual device descriptor. */
+static struct rte_vdev_driver vdev_nfb_vdev = {
+ .probe = vdev_nfb_vdev_probe,
+ .remove = vdev_nfb_vdev_remove,
+};
+
+RTE_PMD_REGISTER_VDEV(VDEV_NFB_DRIVER, vdev_nfb_vdev);
+RTE_PMD_REGISTER_ALIAS(VDEV_NFB_DRIVER, eth_vdev_nfb);
+RTE_PMD_REGISTER_PARAM_STRING(net_vdev_nfb,
+ VDEV_NFB_ARG_DEV "=<string>"
+ );
--
2.52.0
^ permalink raw reply related [flat|nested] 131+ messages in thread
* [PATCH v6 4/8] net/nfb: add device argument "port" to limit used ports
2026-02-02 15:34 ` [PATCH v6 " spinler
` (2 preceding siblings ...)
2026-02-02 15:34 ` [PATCH v6 3/8] net/nfb: add vdev as alternative device probe method spinler
@ 2026-02-02 15:34 ` spinler
2026-02-02 15:34 ` [PATCH v6 5/8] net/nfb: init only MACs associated with device spinler
` (3 subsequent siblings)
7 siblings, 0 replies; 131+ messages in thread
From: spinler @ 2026-02-02 15:34 UTC (permalink / raw)
To: dev; +Cc: Martin Spinler
From: Martin Spinler <spinler@cesnet.cz>
NFB devices do not use separate PCI device for each port; one PCI device
contains handles for all ports instead. For some application this approach
can be limiting.
The "port" argument can be used to select only desired ports.
It can be used multiple times. When is not used at all, the driver
selects all ports.
Signed-off-by: Martin Spinler <spinler@cesnet.cz>
---
drivers/net/nfb/nfb.h | 7 +++++
drivers/net/nfb/nfb_ethdev.c | 55 ++++++++++++++++++++++++++++++++++--
drivers/net/nfb/nfb_vdev.c | 3 +-
3 files changed, 61 insertions(+), 4 deletions(-)
diff --git a/drivers/net/nfb/nfb.h b/drivers/net/nfb/nfb.h
index a1134d7786..10d74eb49c 100644
--- a/drivers/net/nfb/nfb.h
+++ b/drivers/net/nfb/nfb.h
@@ -42,6 +42,13 @@ extern int nfb_logtype;
#define RTE_NFB_DRIVER_NAME net_nfb
+/* Device arguments */
+#define NFB_ARG_PORT "port"
+
+#define NFB_COMMON_ARGS \
+ NFB_ARG_PORT "=<number>" \
+ ""
+
/*
* Handles obtained from the libnfb: each process must use own instance.
* Stored inside dev->process_private.
diff --git a/drivers/net/nfb/nfb_ethdev.c b/drivers/net/nfb/nfb_ethdev.c
index 6eb0cd08fd..b3863af2c3 100644
--- a/drivers/net/nfb/nfb_ethdev.c
+++ b/drivers/net/nfb/nfb_ethdev.c
@@ -4,6 +4,7 @@
* All rights reserved.
*/
+#include <ctype.h>
#include <sys/queue.h>
#include <eal_export.h>
#include <rte_tailq.h>
@@ -22,6 +23,11 @@
#include "nfb_rxmode.h"
#include "nfb.h"
+static const char * const VALID_KEYS[] = {
+ NFB_ARG_PORT,
+ NULL
+};
+
struct nfb_ifc_create_params {
struct nfb_probe_params *probe_params;
struct nc_ifc_map_info map_info;
@@ -713,6 +719,24 @@ nfb_eth_dev_create_for_ifc(struct nfb_ifc_create_params *cp)
return ret;
}
+static int nfb_eth_dev_create_for_ifc_by_port(const char *key __rte_unused,
+ const char *value, void *opaque)
+{
+ char *end;
+ unsigned long port;
+ struct nfb_ifc_create_params *ifc_params = opaque;
+
+ if (value == NULL || strlen(value) == 0 || !isdigit(*value))
+ return -EINVAL;
+
+ port = strtoul(value, &end, 0);
+ if (*end != '\0' || port >= (unsigned long)ifc_params->map_info.ifc_cnt)
+ return -EINVAL;
+
+ ifc_params->ifc_info = &ifc_params->map_info.ifc[port];
+ return nfb_eth_dev_create_for_ifc(ifc_params);
+}
+
RTE_EXPORT_INTERNAL_SYMBOL(nfb_eth_common_probe)
int
nfb_eth_common_probe(struct nfb_probe_params *params)
@@ -722,6 +746,7 @@ nfb_eth_common_probe(struct nfb_probe_params *params)
struct nfb_device *nfb_dev;
struct nfb_ifc_create_params ifc_params;
+ struct rte_kvargs *kvlist = NULL;
nfb_dev = nfb_open(params->path);
if (nfb_dev == NULL) {
@@ -733,16 +758,35 @@ nfb_eth_common_probe(struct nfb_probe_params *params)
if (ret)
goto err_map_info_create;
+ if (params->args != NULL && strlen(params->args) > 0) {
+ kvlist = rte_kvargs_parse(params->args, VALID_KEYS);
+ if (kvlist == NULL) {
+ NFB_LOG(ERR, "Failed to parse device arguments %s", params->args);
+ ret = -EINVAL;
+ goto err_parse_args;
+ }
+ }
+
ifc_params.probe_params = params;
ifc_params.basename_len = strlen(params->name);
- for (i = 0; i < ifc_params.map_info.ifc_cnt; i++) {
- ifc_params.ifc_info = &ifc_params.map_info.ifc[i];
- ret = nfb_eth_dev_create_for_ifc(&ifc_params);
+ /* When at least one port argument is specified, create only selected ports */
+ if (kvlist && rte_kvargs_count(kvlist, NFB_ARG_PORT)) {
+ ret = rte_kvargs_process(kvlist, NFB_ARG_PORT,
+ nfb_eth_dev_create_for_ifc_by_port, (void *)&ifc_params);
if (ret)
goto err_dev_create;
+ } else {
+ for (i = 0; i < ifc_params.map_info.ifc_cnt; i++) {
+ ifc_params.ifc_info = &ifc_params.map_info.ifc[i];
+ ret = nfb_eth_dev_create_for_ifc(&ifc_params);
+ if (ret)
+ goto err_dev_create;
+ }
}
+ rte_kvargs_free(kvlist);
+
nc_map_info_destroy(&ifc_params.map_info);
nfb_close(nfb_dev);
@@ -750,6 +794,8 @@ nfb_eth_common_probe(struct nfb_probe_params *params)
err_dev_create:
nfb_eth_common_remove(params->device);
+ rte_kvargs_free(kvlist);
+err_parse_args:
nc_map_info_destroy(&ifc_params.map_info);
err_map_info_create:
nfb_close(nfb_dev);
@@ -858,3 +904,6 @@ RTE_PMD_REGISTER_PCI(RTE_NFB_DRIVER_NAME, nfb_eth_driver);
RTE_PMD_REGISTER_PCI_TABLE(RTE_NFB_DRIVER_NAME, nfb_pci_id_table);
RTE_PMD_REGISTER_KMOD_DEP(RTE_NFB_DRIVER_NAME, "* nfb");
RTE_LOG_REGISTER_DEFAULT(nfb_logtype, NOTICE);
+RTE_PMD_REGISTER_PARAM_STRING(RTE_NFB_DRIVER_NAME,
+ NFB_COMMON_ARGS
+ );
diff --git a/drivers/net/nfb/nfb_vdev.c b/drivers/net/nfb/nfb_vdev.c
index b79f7ac416..57f10d2068 100644
--- a/drivers/net/nfb/nfb_vdev.c
+++ b/drivers/net/nfb/nfb_vdev.c
@@ -102,5 +102,6 @@ static struct rte_vdev_driver vdev_nfb_vdev = {
RTE_PMD_REGISTER_VDEV(VDEV_NFB_DRIVER, vdev_nfb_vdev);
RTE_PMD_REGISTER_ALIAS(VDEV_NFB_DRIVER, eth_vdev_nfb);
RTE_PMD_REGISTER_PARAM_STRING(net_vdev_nfb,
- VDEV_NFB_ARG_DEV "=<string>"
+ VDEV_NFB_ARG_DEV "=<string> "
+ NFB_COMMON_ARGS
);
--
2.52.0
^ permalink raw reply related [flat|nested] 131+ messages in thread
* [PATCH v6 5/8] net/nfb: init only MACs associated with device
2026-02-02 15:34 ` [PATCH v6 " spinler
` (3 preceding siblings ...)
2026-02-02 15:34 ` [PATCH v6 4/8] net/nfb: add device argument "port" to limit used ports spinler
@ 2026-02-02 15:34 ` spinler
2026-02-02 15:34 ` [PATCH v6 6/8] net/nfb: add compatible cards to driver PCI ID table spinler
` (2 subsequent siblings)
7 siblings, 0 replies; 131+ messages in thread
From: spinler @ 2026-02-02 15:34 UTC (permalink / raw)
To: dev; +Cc: Martin Spinler
From: Martin Spinler <spinler@cesnet.cz>
Use information obtained from the new manner of creating NFB ethdevs
and initialize only RxMACs / TxMACs associated with actual ethdev.
This enables separated management and configuration of interface ports.
Signed-off-by: Martin Spinler <spinler@cesnet.cz>
---
drivers/net/nfb/nfb.h | 12 +--
drivers/net/nfb/nfb_ethdev.c | 137 ++++++++++++++++-------------------
2 files changed, 67 insertions(+), 82 deletions(-)
diff --git a/drivers/net/nfb/nfb.h b/drivers/net/nfb/nfb.h
index 10d74eb49c..01241860b1 100644
--- a/drivers/net/nfb/nfb.h
+++ b/drivers/net/nfb/nfb.h
@@ -36,10 +36,6 @@ extern int nfb_logtype;
#define RTE_ETH_NDP_MAX_RX_QUEUES 32
#define RTE_ETH_NDP_MAX_TX_QUEUES 32
-/* Max index of rx/tx dmas */
-#define RTE_MAX_NC_RXMAC 256
-#define RTE_MAX_NC_TXMAC 256
-
#define RTE_NFB_DRIVER_NAME net_nfb
/* Device arguments */
@@ -54,10 +50,10 @@ extern int nfb_logtype;
* Stored inside dev->process_private.
*/
struct pmd_internals {
- uint16_t max_rxmac;
- uint16_t max_txmac;
- struct nc_rxmac *rxmac[RTE_MAX_NC_RXMAC];
- struct nc_txmac *txmac[RTE_MAX_NC_TXMAC];
+ uint16_t max_rxmac; /**< Count of valid rxmac items */
+ uint16_t max_txmac; /**< Count of valid txmac items */
+ struct nc_rxmac **rxmac; /**< Array of Rx MAC handles */
+ struct nc_txmac **txmac; /**< Array of Tx MAC handles */
struct nfb_device *nfb;
TAILQ_ENTRY(pmd_internals) eth_dev_list; /**< Item in list of all devices */
diff --git a/drivers/net/nfb/nfb_ethdev.c b/drivers/net/nfb/nfb_ethdev.c
index b3863af2c3..dfacfbf5b8 100644
--- a/drivers/net/nfb/nfb_ethdev.c
+++ b/drivers/net/nfb/nfb_ethdev.c
@@ -53,81 +53,75 @@ static const struct rte_ether_addr eth_addr = {
};
/**
- * Open all RX DMA queues
+ * Open Rx and Tx MAC components associated with current ifc
*
- * @param dev
- * Pointer to nfb device.
- * @param[out] rxmac
- * Pointer to output array of nc_rxmac
- * @param[out] max_rxmac
- * Pointer to output max index of rxmac
+ * @param intl
+ * Pointer to driver internal structure
+ * @param params
+ * Pointer to init parameters structure
*/
-static void
-nfb_nc_rxmac_init(struct nfb_device *nfb,
- struct nc_rxmac *rxmac[RTE_MAX_NC_RXMAC],
- uint16_t *max_rxmac)
+static int
+nfb_nc_eth_init(struct pmd_internals *intl, struct nfb_ifc_create_params *params)
{
- *max_rxmac = 0;
- while ((rxmac[*max_rxmac] = nc_rxmac_open_index(nfb, *max_rxmac)))
- ++(*max_rxmac);
-}
+ int ret;
+ int i, rxm, txm;
+ struct nc_ifc_info *ifc = params->ifc_info;
+ struct nc_ifc_map_info *mi = ¶ms->map_info;
-/**
- * Open all TX DMA queues
- *
- * @param dev
- * Pointer to nfb device.
- * @param[out] txmac
- * Pointer to output array of nc_txmac
- * @param[out] max_rxmac
- * Pointer to output max index of txmac
- */
-static void
-nfb_nc_txmac_init(struct nfb_device *nfb,
- struct nc_txmac *txmac[RTE_MAX_NC_TXMAC],
- uint16_t *max_txmac)
-{
- *max_txmac = 0;
- while ((txmac[*max_txmac] = nc_txmac_open_index(nfb, *max_txmac)))
- ++(*max_txmac);
-}
+ ret = -ENOMEM;
+ if (ifc->eth_cnt == 0)
+ return 0;
-/**
- * Close all RX DMA queues
- *
- * @param rxmac
- * Pointer to array of nc_rxmac
- * @param max_rxmac
- * Maximum index of rxmac
- */
-static void
-nfb_nc_rxmac_deinit(struct nc_rxmac *rxmac[RTE_MAX_NC_RXMAC],
- uint16_t max_rxmac)
-{
- uint16_t i;
- for (i = 0; i < max_rxmac; i++) {
- nc_rxmac_close(rxmac[i]);
- rxmac[i] = NULL;
+ intl->rxmac = calloc(ifc->eth_cnt, sizeof(*intl->rxmac));
+ if (intl->rxmac == NULL)
+ goto err_alloc_rxmac;
+
+ intl->txmac = calloc(ifc->eth_cnt, sizeof(*intl->txmac));
+ if (intl->txmac == NULL)
+ goto err_alloc_txmac;
+
+ /* Some eths may not have assigned MAC nodes, hence use separate var for indexing */
+ rxm = 0;
+ txm = 0;
+ for (i = 0; i < mi->eth_cnt; i++) {
+ if (mi->eth[i].ifc != ifc->id)
+ continue;
+
+ intl->rxmac[rxm] = nc_rxmac_open(intl->nfb, mi->eth[i].node_rxmac);
+ if (intl->rxmac[rxm])
+ rxm++;
+
+ intl->txmac[txm] = nc_txmac_open(intl->nfb, mi->eth[i].node_txmac);
+ if (intl->txmac[txm])
+ txm++;
}
+
+ intl->max_rxmac = rxm;
+ intl->max_txmac = txm;
+ return 0;
+
+err_alloc_txmac:
+ free(intl->rxmac);
+err_alloc_rxmac:
+ return ret;
}
/**
- * Close all TX DMA queues
- *
- * @param txmac
- * Pointer to array of nc_txmac
- * @param max_txmac
- * Maximum index of txmac
+ * Close all Rx+Tx MAC components
+ * @param intl
+ * Pointer to internal driver structure
*/
static void
-nfb_nc_txmac_deinit(struct nc_txmac *txmac[RTE_MAX_NC_TXMAC],
- uint16_t max_txmac)
+nfb_nc_eth_deinit(struct pmd_internals *intl)
{
uint16_t i;
- for (i = 0; i < max_txmac; i++) {
- nc_txmac_close(txmac[i]);
- txmac[i] = NULL;
- }
+ for (i = 0; i < intl->max_txmac; i++)
+ nc_txmac_close(intl->txmac[i]);
+ for (i = 0; i < intl->max_rxmac; i++)
+ nc_rxmac_close(intl->rxmac[i]);
+
+ free(intl->txmac);
+ free(intl->rxmac);
}
/**
@@ -344,7 +338,7 @@ nfb_eth_link_update(struct rte_eth_dev *dev,
link.link_duplex = RTE_ETH_LINK_FULL_DUPLEX;
link.link_autoneg = RTE_ETH_LINK_SPEED_FIXED;
- if (internals->rxmac[0] != NULL) {
+ if (internals->max_rxmac) {
nc_rxmac_read_status(internals->rxmac[0], &status);
switch (status.speed) {
@@ -569,12 +563,9 @@ nfb_eth_dev_init(struct rte_eth_dev *dev, void *init_data)
max_rx_queues = ifc->rxq_cnt;
max_tx_queues = ifc->txq_cnt;
- nfb_nc_rxmac_init(internals->nfb,
- internals->rxmac,
- &internals->max_rxmac);
- nfb_nc_txmac_init(internals->nfb,
- internals->txmac,
- &internals->max_txmac);
+ ret = nfb_nc_eth_init(internals, params);
+ if (ret)
+ goto err_nc_eth_init;
/* Set rx, tx burst functions */
dev->rx_pkt_burst = nfb_eth_ndp_rx;
@@ -640,10 +631,9 @@ nfb_eth_dev_init(struct rte_eth_dev *dev, void *init_data)
err_malloc_mac_addrs:
rte_free(priv->queue_map_rx);
err_alloc_queue_map:
- nfb_nc_rxmac_deinit(internals->rxmac, internals->max_rxmac);
- nfb_nc_txmac_deinit(internals->txmac, internals->max_txmac);
+ nfb_nc_eth_deinit(internals);
+err_nc_eth_init:
nfb_close(internals->nfb);
-
err_nfb_open:
rte_free(internals);
err_alloc_internals:
@@ -670,8 +660,7 @@ nfb_eth_dev_uninit(struct rte_eth_dev *dev)
TAILQ_REMOVE(&nfb_eth_dev_list, internals, eth_dev_list);
- nfb_nc_rxmac_deinit(internals->rxmac, internals->max_rxmac);
- nfb_nc_txmac_deinit(internals->txmac, internals->max_txmac);
+ nfb_nc_eth_deinit(internals);
nfb_close(internals->nfb);
rte_free(internals);
--
2.52.0
^ permalink raw reply related [flat|nested] 131+ messages in thread
* [PATCH v6 6/8] net/nfb: add compatible cards to driver PCI ID table
2026-02-02 15:34 ` [PATCH v6 " spinler
` (4 preceding siblings ...)
2026-02-02 15:34 ` [PATCH v6 5/8] net/nfb: init only MACs associated with device spinler
@ 2026-02-02 15:34 ` spinler
2026-02-02 15:34 ` [PATCH v6 7/8] net/nfb: report firmware version spinler
2026-02-02 15:34 ` [PATCH v6 8/8] doc/nfb: cleanup and update guide spinler
7 siblings, 0 replies; 131+ messages in thread
From: spinler @ 2026-02-02 15:34 UTC (permalink / raw)
To: dev; +Cc: Martin Spinler
From: Martin Spinler <spinler@cesnet.cz>
The nfb driver is compatible with:
- NFB-200G2QL card secondary PCI device
- FB2CGHH card
- COMBO-400G1 card
- Common CESNET-NDK cards
Signed-off-by: Martin Spinler <spinler@cesnet.cz>
---
drivers/net/nfb/nfb.h | 5 +++++
drivers/net/nfb/nfb_ethdev.c | 4 ++++
2 files changed, 9 insertions(+)
diff --git a/drivers/net/nfb/nfb.h b/drivers/net/nfb/nfb.h
index 01241860b1..2bce71ac89 100644
--- a/drivers/net/nfb/nfb.h
+++ b/drivers/net/nfb/nfb.h
@@ -24,13 +24,18 @@ extern int nfb_logtype;
/* PCI Vendor ID */
#define PCI_VENDOR_ID_NETCOPE 0x1b26
#define PCI_VENDOR_ID_SILICOM 0x1c2c
+#define PCI_VENDOR_ID_CESNET 0x18ec
/* PCI Device IDs */
#define PCI_DEVICE_ID_NFB_40G2 0xcb80
#define PCI_DEVICE_ID_NFB_100G2 0xc2c1
#define PCI_DEVICE_ID_NFB_200G2QL 0xc250
+#define PCI_DEVICE_ID_NFB_200G2QL_E1 0xc251
#define PCI_DEVICE_ID_FB2CGG3 0x00d0
+#define PCI_DEVICE_ID_FB2CGHH 0x00d2
#define PCI_DEVICE_ID_FB2CGG3D 0xc240
+#define PCI_DEVICE_ID_COMBO400G1 0xc400
+#define PCI_DEVICE_ID_CESNET_NDK_COMMON 0xc000
/* Max index of ndp rx/tx queues */
#define RTE_ETH_NDP_MAX_RX_QUEUES 32
diff --git a/drivers/net/nfb/nfb_ethdev.c b/drivers/net/nfb/nfb_ethdev.c
index dfacfbf5b8..6ff70671a2 100644
--- a/drivers/net/nfb/nfb_ethdev.c
+++ b/drivers/net/nfb/nfb_ethdev.c
@@ -795,8 +795,12 @@ static const struct rte_pci_id nfb_pci_id_table[] = {
{ RTE_PCI_DEVICE(PCI_VENDOR_ID_NETCOPE, PCI_DEVICE_ID_NFB_40G2) },
{ RTE_PCI_DEVICE(PCI_VENDOR_ID_NETCOPE, PCI_DEVICE_ID_NFB_100G2) },
{ RTE_PCI_DEVICE(PCI_VENDOR_ID_NETCOPE, PCI_DEVICE_ID_NFB_200G2QL) },
+ { RTE_PCI_DEVICE(PCI_VENDOR_ID_NETCOPE, PCI_DEVICE_ID_NFB_200G2QL_E1) },
{ RTE_PCI_DEVICE(PCI_VENDOR_ID_SILICOM, PCI_DEVICE_ID_FB2CGG3) },
+ { RTE_PCI_DEVICE(PCI_VENDOR_ID_SILICOM, PCI_DEVICE_ID_FB2CGHH) },
{ RTE_PCI_DEVICE(PCI_VENDOR_ID_SILICOM, PCI_DEVICE_ID_FB2CGG3D) },
+ { RTE_PCI_DEVICE(PCI_VENDOR_ID_CESNET, PCI_DEVICE_ID_COMBO400G1) },
+ { RTE_PCI_DEVICE(PCI_VENDOR_ID_CESNET, PCI_DEVICE_ID_CESNET_NDK_COMMON) },
{ .vendor_id = 0, }
};
--
2.52.0
^ permalink raw reply related [flat|nested] 131+ messages in thread
* [PATCH v6 7/8] net/nfb: report firmware version
2026-02-02 15:34 ` [PATCH v6 " spinler
` (5 preceding siblings ...)
2026-02-02 15:34 ` [PATCH v6 6/8] net/nfb: add compatible cards to driver PCI ID table spinler
@ 2026-02-02 15:34 ` spinler
2026-02-02 15:34 ` [PATCH v6 8/8] doc/nfb: cleanup and update guide spinler
7 siblings, 0 replies; 131+ messages in thread
From: spinler @ 2026-02-02 15:34 UTC (permalink / raw)
To: dev; +Cc: Martin Spinler
From: Martin Spinler <spinler@cesnet.cz>
Report the firmware version, which is described
by project name and project version.
Signed-off-by: Martin Spinler <spinler@cesnet.cz>
---
drivers/net/nfb/nfb_ethdev.c | 27 +++++++++++++++++++++++++++
1 file changed, 27 insertions(+)
diff --git a/drivers/net/nfb/nfb_ethdev.c b/drivers/net/nfb/nfb_ethdev.c
index 6ff70671a2..6dff55cf8b 100644
--- a/drivers/net/nfb/nfb_ethdev.c
+++ b/drivers/net/nfb/nfb_ethdev.c
@@ -488,6 +488,32 @@ nfb_eth_mac_addr_remove(struct rte_eth_dev *dev, uint32_t index)
nc_rxmac_set_mac(internals->rxmac[i], index, 0, 0);
}
+static int
+nfb_eth_fw_version_get(struct rte_eth_dev *dev, char *fw_version,
+ size_t fw_size)
+{
+ int ret;
+ const char *proj_name, *proj_vers;
+ struct pmd_internals *intl = dev->process_private;
+
+ proj_name = nc_info_get_fw_project_name(intl->nfb, NULL);
+ proj_vers = nc_info_get_fw_project_version(intl->nfb, NULL);
+
+ if (proj_name == NULL)
+ proj_name = "";
+ if (proj_vers == NULL)
+ proj_vers = "";
+
+ ret = snprintf(fw_version, fw_size, "%s;%s", proj_name, proj_vers);
+ if (ret < 0)
+ return ret;
+
+ if (ret >= (signed int)fw_size)
+ return strlen(proj_name) + 1 + strlen(proj_vers) + 1;
+
+ return 0;
+}
+
static const struct eth_dev_ops ops = {
.dev_start = nfb_eth_dev_start,
.dev_stop = nfb_eth_dev_stop,
@@ -514,6 +540,7 @@ static const struct eth_dev_ops ops = {
.mac_addr_set = nfb_eth_mac_addr_set,
.mac_addr_add = nfb_eth_mac_addr_add,
.mac_addr_remove = nfb_eth_mac_addr_remove,
+ .fw_version_get = nfb_eth_fw_version_get,
};
/**
--
2.52.0
^ permalink raw reply related [flat|nested] 131+ messages in thread
* [PATCH v6 8/8] doc/nfb: cleanup and update guide
2026-02-02 15:34 ` [PATCH v6 " spinler
` (6 preceding siblings ...)
2026-02-02 15:34 ` [PATCH v6 7/8] net/nfb: report firmware version spinler
@ 2026-02-02 15:34 ` spinler
2026-02-02 17:42 ` [REVIEW] " Stephen Hemminger
7 siblings, 1 reply; 131+ messages in thread
From: spinler @ 2026-02-02 15:34 UTC (permalink / raw)
To: dev; +Cc: Martin Spinler
From: Martin Spinler <spinler@cesnet.cz>
Clean up documentation and update the guide with:
- Current card list with firmware download links
- Updated port argument description
Signed-off-by: Martin Spinler <spinler@cesnet.cz>
---
doc/guides/nics/features/nfb.ini | 4 +
doc/guides/nics/nfb.rst | 187 +++++++++++--------------
doc/guides/rel_notes/release_26_03.rst | 6 +
3 files changed, 95 insertions(+), 102 deletions(-)
diff --git a/doc/guides/nics/features/nfb.ini b/doc/guides/nics/features/nfb.ini
index c77b592789..7ac89fc15f 100644
--- a/doc/guides/nics/features/nfb.ini
+++ b/doc/guides/nics/features/nfb.ini
@@ -9,9 +9,13 @@ Link status = Y
Queue start/stop = Y
Promiscuous mode = Y
Allmulticast mode = Y
+Unicast MAC filter = Y
+Timestamp offload = P
Basic stats = Y
Extended stats = Y
Stats per queue = Y
+FW version = Y
+Multiprocess aware = Y
Linux = Y
x86-64 = Y
Usage doc = Y
diff --git a/doc/guides/nics/nfb.rst b/doc/guides/nics/nfb.rst
index a9b4049654..55a635d8d4 100644
--- a/doc/guides/nics/nfb.rst
+++ b/doc/guides/nics/nfb.rst
@@ -2,64 +2,101 @@
Copyright 2019 Cesnet
Copyright 2019 Netcope Technologies
-NFB poll mode driver library
-=================================
+NFB Poll Mode Driver
+====================
+
+The NFB PMD implements support for the FPGA-based
+programmable NICs running `CESNET-NDK <https://www.liberouter.org/ndk/>`_
+based firmware (formerly known as the NetCOPE platform). The CESNET Network Development Kit offers
+wide spectrum of supported cards, for example:
+N6010, FB2CGG3 (Silicom Denmark),
+IA-420F, IA-440i (BittWare),
+AGI-FH400G (ReflexCES),
+and `many more <https://github.com/CESNET/ndk-fpga/tree/devel/cards>`_.
+
+The CESNET-NDK framework is open source and
+can be found on `CESNET-NDK <https://github.com/CESNET/ndk-fpga>`_.
+Ready-to-use demo firmwares can be found on the `DYNANIC page <https://dyna-nic.com/try-now/>`_.
+
+Software compatibility and firmware for
+`historical cards <https://www.liberouter.org/technologies/cards/>`_
+are left unmaintained.
+
+Software prerequisites
+----------------------
-The NFB poll mode driver library implements support for the Netcope
-FPGA Boards (**NFB-40G2, NFB-100G2, NFB-200G2QL**) and Silicom **FB2CGG3** card,
-FPGA-based programmable NICs. The NFB PMD uses interface provided by the libnfb
-library to communicate with these cards over the nfb layer.
+This PMD requires a Linux kernel module, which is responsible for initialization and
+allocation of resources needed for the nfb layer function.
+Communication between PMD and kernel modules is mediated by the libnfb library.
+The kernel module and library are not part of DPDK and must be installed
+separately. Dependencies can be found on GitHub:
+`nfb-framework <https://github.com/CESNET/ndk-sw>`_ as source code,
+or for RPM-based distributions, the prebuilt `nfb-framework` package on
+`Fedora Copr <https://copr.fedorainfracloud.org/coprs/g/CESNET/nfb-framework/>`_.
-More information about the
-`NFB cards <https://www.liberouter.org/technologies/cards/>`_
-and used technology
-(`Network Development Kit <https://www.liberouter.org/ndk/>`_)
-can be found on the `Liberouter website <http://www.liberouter.org/>`_.
+Before starting the DPDK, make sure that the kernel module is loaded (`sudo modprobe nfb`)
+and the card is running the CESNET-NDK based firmware (`nfb-info -l`).
.. note::
- Currently the driver is supported only on x86_64 architectures.
- Only x86_64 versions of the external libraries are provided.
+ Currently, the driver is supported only on x86_64 architectures.
+
+NFB card architecture
+---------------------
+
+Ethernet Ports
+~~~~~~~~~~~~~~
+
+The NFB cards are multi-port multi-queue cards, where (generally) data from any
+Ethernet port may be sent by the firmware to any queue.
-Prerequisites
--------------
+The cards were historically represented in DPDK as a single port.
+Currently each Ethernet channel is represented as one DPDK port.
-This PMD requires kernel modules which are responsible for initialization and
-allocation of resources needed for nfb layer function.
-Communication between PMD and kernel modules is mediated by libnfb library.
-These kernel modules and library are not part of DPDK and must be installed
-separately:
+.. note::
-* **libnfb library**
+ Normally, one port corresponds to one channel, but ports can often be configured in a separate
+ manner. For example one 100G port can be used as 4x25G or 4x10G independent Ethernet channels.
- The library provides API for initialization of nfb transfers, receiving and
- transmitting data segments.
+By default, all ports are initialized and used for the allowed PCI device.
+When this behaviour is limiting (e.g., for multiple instances of DPDK app on different ports
+of the same PCI device), ports can be specified by the `port` item in the `allow` argument:
-* **Kernel modules**
+.. code-block:: console
- * nfb
+ -a 0000:01:00.0,port=0,port=3
- Kernel modules manage initialization of hardware, allocation and
- sharing of resources for user space applications.
+PCIe slots
+~~~~~~~~~~
-Dependencies can be found here:
-`Netcope common <https://github.com/CESNET/ndk-sw>`_.
+Some cards employ more than one PCIe device for better data throughput. This can be achieved by
+slot bifurcation (only a minor improvement) or by an add-on cable connected to another PCIe slot.
+Both improvements can work together, as is, for example, in the case of the AGI-FH400G card.
-Versions of the packages
-~~~~~~~~~~~~~~~~~~~~~~~~
+Because primary and secondary slot(s) can be attached to different NUMA nodes
+(also applies for bifurcation on some HW), the data structures need to be correctly allocated.
+(Device-aware allocation matters also on IOMMU-enabled systems.)
+The firmware already provides DMA queue to PCI device mapping. The DPDK application just needs to
+use all PCI devices, otherwise some queues will not be available; provide all PCI endpoints
+listed in the `nfb-info -v` in the `allow` argument.
-The minimum version of the provided packages:
+.. note::
-* for DPDK from 19.05
+ For cards where the number of Ethernet ports is less than the number of PCI devices
+ (e.g., AGI-FH400G: 1 port, up to 4 PCI devices), the virtual DPDK ports are
+ created to achieve the best NUMA-aware throughput
+ (virtual ports lack a lot of configuration features).
-Configuration
--------------
+Features
+--------
Timestamps
+~~~~~~~~~~
-The PMD supports hardware timestamps of frame receipt on physical network interface. In order to use
-the timestamps, the hardware timestamping unit must be enabled (follow the documentation of the NFB
-products). The standard `RTE_ETH_RX_OFFLOAD_TIMESTAMP` flag can be used for this feature.
+The PMD supports hardware timestamps of frame receipt on physical network interface.
+In order to use the timestamps, the hardware timestamping unit must be enabled
+(follow the documentation of the NFB products).
+The standard `RTE_ETH_RX_OFFLOAD_TIMESTAMP` flag can be used for this feature.
When the timestamps are enabled, a timestamp validity flag is set in the MBUFs
containing received frames and timestamp is inserted into the `rte_mbuf` struct.
@@ -67,72 +104,18 @@ containing received frames and timestamp is inserted into the `rte_mbuf` struct.
The timestamp is an `uint64_t` field and holds the number of nanoseconds
elapsed since 1.1.1970 00:00:00 UTC.
+Simulation
+~~~~~~~~~~
-Using the NFB PMD
-----------------------
-
-Kernel modules have to be loaded before running the DPDK application.
-
-NFB card architecture
----------------------
-
-The NFB cards are multi-port multi-queue cards, where (generally) data from any
-Ethernet port may be sent to any queue.
-They are represented in DPDK as a single port.
-
-NFB-200G2QL card employs an add-on cable which allows to connect it to two
-physical PCI-E slots at the same time (see the diagram below).
-This is done to allow 200 Gbps of traffic to be transferred through the PCI-E
-bus (note that a single PCI-E 3.0 x16 slot provides only 125 Gbps theoretical
-throughput).
-
-Although each slot may be connected to a different CPU and therefore to a different
-NUMA node, the card is represented as a single port in DPDK. To work with data
-from the individual queues on the right NUMA node, connection of NUMA nodes on
-first and last queue (each NUMA node has half of the queues) need to be checked.
-
-Limitations
------------
-
-Driver is usable only on Linux architecture, namely on CentOS.
-
-Since a card is always represented as a single port, but can be connected to two
-NUMA nodes, there is need for manual check where master/slave is connected.
-
-Example of usage
-----------------
-
-Read packets from 0. and 1. receive queue and write them to 0. and 1.
-transmit queue:
+The CESNET-NDK framework offers the possibility of simulating the firmware together with DPDK.
+This allows for easy debugging of a packet flow behaviour with a specific firmware configuration.
+The DPDK NFB driver can be connected to the simulator (Questa/ModelSim/nvc) via a virtual device:
.. code-block:: console
- ./<build_dir>/app/dpdk-testpmd -l 0-3 -n 2 \
- -- --port-topology=chained --rxq=2 --txq=2 --nb-cores=2 -i -a
-
-Example output:
-
-.. code-block:: console
+ dpdk-testpmd
+ --vdev=eth_vdev_nfb,dev=libnfb-ext-grpc.so:grpc+dma_vas:localhost:50051,queue_driver=native
+ --iova-mode=va -- -i
- [...]
- EAL: PCI device 0000:06:00.0 on NUMA socket -1
- EAL: probe driver: 1b26:c1c1 net_nfb
- PMD: Initializing NFB device (0000:06:00.0)
- PMD: Available DMA queues RX: 8 TX: 8
- PMD: NFB device (0000:06:00.0) successfully initialized
- Interactive-mode selected
- Auto-start selected
- Configuring Port 0 (socket 0)
- Port 0: 00:11:17:00:00:00
- Checking link statuses...
- Port 0 Link Up - speed 10000 Mbps - full-duplex
- Done
- Start automatic packet forwarding
- io packet forwarding - CRC stripping disabled - packets/burst=32
- nb forwarding cores=2 - nb forwarding ports=1
- RX queues=2 - RX desc=128 - RX free threshold=0
- RX threshold registers: pthresh=0 hthresh=0 wthresh=0
- TX queues=2 - TX desc=512 - TX free threshold=0
- TX threshold registers: pthresh=0 hthresh=0 wthresh=0
- TX RS bit threshold=0 - TXQ flags=0x0
- testpmd>
+More info about the simulation can be found int the CESNET-NDK `documentation
+<https://cesnet.github.io/ndk-fpga/devel/ndk_apps/minimal/tests/cocotb/readme.html>`_.
diff --git a/doc/guides/rel_notes/release_26_03.rst b/doc/guides/rel_notes/release_26_03.rst
index 29d8c6bb37..d484ca98d2 100644
--- a/doc/guides/rel_notes/release_26_03.rst
+++ b/doc/guides/rel_notes/release_26_03.rst
@@ -58,6 +58,12 @@ New Features
* **Updated CESNET NFB ethernet driver.**
* The timestamp value has been updated to make it usable.
+ * The DPDK port now represents just one Ethernet port instead of all Ethernet ports on the NIC.
+ * All ports are used by default, but a subset can be selected using the ``port`` argument.
+ * Report firmware version.
+ * Common CESNET-NDK-based adapters have been added,
+ including the FB2CGHH (Silicom Denmark) and XpressSX AGI-FH400G (Reflex CES).
+
Removed Items
-------------
--
2.52.0
^ permalink raw reply related [flat|nested] 131+ messages in thread
* [REVIEW] doc/nfb: cleanup and update guide
2026-02-02 15:34 ` [PATCH v6 8/8] doc/nfb: cleanup and update guide spinler
@ 2026-02-02 17:42 ` Stephen Hemminger
2026-02-02 17:51 ` Stephen Hemminger
` (3 more replies)
0 siblings, 4 replies; 131+ messages in thread
From: Stephen Hemminger @ 2026-02-02 17:42 UTC (permalink / raw)
To: dev; +Cc: Stephen Hemminger
AI-generated review of bundle-1720-nfb-multiport.mbox
Reviewed using Claude (claude-opus-4-5-20251101) on 2026-02-02
This is an automated review. Please verify all suggestions.
---
# DPDK Patch Review: net/nfb Multi-port Series (v6)
## Overview
This is an 8-patch series that enhances the NFB driver to create one ethdev per physical Ethernet port instead of one ethdev per PCI device.
---
## Patch 1/8: net/nfb: prepare for indirect queue mapping scheme
### Errors
None.
### Warnings
1. **Line length exceeds 100 characters** (nfb_ethdev.c)
- Line 580: `priv->queue_map_rx = rte_calloc("NFB queue map", (max_rx_queues + max_tx_queues),`
- Split across multiple lines to stay within 100 characters.
### Info
- Clean preparation patch that adds queue mapping infrastructure for the multi-port implementation.
---
## Patch 2/8: net/nfb: create one ethdev per ethernet port
### Errors
1. **`__rte_internal` functions in header file without the tag appearing alone on its own line** (nfb.h:89-92)
```c
__rte_internal
int nfb_eth_common_probe(struct nfb_probe_params *params);
__rte_internal
int nfb_eth_common_remove(struct rte_device *dev);
```
The `__rte_internal` tag should be alone on its line, but the function signature should have the return type on a separate line per DPDK style:
```c
__rte_internal
int
nfb_eth_common_probe(struct nfb_probe_params *params);
```
### Warnings
1. **Extra blank line** (nfb.h:93)
- There are two blank lines before the `__rte_internal` declaration; one is sufficient.
2. **Line exceeds 100 characters** (nfb_ethdev.c:704)
```c
ret = snprintf(pp->name + cp->basename_len, sizeof(pp->name) - cp->basename_len,
```
This line is approximately 101 characters.
### Info
- Good refactoring to support multi-port architecture.
---
## Patch 3/8: net/nfb: add vdev as alternative device probe method
### Errors
None.
### Warnings
1. **Unnecessary cast of void pointer** (nfb_vdev.c:62)
```c
ret = rte_kvargs_process(kvlist, NFB_ARG_PORT,
nfb_eth_dev_create_for_ifc_by_port, (void *)&ifc_params);
```
The cast `(void *)` is unnecessary in C.
2. **Missing newline at end of file** (nfb_vdev.c)
- The file appears to end without a trailing newline after line 108.
### Info
- Useful addition for virtual/simulated NFB devices.
---
## Patch 4/8: net/nfb: add device argument "port" to limit used ports
### Errors
None.
### Warnings
1. **Unnecessary cast of void pointer** (nfb_ethdev.c:774)
```c
nfb_eth_dev_create_for_ifc_by_port, (void *)&ifc_params);
```
The cast is unnecessary.
### Info
- Good feature addition for selecting specific ports.
---
## Patch 5/8: net/nfb: init only MACs associated with device
### Errors
None.
### Warnings
1. **Using `calloc()` instead of `rte_calloc()`** (nfb_ethdev.c:74-78)
```c
intl->rxmac = calloc(ifc->eth_cnt, sizeof(*intl->rxmac));
...
intl->txmac = calloc(ifc->eth_cnt, sizeof(*intl->txmac));
```
For consistency within the driver and proper NUMA-aware allocation, consider using `rte_calloc()` or `rte_zmalloc()`. However, since these are control structures (not DMA-accessible), standard `calloc()` is acceptable.
2. **Using `free()` instead of `rte_free()`** (nfb_ethdev.c:114-115)
```c
free(intl->txmac);
free(intl->rxmac);
```
Should match the allocation function. If using `calloc()`, then `free()` is correct.
### Info
- Properly scopes MAC initialization to the associated device.
---
## Patch 6/8: net/nfb: add compatible cards to driver PCI ID table
### Errors
None.
### Warnings
None.
### Info
- Straightforward addition of PCI IDs for additional compatible cards.
---
## Patch 7/8: net/nfb: report firmware version
### Errors
None.
### Warnings
None.
### Info
- Clean implementation of firmware version reporting.
---
## Patch 8/8: doc/nfb: cleanup and update guide
### Errors
None.
### Warnings
1. **HTTP link instead of HTTPS** (nfb.rst:20)
```rst
`historical cards <https://www.liberouter.org/technologies/cards/>`_
```
This appears to be HTTPS, which is correct. No issue.
### Info
- Documentation updates align well with the code changes.
- Release notes properly document the new features.
---
## Summary
### Errors (Must Fix)
1. **Patch 2**: `__rte_internal` function declarations should have return type on separate line per DPDK style.
### Warnings (Should Fix)
1. **Patch 1**: Line exceeds 100 characters in nfb_ethdev.c.
2. **Patch 2**: Extra blank line in nfb.h; line exceeds 100 characters.
3. **Patches 3, 4**: Unnecessary `(void *)` cast.
4. **Patch 3**: Verify file ends with newline.
### General Observations
- The patch series is well-structured with clear separation of concerns.
- Commit messages are well-written and follow DPDK conventions.
- The code changes are logical and the multi-port architecture is cleanly implemented.
- Documentation is updated appropriately with release notes.
^ permalink raw reply [flat|nested] 131+ messages in thread
* Re: [PATCH v6 1/8] net/nfb: prepare for indirect queue mapping scheme
2026-02-02 15:34 ` [PATCH v6 1/8] net/nfb: prepare for indirect queue mapping scheme spinler
@ 2026-02-02 17:50 ` Stephen Hemminger
0 siblings, 0 replies; 131+ messages in thread
From: Stephen Hemminger @ 2026-02-02 17:50 UTC (permalink / raw)
To: spinler; +Cc: dev
On Mon, 2 Feb 2026 16:34:42 +0100
spinler@cesnet.cz wrote:
> priv->queue_map_rx = rte_calloc("NFB queue map", (max_rx_queues + max_tx_queues),
> + sizeof(*priv->queue_map_rx), 0);
You could drop the parenthesis around max_rx_queues + max_tx_queues and shorten line that way.
^ permalink raw reply [flat|nested] 131+ messages in thread
* Re: [REVIEW] doc/nfb: cleanup and update guide
2026-02-02 17:42 ` [REVIEW] " Stephen Hemminger
@ 2026-02-02 17:51 ` Stephen Hemminger
2026-02-02 17:52 ` Stephen Hemminger
` (2 subsequent siblings)
3 siblings, 0 replies; 131+ messages in thread
From: Stephen Hemminger @ 2026-02-02 17:51 UTC (permalink / raw)
To: dev
On Mon, 2 Feb 2026 09:42:35 -0800
Stephen Hemminger <stephen@networkplumber.org> wrote:
> ## Patch 2/8: net/nfb: create one ethdev per ethernet port
>
> ### Errors
>
> 1. **`__rte_internal` functions in header file without the tag appearing alone on its own line** (nfb.h:89-92)
> ```c
> __rte_internal
> int nfb_eth_common_probe(struct nfb_probe_params *params);
> __rte_internal
> int nfb_eth_common_remove(struct rte_device *dev);
> ```
> The `__rte_internal` tag should be alone on its line, but the function signature should have the return type on a separate line per DPDK style:
> ```c
> __rte_internal
> int
> nfb_eth_common_probe(struct nfb_probe_params *params);
> ```
Definately a false positive, ignore this.
^ permalink raw reply [flat|nested] 131+ messages in thread
* Re: [REVIEW] doc/nfb: cleanup and update guide
2026-02-02 17:42 ` [REVIEW] " Stephen Hemminger
2026-02-02 17:51 ` Stephen Hemminger
@ 2026-02-02 17:52 ` Stephen Hemminger
2026-02-02 17:54 ` Stephen Hemminger
2026-02-02 17:54 ` Stephen Hemminger
3 siblings, 0 replies; 131+ messages in thread
From: Stephen Hemminger @ 2026-02-02 17:52 UTC (permalink / raw)
To: dev
On Mon, 2 Feb 2026 09:42:35 -0800
Stephen Hemminger <stephen@networkplumber.org> wrote:
> ## Patch 3/8: net/nfb: add vdev as alternative device probe method
>
> ### Errors
> None.
>
> ### Warnings
>
> 1. **Unnecessary cast of void pointer** (nfb_vdev.c:62)
> ```c
> ret = rte_kvargs_process(kvlist, NFB_ARG_PORT,
> nfb_eth_dev_create_for_ifc_by_port, (void *)&ifc_params);
> ```
> The cast `(void *)` is unnecessary in C.
>
> 2. **Missing newline at end of file** (nfb_vdev.c)
> - The file appears to end without a trailing newline after line 108.
>
> ### Info
> - Useful addition for virtual/simulated NFB devices.
The first is a "meh" not a serious issue
Not sure why false positive #2 happens.
^ permalink raw reply [flat|nested] 131+ messages in thread
* Re: [REVIEW] doc/nfb: cleanup and update guide
2026-02-02 17:42 ` [REVIEW] " Stephen Hemminger
2026-02-02 17:51 ` Stephen Hemminger
2026-02-02 17:52 ` Stephen Hemminger
@ 2026-02-02 17:54 ` Stephen Hemminger
2026-02-02 17:54 ` Stephen Hemminger
3 siblings, 0 replies; 131+ messages in thread
From: Stephen Hemminger @ 2026-02-02 17:54 UTC (permalink / raw)
To: dev
On Mon, 2 Feb 2026 09:42:35 -0800
Stephen Hemminger <stephen@networkplumber.org> wrote:
> ## Patch 5/8: net/nfb: init only MACs associated with device
>
> ### Errors
> None.
>
> ### Warnings
>
> 1. **Using `calloc()` instead of `rte_calloc()`** (nfb_ethdev.c:74-78)
> ```c
> intl->rxmac = calloc(ifc->eth_cnt, sizeof(*intl->rxmac));
> ...
> intl->txmac = calloc(ifc->eth_cnt, sizeof(*intl->txmac));
> ```
> For consistency within the driver and proper NUMA-aware allocation, consider using `rte_calloc()` or `rte_zmalloc()`. However, since these are control structures (not DMA-accessible), standard `calloc()` is acceptable.
>
> 2. **Using `free()` instead of `rte_free()`** (nfb_ethdev.c:114-115)
> ```c
> free(intl->txmac);
> free(intl->rxmac);
> ```
> Should match the allocation function. If using `calloc()`, then `free()` is correct.
Another false-positive. If you can use regular calloc then it is fine.
^ permalink raw reply [flat|nested] 131+ messages in thread
* Re: [REVIEW] doc/nfb: cleanup and update guide
2026-02-02 17:42 ` [REVIEW] " Stephen Hemminger
` (2 preceding siblings ...)
2026-02-02 17:54 ` Stephen Hemminger
@ 2026-02-02 17:54 ` Stephen Hemminger
3 siblings, 0 replies; 131+ messages in thread
From: Stephen Hemminger @ 2026-02-02 17:54 UTC (permalink / raw)
To: dev
On Mon, 2 Feb 2026 09:42:35 -0800
Stephen Hemminger <stephen@networkplumber.org> wrote:
> ## Summary
>
> ### Errors (Must Fix)
> 1. **Patch 2**: `__rte_internal` function declarations should have return type on separate line per DPDK style.
>
> ### Warnings (Should Fix)
> 1. **Patch 1**: Line exceeds 100 characters in nfb_ethdev.c.
> 2. **Patch 2**: Extra blank line in nfb.h; line exceeds 100 characters.
> 3. **Patches 3, 4**: Unnecessary `(void *)` cast.
> 4. **Patch 3**: Verify file ends with newline.
>
> ### General Observations
> - The patch series is well-structured with clear separation of concerns.
> - Commit messages are well-written and follow DPDK conventions.
> - The code changes are logical and the multi-port architecture is cleanly implemented.
> - Documentation is updated appropriately with release notes.
I am fine with patch as is. But getting merge conflicts with next-net and main.
Please rebse.
^ permalink raw reply [flat|nested] 131+ messages in thread
* [PATCH v7 0/8] net/nfb: rework to real multiport
2026-01-15 15:16 [PATCH 0/8] net/nfb: rework to real multiport spinler
` (13 preceding siblings ...)
2026-02-02 15:34 ` [PATCH v6 " spinler
@ 2026-02-04 12:31 ` spinler
2026-02-04 12:31 ` [PATCH v7 1/8] net/nfb: prepare for indirect queue mapping scheme spinler
` (9 more replies)
2026-02-13 18:53 ` [PATCH v8 " spinler
` (2 subsequent siblings)
17 siblings, 10 replies; 131+ messages in thread
From: spinler @ 2026-02-04 12:31 UTC (permalink / raw)
To: dev; +Cc: Martin Spinler
From: Martin Spinler <spinler@cesnet.cz>
This series implements real multiport for better user experience.
The existing driver creates one ethdev/port for one PCI device.
As the CESNET-NDK based cards aren't capable to represent each
Ethernet port by own PCI device, new driver implementation
processes real port configuration from firmware/card and switches
from rte_eth_dev_pci_generic_probe to multiple rte_eth_dev_create calls.
---
Depends-on: patch-37245 ("doc/nfb: update release notes for nfb driver")
v7:
* Rebased to patch-37245
* Removed timestamp update info from release notes
* Added missing ctype include for isdigit()
* Updated style: removed extra brackets
Martin Spinler (8):
net/nfb: prepare for indirect queue mapping scheme
net/nfb: create one ethdev per ethernet port
net/nfb: add vdev as alternative device probe method
net/nfb: add device argument "port" to limit used ports
net/nfb: init only MACs associated with device
net/nfb: add compatible cards to driver PCI ID table
net/nfb: report firmware version
doc/nfb: cleanup and update guide
doc/guides/nics/features/nfb.ini | 4 +
doc/guides/nics/nfb.rst | 187 +++++------
doc/guides/rel_notes/release_26_03.rst | 6 +
drivers/net/nfb/meson.build | 1 +
drivers/net/nfb/nfb.h | 56 +++-
drivers/net/nfb/nfb_ethdev.c | 445 +++++++++++++++++++------
drivers/net/nfb/nfb_rx.c | 30 +-
drivers/net/nfb/nfb_rx.h | 9 +-
drivers/net/nfb/nfb_tx.c | 27 +-
drivers/net/nfb/nfb_tx.h | 7 +-
drivers/net/nfb/nfb_vdev.c | 107 ++++++
11 files changed, 622 insertions(+), 257 deletions(-)
create mode 100644 drivers/net/nfb/nfb_vdev.c
--
2.53.0
^ permalink raw reply [flat|nested] 131+ messages in thread
* [PATCH v7 1/8] net/nfb: prepare for indirect queue mapping scheme
2026-02-04 12:31 ` [PATCH v7 0/8] net/nfb: rework to real multiport spinler
@ 2026-02-04 12:31 ` spinler
2026-02-04 12:31 ` [PATCH v7 2/8] net/nfb: create one ethdev per ethernet port spinler
` (8 subsequent siblings)
9 siblings, 0 replies; 131+ messages in thread
From: spinler @ 2026-02-04 12:31 UTC (permalink / raw)
To: dev; +Cc: Martin Spinler
From: Martin Spinler <spinler@cesnet.cz>
The NFB driver is being enhanced to create one ethdev per physical
Ethernet port instead of one ethdev per PCI device. This requires
an indirect mapping layer because DPDK queue indices no longer
directly correspond to firmware queue indices.
This change adds queue_map_rx and queue_map_tx arrays to track the
mapping between DPDK queue indices and firmware queue IDs, preparing
for the multi-port implementation in subsequent patches.
Signed-off-by: Martin Spinler <spinler@cesnet.cz>
---
drivers/net/nfb/nfb.h | 5 +++++
drivers/net/nfb/nfb_ethdev.c | 21 +++++++++++++++++++++
drivers/net/nfb/nfb_rx.c | 30 +++++++++++++++---------------
drivers/net/nfb/nfb_rx.h | 9 ++++-----
drivers/net/nfb/nfb_tx.c | 27 +++++++++++++++------------
drivers/net/nfb/nfb_tx.h | 7 +++----
6 files changed, 63 insertions(+), 36 deletions(-)
diff --git a/drivers/net/nfb/nfb.h b/drivers/net/nfb/nfb.h
index 90b04c6151..6b74b3e6ec 100644
--- a/drivers/net/nfb/nfb.h
+++ b/drivers/net/nfb/nfb.h
@@ -60,6 +60,11 @@ struct pmd_internals {
struct pmd_priv {
uint16_t max_rx_queues;
uint16_t max_tx_queues;
+
+ /** Mapping from DPDK RX queue index to firmware queue ID */
+ int *queue_map_rx;
+ /** Mapping from DPDK TX queue index to firmware queue ID */
+ int *queue_map_tx;
};
#endif /* _NFB_H_ */
diff --git a/drivers/net/nfb/nfb_ethdev.c b/drivers/net/nfb/nfb_ethdev.c
index 967f127f40..8ded85cddd 100644
--- a/drivers/net/nfb/nfb_ethdev.c
+++ b/drivers/net/nfb/nfb_ethdev.c
@@ -509,6 +509,7 @@ static const struct eth_dev_ops ops = {
static int
nfb_eth_dev_init(struct rte_eth_dev *dev)
{
+ int i;
int ret;
uint32_t mac_count;
struct rte_eth_dev_data *data = dev->data;
@@ -577,6 +578,20 @@ nfb_eth_dev_init(struct rte_eth_dev *dev)
priv->max_rx_queues = max_rx_queues;
priv->max_tx_queues = max_tx_queues;
+ priv->queue_map_rx = rte_calloc("NFB queue map", max_rx_queues + max_tx_queues,
+ sizeof(*priv->queue_map_rx), 0);
+ if (priv->queue_map_rx == NULL) {
+ ret = -ENOMEM;
+ goto err_alloc_queue_map;
+ }
+ priv->queue_map_tx = priv->queue_map_rx + max_rx_queues;
+
+ /* default queue mapping is 1:1 */
+ for (i = 0; i < max_rx_queues; i++)
+ priv->queue_map_rx[i] = i;
+ for (i = 0; i < max_tx_queues; i++)
+ priv->queue_map_tx[i] = i;
+
/* Allocate space for MAC addresses */
mac_count = nfb_eth_get_max_mac_address_count(dev);
data->mac_addrs = rte_zmalloc(data->name,
@@ -608,6 +623,8 @@ nfb_eth_dev_init(struct rte_eth_dev *dev)
return 0;
err_malloc_mac_addrs:
+ rte_free(priv->queue_map_rx);
+err_alloc_queue_map:
nfb_nc_rxmac_deinit(internals->rxmac, internals->max_rxmac);
nfb_nc_txmac_deinit(internals->txmac, internals->max_txmac);
nfb_close(internals->nfb);
@@ -633,6 +650,10 @@ nfb_eth_dev_uninit(struct rte_eth_dev *dev)
struct rte_pci_device *pci_dev = RTE_ETH_DEV_TO_PCI(dev);
struct rte_pci_addr *pci_addr = &pci_dev->addr;
struct pmd_internals *internals = dev->process_private;
+ struct pmd_priv *priv = dev->data->dev_private;
+
+ if (rte_eal_process_type() == RTE_PROC_PRIMARY)
+ rte_free(priv->queue_map_rx);
nfb_nc_rxmac_deinit(internals->rxmac, internals->max_rxmac);
nfb_nc_txmac_deinit(internals->txmac, internals->max_txmac);
diff --git a/drivers/net/nfb/nfb_rx.c b/drivers/net/nfb/nfb_rx.c
index 413d275853..ff630f32f4 100644
--- a/drivers/net/nfb/nfb_rx.c
+++ b/drivers/net/nfb/nfb_rx.c
@@ -61,12 +61,13 @@ nfb_eth_rx_queue_setup(struct rte_eth_dev *dev,
struct rte_mempool *mb_pool)
{
struct pmd_internals *internals = dev->process_private;
+ struct pmd_priv *priv = dev->data->dev_private;
- struct ndp_rx_queue *rxq;
int ret;
+ int qid;
+ struct ndp_rx_queue *rxq;
- rxq = rte_zmalloc_socket("ndp rx queue",
- sizeof(struct ndp_rx_queue),
+ rxq = rte_zmalloc_socket("ndp rx queue", sizeof(struct ndp_rx_queue),
RTE_CACHE_LINE_SIZE, socket_id);
if (rxq == NULL) {
@@ -77,23 +78,23 @@ nfb_eth_rx_queue_setup(struct rte_eth_dev *dev,
rxq->flags = 0;
- ret = nfb_eth_rx_queue_init(internals->nfb,
- rx_queue_id,
- dev->data->port_id,
- mb_pool,
- rxq);
+ qid = priv->queue_map_rx[rx_queue_id];
- if (ret == 0)
- dev->data->rx_queues[rx_queue_id] = rxq;
- else
- rte_free(rxq);
+ ret = nfb_eth_rx_queue_init(internals->nfb, qid, dev->data->port_id, mb_pool, rxq);
+ if (ret)
+ goto err_queue_init;
+ dev->data->rx_queues[rx_queue_id] = rxq;
+ return 0;
+
+err_queue_init:
+ rte_free(rxq);
return ret;
}
int
nfb_eth_rx_queue_init(struct nfb_device *nfb,
- uint16_t rx_queue_id,
+ int qid,
uint16_t port_id,
struct rte_mempool *mb_pool,
struct ndp_rx_queue *rxq)
@@ -104,12 +105,11 @@ nfb_eth_rx_queue_init(struct nfb_device *nfb,
if (nfb == NULL)
return -EINVAL;
- rxq->queue = ndp_open_rx_queue(nfb, rx_queue_id);
+ rxq->queue = ndp_open_rx_queue(nfb, qid);
if (rxq->queue == NULL)
return -EINVAL;
rxq->nfb = nfb;
- rxq->rx_queue_id = rx_queue_id;
rxq->in_port = port_id;
rxq->mb_pool = mb_pool;
rxq->buf_size = (uint16_t)(mbp_priv->mbuf_data_room_size -
diff --git a/drivers/net/nfb/nfb_rx.h b/drivers/net/nfb/nfb_rx.h
index 67b3b00e2a..831945c577 100644
--- a/drivers/net/nfb/nfb_rx.h
+++ b/drivers/net/nfb/nfb_rx.h
@@ -30,7 +30,6 @@ nfb_timestamp_dynfield(struct rte_mbuf *mbuf)
struct ndp_rx_queue {
struct nfb_device *nfb; /* nfb dev structure */
struct ndp_queue *queue; /* rx queue */
- uint16_t rx_queue_id; /* index */
uint8_t in_port; /* port */
uint8_t flags; /* setup flags */
@@ -47,8 +46,8 @@ struct ndp_rx_queue {
*
* @param nfb
* Pointer to nfb device structure.
- * @param rx_queue_id
- * RX queue index.
+ * @param qid
+ * RX queue ID.
* @param port_id
* Device [external] port identifier.
* @param mb_pool
@@ -60,7 +59,7 @@ struct ndp_rx_queue {
*/
int
nfb_eth_rx_queue_init(struct nfb_device *nfb,
- uint16_t rx_queue_id,
+ int qid,
uint16_t port_id,
struct rte_mempool *mb_pool,
struct ndp_rx_queue *rxq);
@@ -70,7 +69,7 @@ nfb_eth_rx_queue_init(struct nfb_device *nfb,
*
* @param dev
* Pointer to Ethernet device structure.
- * @param idx
+ * @param rx_queue_id
* RX queue index.
* @param desc
* Number of descriptors to configure in queue.
diff --git a/drivers/net/nfb/nfb_tx.c b/drivers/net/nfb/nfb_tx.c
index 1f997ce22f..b5cbedeaf6 100644
--- a/drivers/net/nfb/nfb_tx.c
+++ b/drivers/net/nfb/nfb_tx.c
@@ -54,11 +54,13 @@ nfb_eth_tx_queue_setup(struct rte_eth_dev *dev,
const struct rte_eth_txconf *tx_conf __rte_unused)
{
struct pmd_internals *internals = dev->process_private;
+ struct pmd_priv *priv = dev->data->dev_private;
+
int ret;
+ int qid;
struct ndp_tx_queue *txq;
- txq = rte_zmalloc_socket("ndp tx queue",
- sizeof(struct ndp_tx_queue),
+ txq = rte_zmalloc_socket("ndp tx queue", sizeof(struct ndp_tx_queue),
RTE_CACHE_LINE_SIZE, socket_id);
if (txq == NULL) {
@@ -67,32 +69,33 @@ nfb_eth_tx_queue_setup(struct rte_eth_dev *dev,
return -ENOMEM;
}
- ret = nfb_eth_tx_queue_init(internals->nfb,
- tx_queue_id,
- txq);
+ qid = priv->queue_map_tx[tx_queue_id];
- if (ret == 0)
- dev->data->tx_queues[tx_queue_id] = txq;
- else
- rte_free(txq);
+ ret = nfb_eth_tx_queue_init(internals->nfb, qid, txq);
+ if (ret)
+ goto err_queue_init;
+ dev->data->tx_queues[tx_queue_id] = txq;
+ return 0;
+
+err_queue_init:
+ rte_free(txq);
return ret;
}
int
nfb_eth_tx_queue_init(struct nfb_device *nfb,
- uint16_t tx_queue_id,
+ int qid,
struct ndp_tx_queue *txq)
{
if (nfb == NULL)
return -EINVAL;
- txq->queue = ndp_open_tx_queue(nfb, tx_queue_id);
+ txq->queue = ndp_open_tx_queue(nfb, qid);
if (txq->queue == NULL)
return -EINVAL;
txq->nfb = nfb;
- txq->tx_queue_id = tx_queue_id;
txq->tx_pkts = 0;
txq->tx_bytes = 0;
diff --git a/drivers/net/nfb/nfb_tx.h b/drivers/net/nfb/nfb_tx.h
index f107cf914b..c253af1a86 100644
--- a/drivers/net/nfb/nfb_tx.h
+++ b/drivers/net/nfb/nfb_tx.h
@@ -17,7 +17,6 @@
struct ndp_tx_queue {
struct nfb_device *nfb; /* nfb dev structure */
struct ndp_queue *queue; /* tx queue */
- uint16_t tx_queue_id; /* index */
volatile uint64_t tx_pkts; /* packets transmitted */
volatile uint64_t tx_bytes; /* bytes transmitted */
volatile uint64_t err_pkts; /* erroneous packets */
@@ -54,8 +53,8 @@ nfb_eth_tx_queue_setup(struct rte_eth_dev *dev,
*
* @param nfb
* Pointer to nfb device structure.
- * @param tx_queue_id
- * TX queue index.
+ * @param qid
+ * TX queue ID.
* @param[out] txq
* Pointer to ndp_tx_queue output structure
*
@@ -64,7 +63,7 @@ nfb_eth_tx_queue_setup(struct rte_eth_dev *dev,
*/
int
nfb_eth_tx_queue_init(struct nfb_device *nfb,
- uint16_t tx_queue_id,
+ int qid,
struct ndp_tx_queue *txq);
/**
--
2.53.0
^ permalink raw reply related [flat|nested] 131+ messages in thread
* [PATCH v7 2/8] net/nfb: create one ethdev per ethernet port
2026-02-04 12:31 ` [PATCH v7 0/8] net/nfb: rework to real multiport spinler
2026-02-04 12:31 ` [PATCH v7 1/8] net/nfb: prepare for indirect queue mapping scheme spinler
@ 2026-02-04 12:31 ` spinler
2026-02-04 12:31 ` [PATCH v7 3/8] net/nfb: add vdev as alternative device probe method spinler
` (7 subsequent siblings)
9 siblings, 0 replies; 131+ messages in thread
From: spinler @ 2026-02-04 12:31 UTC (permalink / raw)
To: dev; +Cc: Martin Spinler
From: Martin Spinler <spinler@cesnet.cz>
The existing driver creates one ethdev/port for one PCI device.
As the CESNET-NDK-based cards are mostly unable to represent
each physical port with one PCI device, this atypical behaviour
often limits the user.
The nfb-framework comes with generic port representation API,
which provides information about ports and their mapping to firmware
resource (physical ports, DMA queues, processing pipelines).
This driver design switches from one rte_eth_dev_pci_generic_probe call
to multiple rte_eth_dev_create calls, while parses the firmware mapping
information.
Signed-off-by: Martin Spinler <spinler@cesnet.cz>
---
drivers/net/nfb/nfb.h | 27 +++++
drivers/net/nfb/nfb_ethdev.c | 215 ++++++++++++++++++++++++++++-------
2 files changed, 201 insertions(+), 41 deletions(-)
diff --git a/drivers/net/nfb/nfb.h b/drivers/net/nfb/nfb.h
index 6b74b3e6ec..a1134d7786 100644
--- a/drivers/net/nfb/nfb.h
+++ b/drivers/net/nfb/nfb.h
@@ -11,6 +11,7 @@
#include <nfb/ndp.h>
#include <netcope/rxmac.h>
#include <netcope/txmac.h>
+#include <netcope/info.h>
extern int nfb_logtype;
#define RTE_LOGTYPE_NFB nfb_logtype
@@ -51,6 +52,9 @@ struct pmd_internals {
struct nc_rxmac *rxmac[RTE_MAX_NC_RXMAC];
struct nc_txmac *txmac[RTE_MAX_NC_TXMAC];
struct nfb_device *nfb;
+
+ TAILQ_ENTRY(pmd_internals) eth_dev_list; /**< Item in list of all devices */
+ struct rte_eth_dev *eth_dev; /**< Handle for matching the device being removed */
};
/*
@@ -67,4 +71,27 @@ struct pmd_priv {
int *queue_map_tx;
};
+/* Data for common device probing */
+struct nfb_probe_params {
+ /** Generic device information */
+ struct rte_device *device;
+ /** Port bus specific initialisation callback function */
+ ethdev_bus_specific_init specific_init;
+ /** Port bus specific initialisation parameters */
+ void *specific_device;
+
+ const char *path; /**< libnfb device string */
+ const char *args; /**< Device arguments */
+ int nfb_id; /**< ID of NFB device in system */
+ int ep_index; /**< (PCIe) endpoint index for matching assigned interfaces */
+
+ char name[RTE_DEV_NAME_MAX_LEN]; /**< Probed name (e.g. PCI device name) */
+};
+
+
+__rte_internal
+int nfb_eth_common_probe(struct nfb_probe_params *params);
+__rte_internal
+int nfb_eth_common_remove(struct rte_device *dev);
+
#endif /* _NFB_H_ */
diff --git a/drivers/net/nfb/nfb_ethdev.c b/drivers/net/nfb/nfb_ethdev.c
index 8ded85cddd..77a1488731 100644
--- a/drivers/net/nfb/nfb_ethdev.c
+++ b/drivers/net/nfb/nfb_ethdev.c
@@ -4,6 +4,10 @@
* All rights reserved.
*/
+#include <sys/queue.h>
+#include <eal_export.h>
+#include <rte_tailq.h>
+
#include <nfb/nfb.h>
#include <nfb/ndp.h>
#include <netcope/rxmac.h>
@@ -18,6 +22,21 @@
#include "nfb_rxmode.h"
#include "nfb.h"
+struct nfb_ifc_create_params {
+ struct nfb_probe_params *probe_params;
+ struct nc_ifc_map_info map_info;
+ struct nc_ifc_info *ifc_info;
+
+ int basename_len; /* Cached real length of original probe_params->name */
+};
+
+/* The TAILQ entries are used for cleanup of allocated resources
+ * in situations, where dev_close is not called.
+ */
+TAILQ_HEAD(nfb_pmd_internals_head, pmd_internals);
+static struct nfb_pmd_internals_head nfb_eth_dev_list =
+ TAILQ_HEAD_INITIALIZER(nfb_eth_dev_list);
+
static int nfb_eth_dev_uninit(struct rte_eth_dev *dev);
/**
@@ -507,23 +526,20 @@ static const struct eth_dev_ops ops = {
* 0 on success, a negative errno value otherwise.
*/
static int
-nfb_eth_dev_init(struct rte_eth_dev *dev)
+nfb_eth_dev_init(struct rte_eth_dev *dev, void *init_data)
{
int i;
+ int cnt;
int ret;
uint32_t mac_count;
struct rte_eth_dev_data *data = dev->data;
struct pmd_internals *internals;
+ struct nfb_ifc_create_params *params = init_data;
+ struct nc_ifc_info *ifc = params->ifc_info;
+ struct nc_ifc_map_info *mi = ¶ms->map_info;
struct pmd_priv *priv = data->dev_private;
- struct rte_pci_device *pci_dev = RTE_ETH_DEV_TO_PCI(dev);
- struct rte_pci_addr *pci_addr = &pci_dev->addr;
struct rte_ether_addr eth_addr_init;
uint16_t max_rx_queues, max_tx_queues;
- char nfb_dev[PATH_MAX];
-
- NFB_LOG(INFO, "Initializing NFB device (" PCI_PRI_FMT ")",
- pci_addr->domain, pci_addr->bus, pci_addr->devid,
- pci_addr->function);
internals = rte_zmalloc_socket("nfb_internals",
sizeof(struct pmd_internals), RTE_CACHE_LINE_SIZE,
@@ -535,27 +551,17 @@ nfb_eth_dev_init(struct rte_eth_dev *dev)
dev->process_private = internals;
- snprintf(nfb_dev, sizeof(nfb_dev),
- "/dev/nfb/by-pci-slot/" PCI_PRI_FMT,
- pci_addr->domain, pci_addr->bus, pci_addr->devid,
- pci_addr->function);
-
- /*
- * Get number of available DMA RX and TX queues, which is maximum
- * number of queues that can be created and store it in private device
- * data structure.
- */
- internals->nfb = nfb_open(nfb_dev);
+ /* Open device handle */
+ internals->nfb = nfb_open(params->probe_params->path);
if (internals->nfb == NULL) {
- NFB_LOG(ERR, "nfb_open(): failed to open %s", nfb_dev);
+ NFB_LOG(ERR, "nfb_open(): failed to open %s", params->probe_params->path);
ret = -EINVAL;
goto err_nfb_open;
}
- max_rx_queues = ndp_get_rx_queue_available_count(internals->nfb);
- max_tx_queues = ndp_get_tx_queue_available_count(internals->nfb);
- NFB_LOG(INFO, "Available NDP queues RX: %u TX: %u",
- max_rx_queues, max_tx_queues);
+ /* Get number of available DMA RX and TX queues */
+ max_rx_queues = ifc->rxq_cnt;
+ max_tx_queues = ifc->txq_cnt;
nfb_nc_rxmac_init(internals->nfb,
internals->rxmac,
@@ -586,11 +592,18 @@ nfb_eth_dev_init(struct rte_eth_dev *dev)
}
priv->queue_map_tx = priv->queue_map_rx + max_rx_queues;
- /* default queue mapping is 1:1 */
- for (i = 0; i < max_rx_queues; i++)
- priv->queue_map_rx[i] = i;
- for (i = 0; i < max_tx_queues; i++)
- priv->queue_map_tx[i] = i;
+ /* Use queue mapping provided by libnfb */
+ cnt = 0;
+ for (i = 0; i < mi->rxq_cnt; i++) {
+ if (mi->rxq[i].ifc == ifc->id)
+ priv->queue_map_rx[cnt++] = mi->rxq[i].id;
+ }
+
+ cnt = 0;
+ for (i = 0; i < mi->txq_cnt; i++) {
+ if (mi->txq[i].ifc == ifc->id)
+ priv->queue_map_tx[cnt++] = mi->txq[i].id;
+ }
/* Allocate space for MAC addresses */
mac_count = nfb_eth_get_max_mac_address_count(dev);
@@ -616,10 +629,6 @@ nfb_eth_dev_init(struct rte_eth_dev *dev)
data->dev_flags |= RTE_ETH_DEV_AUTOFILL_QUEUE_XSTATS;
}
- NFB_LOG(INFO, "NFB device (" PCI_PRI_FMT ") successfully initialized",
- pci_addr->domain, pci_addr->bus, pci_addr->devid,
- pci_addr->function);
-
return 0;
err_malloc_mac_addrs:
@@ -647,25 +656,104 @@ nfb_eth_dev_init(struct rte_eth_dev *dev)
static int
nfb_eth_dev_uninit(struct rte_eth_dev *dev)
{
- struct rte_pci_device *pci_dev = RTE_ETH_DEV_TO_PCI(dev);
- struct rte_pci_addr *pci_addr = &pci_dev->addr;
struct pmd_internals *internals = dev->process_private;
struct pmd_priv *priv = dev->data->dev_private;
if (rte_eal_process_type() == RTE_PROC_PRIMARY)
rte_free(priv->queue_map_rx);
+ TAILQ_REMOVE(&nfb_eth_dev_list, internals, eth_dev_list);
+
nfb_nc_rxmac_deinit(internals->rxmac, internals->max_rxmac);
nfb_nc_txmac_deinit(internals->txmac, internals->max_txmac);
nfb_close(internals->nfb);
rte_free(internals);
- NFB_LOG(INFO, "NFB device (" PCI_PRI_FMT ") successfully uninitialized",
- pci_addr->domain, pci_addr->bus, pci_addr->devid,
- pci_addr->function);
+ return 0;
+}
+
+static int
+nfb_eth_dev_create_for_ifc(struct nfb_ifc_create_params *cp)
+{
+ int ret;
+ struct nc_ifc_info *ifc;
+ struct nfb_probe_params *pp;
+ struct rte_eth_dev *eth_dev;
+ struct pmd_internals *p;
+
+ ifc = cp->ifc_info;
+ pp = cp->probe_params;
+
+ /* Skip interfaces which doesn't belong to the probed PCI device */
+ if ((pp->ep_index != -1 && ifc->ep != pp->ep_index) ||
+ (ifc->flags & NC_IFC_INFO_FLAG_ACTIVE) == 0)
+ return 0;
+
+ ret = snprintf(pp->name + cp->basename_len, sizeof(pp->name) - cp->basename_len,
+ "_eth%d", ifc->id);
+ if (ret < 0 || ret >= (signed int)sizeof(pp->name) - cp->basename_len)
+ return -EINVAL;
+
+ ret = rte_eth_dev_create(pp->device, pp->name,
+ sizeof(struct pmd_priv),
+ pp->specific_init, pp->specific_device,
+ nfb_eth_dev_init, cp);
+ if (ret)
+ goto out;
+
+ eth_dev = rte_eth_dev_get_by_name(pp->name);
+ p = eth_dev->process_private;
+ p->eth_dev = eth_dev;
+ TAILQ_INSERT_TAIL(&nfb_eth_dev_list, p, eth_dev_list);
+
+out:
+ /* return to original name (just for clarity) */
+ pp->name[cp->basename_len] = '\0';
+ return ret;
+}
+
+RTE_EXPORT_INTERNAL_SYMBOL(nfb_eth_common_probe)
+int
+nfb_eth_common_probe(struct nfb_probe_params *params)
+{
+ int i;
+ int ret;
+
+ struct nfb_device *nfb_dev;
+ struct nfb_ifc_create_params ifc_params;
+
+ nfb_dev = nfb_open(params->path);
+ if (nfb_dev == NULL) {
+ NFB_LOG(ERR, "nfb_open(): failed to open %s", params->path);
+ return -EINVAL;
+ }
+
+ ret = nc_ifc_map_info_create_ordinary(nfb_dev, &ifc_params.map_info);
+ if (ret)
+ goto err_map_info_create;
+
+ ifc_params.probe_params = params;
+ ifc_params.basename_len = strlen(params->name);
+
+ for (i = 0; i < ifc_params.map_info.ifc_cnt; i++) {
+ ifc_params.ifc_info = &ifc_params.map_info.ifc[i];
+ ret = nfb_eth_dev_create_for_ifc(&ifc_params);
+ if (ret)
+ goto err_dev_create;
+ }
+
+ nc_map_info_destroy(&ifc_params.map_info);
+ nfb_close(nfb_dev);
return 0;
+
+err_dev_create:
+ nfb_eth_common_remove(params->device);
+ nc_map_info_destroy(&ifc_params.map_info);
+err_map_info_create:
+ nfb_close(nfb_dev);
+ return ret;
}
static const struct rte_pci_id nfb_pci_id_table[] = {
@@ -694,8 +782,53 @@ static int
nfb_eth_pci_probe(struct rte_pci_driver *pci_drv __rte_unused,
struct rte_pci_device *pci_dev)
{
- return rte_eth_dev_pci_generic_probe(pci_dev,
- sizeof(struct pmd_priv), nfb_eth_dev_init);
+ int ret;
+ char path[PATH_MAX];
+
+ struct nc_composed_device_info comp_dev_info;
+ struct nfb_probe_params params = {0};
+
+ rte_pci_device_name(&pci_dev->addr, params.name, sizeof(params.name));
+
+ /*
+ * NFB device can be composed from multiple PCI devices,
+ * find the base char device ID for the current PCI device
+ */
+ ret = nc_get_composed_device_info_by_pci(NULL, params.name, &comp_dev_info);
+ if (ret) {
+ NFB_LOG(ERR, "Could not find NFB device for %s", params.name);
+ return -ENODEV;
+ }
+
+ ret = snprintf(path, sizeof(path), NFB_BASE_DEV_PATH "%d", comp_dev_info.nfb_id);
+ if (ret < 0 || ret >= (signed int)sizeof(path))
+ return -EINVAL;
+
+ params.args = pci_dev->device.devargs ? pci_dev->device.devargs->args : NULL;
+ params.path = path;
+ params.nfb_id = comp_dev_info.nfb_id;
+ params.ep_index = comp_dev_info.ep_index;
+
+ params.device = &pci_dev->device;
+ params.specific_init = eth_dev_pci_specific_init;
+ params.specific_device = pci_dev;
+
+ return nfb_eth_common_probe(¶ms);
+}
+
+RTE_EXPORT_INTERNAL_SYMBOL(nfb_eth_common_remove)
+int
+nfb_eth_common_remove(struct rte_device *dev)
+{
+ struct pmd_internals *entry, *temp;
+
+ RTE_TAILQ_FOREACH_SAFE(entry, &nfb_eth_dev_list, eth_dev_list, temp) {
+ if (dev == entry->eth_dev->device) {
+ TAILQ_REMOVE(&nfb_eth_dev_list, entry, eth_dev_list);
+ rte_eth_dev_destroy(entry->eth_dev, nfb_eth_dev_uninit);
+ }
+ }
+ return 0;
}
/**
@@ -712,7 +845,7 @@ nfb_eth_pci_probe(struct rte_pci_driver *pci_drv __rte_unused,
static int
nfb_eth_pci_remove(struct rte_pci_device *pci_dev)
{
- return rte_eth_dev_pci_generic_remove(pci_dev, nfb_eth_dev_uninit);
+ return nfb_eth_common_remove(&pci_dev->device);
}
static struct rte_pci_driver nfb_eth_driver = {
--
2.53.0
^ permalink raw reply related [flat|nested] 131+ messages in thread
* [PATCH v7 3/8] net/nfb: add vdev as alternative device probe method
2026-02-04 12:31 ` [PATCH v7 0/8] net/nfb: rework to real multiport spinler
2026-02-04 12:31 ` [PATCH v7 1/8] net/nfb: prepare for indirect queue mapping scheme spinler
2026-02-04 12:31 ` [PATCH v7 2/8] net/nfb: create one ethdev per ethernet port spinler
@ 2026-02-04 12:31 ` spinler
2026-02-04 12:31 ` [PATCH v7 4/8] net/nfb: add device argument "port" to limit used ports spinler
` (6 subsequent siblings)
9 siblings, 0 replies; 131+ messages in thread
From: spinler @ 2026-02-04 12:31 UTC (permalink / raw)
To: dev; +Cc: Martin Spinler
From: Martin Spinler <spinler@cesnet.cz>
VDEV are useful for NFB devices not represented by PCI device,
e.g. virtual or simulated devices.
Signed-off-by: Martin Spinler <spinler@cesnet.cz>
---
drivers/net/nfb/meson.build | 1 +
drivers/net/nfb/nfb_vdev.c | 106 ++++++++++++++++++++++++++++++++++++
2 files changed, 107 insertions(+)
create mode 100644 drivers/net/nfb/nfb_vdev.c
diff --git a/drivers/net/nfb/meson.build b/drivers/net/nfb/meson.build
index d7a255c928..9e458dfb4a 100644
--- a/drivers/net/nfb/meson.build
+++ b/drivers/net/nfb/meson.build
@@ -16,6 +16,7 @@ ext_deps += dep
sources = files(
'nfb_ethdev.c',
+ 'nfb_vdev.c',
'nfb_rx.c',
'nfb_rxmode.c',
'nfb_stats.c',
diff --git a/drivers/net/nfb/nfb_vdev.c b/drivers/net/nfb/nfb_vdev.c
new file mode 100644
index 0000000000..b79f7ac416
--- /dev/null
+++ b/drivers/net/nfb/nfb_vdev.c
@@ -0,0 +1,106 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2026 Cesnet
+ */
+
+#include <rte_kvargs.h>
+#include <ethdev_vdev.h>
+
+#include "nfb.h"
+
+#define VDEV_NFB_DRIVER net_vdev_nfb
+#define VDEV_NFB_ARG_DEV "dev"
+
+static int
+vdev_nfb_vdev_probe(struct rte_vdev_device *dev)
+{
+ unsigned int count;
+ int ret = 0;
+ size_t len, pos;
+
+ struct nfb_probe_params params;
+
+ const char *vdev_name = rte_vdev_device_name(dev);
+ const char *vdev_args = rte_vdev_device_args(dev);
+ char *dev_params, *dev_params_mod;
+ struct rte_kvargs *kvargs;
+
+ kvargs = rte_kvargs_parse(vdev_args, NULL);
+ if (kvargs == NULL) {
+ NFB_LOG(ERR, "Failed to parse device arguments %s", vdev_args);
+ ret = -EINVAL;
+ goto err_parse_args;
+ }
+
+ dev_params = strdup(vdev_args);
+ if (dev_params == NULL) {
+ ret = -ENOMEM;
+ goto err_strdup_params;
+ }
+
+ params.device = &dev->device;
+ params.specific_init = NULL;
+ params.specific_device = NULL;
+ params.path = nfb_default_dev_path();
+ params.args = dev_params;
+ params.nfb_id = 0;
+ params.ep_index = -1;
+
+ len = strlen(dev_params) + 1;
+ pos = 0;
+ dev_params[pos] = '\0';
+
+ /* Parse parameters for virtual device */
+ for (count = 0; count != kvargs->count; ++count) {
+ const struct rte_kvargs_pair *pair = &kvargs->pairs[count];
+
+ if (!strcmp(pair->key, VDEV_NFB_ARG_DEV)) {
+ params.path = pair->value;
+ } else {
+ /* Clone non-vdev arguments, result is shorter or equal length */
+ dev_params_mod = dev_params + pos;
+ ret = snprintf(dev_params_mod, len, "%s%s=%s",
+ pos == 0 ? "" : ",", pair->key, pair->value);
+ if (ret < 0 || ret >= (signed int)len)
+ goto err_clone_args;
+ pos += ret;
+ len -= ret;
+ }
+ }
+
+ strlcpy(params.name, vdev_name, sizeof(params.name));
+
+ ret = nfb_eth_common_probe(¶ms);
+ if (ret)
+ goto err_nfb_common_probe;
+
+ free(dev_params);
+ rte_kvargs_free(kvargs);
+
+ return ret;
+
+err_nfb_common_probe:
+err_clone_args:
+ free(dev_params);
+err_strdup_params:
+ rte_kvargs_free(kvargs);
+err_parse_args:
+ return ret;
+}
+
+static int
+vdev_nfb_vdev_remove(struct rte_vdev_device *dev)
+{
+ return nfb_eth_common_remove(&dev->device);
+}
+
+/** Virtual device descriptor. */
+static struct rte_vdev_driver vdev_nfb_vdev = {
+ .probe = vdev_nfb_vdev_probe,
+ .remove = vdev_nfb_vdev_remove,
+};
+
+RTE_PMD_REGISTER_VDEV(VDEV_NFB_DRIVER, vdev_nfb_vdev);
+RTE_PMD_REGISTER_ALIAS(VDEV_NFB_DRIVER, eth_vdev_nfb);
+RTE_PMD_REGISTER_PARAM_STRING(net_vdev_nfb,
+ VDEV_NFB_ARG_DEV "=<string>"
+ );
--
2.53.0
^ permalink raw reply related [flat|nested] 131+ messages in thread
* [PATCH v7 4/8] net/nfb: add device argument "port" to limit used ports
2026-02-04 12:31 ` [PATCH v7 0/8] net/nfb: rework to real multiport spinler
` (2 preceding siblings ...)
2026-02-04 12:31 ` [PATCH v7 3/8] net/nfb: add vdev as alternative device probe method spinler
@ 2026-02-04 12:31 ` spinler
2026-02-04 12:31 ` [PATCH v7 5/8] net/nfb: init only MACs associated with device spinler
` (5 subsequent siblings)
9 siblings, 0 replies; 131+ messages in thread
From: spinler @ 2026-02-04 12:31 UTC (permalink / raw)
To: dev; +Cc: Martin Spinler
From: Martin Spinler <spinler@cesnet.cz>
NFB devices do not use separate PCI device for each port; one PCI device
contains handles for all ports instead. For some application this approach
can be limiting.
The "port" argument can be used to select only desired ports.
It can be used multiple times. When is not used at all, the driver
selects all ports.
Signed-off-by: Martin Spinler <spinler@cesnet.cz>
---
drivers/net/nfb/nfb.h | 7 +++++
drivers/net/nfb/nfb_ethdev.c | 55 ++++++++++++++++++++++++++++++++++--
drivers/net/nfb/nfb_vdev.c | 3 +-
3 files changed, 61 insertions(+), 4 deletions(-)
diff --git a/drivers/net/nfb/nfb.h b/drivers/net/nfb/nfb.h
index a1134d7786..10d74eb49c 100644
--- a/drivers/net/nfb/nfb.h
+++ b/drivers/net/nfb/nfb.h
@@ -42,6 +42,13 @@ extern int nfb_logtype;
#define RTE_NFB_DRIVER_NAME net_nfb
+/* Device arguments */
+#define NFB_ARG_PORT "port"
+
+#define NFB_COMMON_ARGS \
+ NFB_ARG_PORT "=<number>" \
+ ""
+
/*
* Handles obtained from the libnfb: each process must use own instance.
* Stored inside dev->process_private.
diff --git a/drivers/net/nfb/nfb_ethdev.c b/drivers/net/nfb/nfb_ethdev.c
index 77a1488731..09a18362f5 100644
--- a/drivers/net/nfb/nfb_ethdev.c
+++ b/drivers/net/nfb/nfb_ethdev.c
@@ -4,6 +4,7 @@
* All rights reserved.
*/
+#include <ctype.h>
#include <sys/queue.h>
#include <eal_export.h>
#include <rte_tailq.h>
@@ -22,6 +23,11 @@
#include "nfb_rxmode.h"
#include "nfb.h"
+static const char * const VALID_KEYS[] = {
+ NFB_ARG_PORT,
+ NULL
+};
+
struct nfb_ifc_create_params {
struct nfb_probe_params *probe_params;
struct nc_ifc_map_info map_info;
@@ -713,6 +719,24 @@ nfb_eth_dev_create_for_ifc(struct nfb_ifc_create_params *cp)
return ret;
}
+static int nfb_eth_dev_create_for_ifc_by_port(const char *key __rte_unused,
+ const char *value, void *opaque)
+{
+ char *end;
+ unsigned long port;
+ struct nfb_ifc_create_params *ifc_params = opaque;
+
+ if (value == NULL || strlen(value) == 0 || !isdigit(*value))
+ return -EINVAL;
+
+ port = strtoul(value, &end, 0);
+ if (*end != '\0' || port >= (unsigned long)ifc_params->map_info.ifc_cnt)
+ return -EINVAL;
+
+ ifc_params->ifc_info = &ifc_params->map_info.ifc[port];
+ return nfb_eth_dev_create_for_ifc(ifc_params);
+}
+
RTE_EXPORT_INTERNAL_SYMBOL(nfb_eth_common_probe)
int
nfb_eth_common_probe(struct nfb_probe_params *params)
@@ -722,6 +746,7 @@ nfb_eth_common_probe(struct nfb_probe_params *params)
struct nfb_device *nfb_dev;
struct nfb_ifc_create_params ifc_params;
+ struct rte_kvargs *kvlist = NULL;
nfb_dev = nfb_open(params->path);
if (nfb_dev == NULL) {
@@ -733,16 +758,35 @@ nfb_eth_common_probe(struct nfb_probe_params *params)
if (ret)
goto err_map_info_create;
+ if (params->args != NULL && strlen(params->args) > 0) {
+ kvlist = rte_kvargs_parse(params->args, VALID_KEYS);
+ if (kvlist == NULL) {
+ NFB_LOG(ERR, "Failed to parse device arguments %s", params->args);
+ ret = -EINVAL;
+ goto err_parse_args;
+ }
+ }
+
ifc_params.probe_params = params;
ifc_params.basename_len = strlen(params->name);
- for (i = 0; i < ifc_params.map_info.ifc_cnt; i++) {
- ifc_params.ifc_info = &ifc_params.map_info.ifc[i];
- ret = nfb_eth_dev_create_for_ifc(&ifc_params);
+ /* When at least one port argument is specified, create only selected ports */
+ if (kvlist && rte_kvargs_count(kvlist, NFB_ARG_PORT)) {
+ ret = rte_kvargs_process(kvlist, NFB_ARG_PORT,
+ nfb_eth_dev_create_for_ifc_by_port, (void *)&ifc_params);
if (ret)
goto err_dev_create;
+ } else {
+ for (i = 0; i < ifc_params.map_info.ifc_cnt; i++) {
+ ifc_params.ifc_info = &ifc_params.map_info.ifc[i];
+ ret = nfb_eth_dev_create_for_ifc(&ifc_params);
+ if (ret)
+ goto err_dev_create;
+ }
}
+ rte_kvargs_free(kvlist);
+
nc_map_info_destroy(&ifc_params.map_info);
nfb_close(nfb_dev);
@@ -750,6 +794,8 @@ nfb_eth_common_probe(struct nfb_probe_params *params)
err_dev_create:
nfb_eth_common_remove(params->device);
+ rte_kvargs_free(kvlist);
+err_parse_args:
nc_map_info_destroy(&ifc_params.map_info);
err_map_info_create:
nfb_close(nfb_dev);
@@ -858,3 +904,6 @@ RTE_PMD_REGISTER_PCI(RTE_NFB_DRIVER_NAME, nfb_eth_driver);
RTE_PMD_REGISTER_PCI_TABLE(RTE_NFB_DRIVER_NAME, nfb_pci_id_table);
RTE_PMD_REGISTER_KMOD_DEP(RTE_NFB_DRIVER_NAME, "* nfb");
RTE_LOG_REGISTER_DEFAULT(nfb_logtype, NOTICE);
+RTE_PMD_REGISTER_PARAM_STRING(RTE_NFB_DRIVER_NAME,
+ NFB_COMMON_ARGS
+ );
diff --git a/drivers/net/nfb/nfb_vdev.c b/drivers/net/nfb/nfb_vdev.c
index b79f7ac416..57f10d2068 100644
--- a/drivers/net/nfb/nfb_vdev.c
+++ b/drivers/net/nfb/nfb_vdev.c
@@ -102,5 +102,6 @@ static struct rte_vdev_driver vdev_nfb_vdev = {
RTE_PMD_REGISTER_VDEV(VDEV_NFB_DRIVER, vdev_nfb_vdev);
RTE_PMD_REGISTER_ALIAS(VDEV_NFB_DRIVER, eth_vdev_nfb);
RTE_PMD_REGISTER_PARAM_STRING(net_vdev_nfb,
- VDEV_NFB_ARG_DEV "=<string>"
+ VDEV_NFB_ARG_DEV "=<string> "
+ NFB_COMMON_ARGS
);
--
2.53.0
^ permalink raw reply related [flat|nested] 131+ messages in thread
* [PATCH v7 5/8] net/nfb: init only MACs associated with device
2026-02-04 12:31 ` [PATCH v7 0/8] net/nfb: rework to real multiport spinler
` (3 preceding siblings ...)
2026-02-04 12:31 ` [PATCH v7 4/8] net/nfb: add device argument "port" to limit used ports spinler
@ 2026-02-04 12:31 ` spinler
2026-02-04 12:31 ` [PATCH v7 6/8] net/nfb: add compatible cards to driver PCI ID table spinler
` (4 subsequent siblings)
9 siblings, 0 replies; 131+ messages in thread
From: spinler @ 2026-02-04 12:31 UTC (permalink / raw)
To: dev; +Cc: Martin Spinler
From: Martin Spinler <spinler@cesnet.cz>
Use information obtained from the new manner of creating NFB ethdevs
and initialize only RxMACs / TxMACs associated with actual ethdev.
This enables separated management and configuration of interface ports.
Signed-off-by: Martin Spinler <spinler@cesnet.cz>
---
drivers/net/nfb/nfb.h | 12 +--
drivers/net/nfb/nfb_ethdev.c | 137 ++++++++++++++++-------------------
2 files changed, 67 insertions(+), 82 deletions(-)
diff --git a/drivers/net/nfb/nfb.h b/drivers/net/nfb/nfb.h
index 10d74eb49c..01241860b1 100644
--- a/drivers/net/nfb/nfb.h
+++ b/drivers/net/nfb/nfb.h
@@ -36,10 +36,6 @@ extern int nfb_logtype;
#define RTE_ETH_NDP_MAX_RX_QUEUES 32
#define RTE_ETH_NDP_MAX_TX_QUEUES 32
-/* Max index of rx/tx dmas */
-#define RTE_MAX_NC_RXMAC 256
-#define RTE_MAX_NC_TXMAC 256
-
#define RTE_NFB_DRIVER_NAME net_nfb
/* Device arguments */
@@ -54,10 +50,10 @@ extern int nfb_logtype;
* Stored inside dev->process_private.
*/
struct pmd_internals {
- uint16_t max_rxmac;
- uint16_t max_txmac;
- struct nc_rxmac *rxmac[RTE_MAX_NC_RXMAC];
- struct nc_txmac *txmac[RTE_MAX_NC_TXMAC];
+ uint16_t max_rxmac; /**< Count of valid rxmac items */
+ uint16_t max_txmac; /**< Count of valid txmac items */
+ struct nc_rxmac **rxmac; /**< Array of Rx MAC handles */
+ struct nc_txmac **txmac; /**< Array of Tx MAC handles */
struct nfb_device *nfb;
TAILQ_ENTRY(pmd_internals) eth_dev_list; /**< Item in list of all devices */
diff --git a/drivers/net/nfb/nfb_ethdev.c b/drivers/net/nfb/nfb_ethdev.c
index 09a18362f5..2c3c52d7ad 100644
--- a/drivers/net/nfb/nfb_ethdev.c
+++ b/drivers/net/nfb/nfb_ethdev.c
@@ -53,81 +53,75 @@ static const struct rte_ether_addr eth_addr = {
};
/**
- * Open all RX DMA queues
+ * Open Rx and Tx MAC components associated with current ifc
*
- * @param dev
- * Pointer to nfb device.
- * @param[out] rxmac
- * Pointer to output array of nc_rxmac
- * @param[out] max_rxmac
- * Pointer to output max index of rxmac
+ * @param intl
+ * Pointer to driver internal structure
+ * @param params
+ * Pointer to init parameters structure
*/
-static void
-nfb_nc_rxmac_init(struct nfb_device *nfb,
- struct nc_rxmac *rxmac[RTE_MAX_NC_RXMAC],
- uint16_t *max_rxmac)
+static int
+nfb_nc_eth_init(struct pmd_internals *intl, struct nfb_ifc_create_params *params)
{
- *max_rxmac = 0;
- while ((rxmac[*max_rxmac] = nc_rxmac_open_index(nfb, *max_rxmac)))
- ++(*max_rxmac);
-}
+ int ret;
+ int i, rxm, txm;
+ struct nc_ifc_info *ifc = params->ifc_info;
+ struct nc_ifc_map_info *mi = ¶ms->map_info;
-/**
- * Open all TX DMA queues
- *
- * @param dev
- * Pointer to nfb device.
- * @param[out] txmac
- * Pointer to output array of nc_txmac
- * @param[out] max_rxmac
- * Pointer to output max index of txmac
- */
-static void
-nfb_nc_txmac_init(struct nfb_device *nfb,
- struct nc_txmac *txmac[RTE_MAX_NC_TXMAC],
- uint16_t *max_txmac)
-{
- *max_txmac = 0;
- while ((txmac[*max_txmac] = nc_txmac_open_index(nfb, *max_txmac)))
- ++(*max_txmac);
-}
+ ret = -ENOMEM;
+ if (ifc->eth_cnt == 0)
+ return 0;
-/**
- * Close all RX DMA queues
- *
- * @param rxmac
- * Pointer to array of nc_rxmac
- * @param max_rxmac
- * Maximum index of rxmac
- */
-static void
-nfb_nc_rxmac_deinit(struct nc_rxmac *rxmac[RTE_MAX_NC_RXMAC],
- uint16_t max_rxmac)
-{
- uint16_t i;
- for (i = 0; i < max_rxmac; i++) {
- nc_rxmac_close(rxmac[i]);
- rxmac[i] = NULL;
+ intl->rxmac = calloc(ifc->eth_cnt, sizeof(*intl->rxmac));
+ if (intl->rxmac == NULL)
+ goto err_alloc_rxmac;
+
+ intl->txmac = calloc(ifc->eth_cnt, sizeof(*intl->txmac));
+ if (intl->txmac == NULL)
+ goto err_alloc_txmac;
+
+ /* Some eths may not have assigned MAC nodes, hence use separate var for indexing */
+ rxm = 0;
+ txm = 0;
+ for (i = 0; i < mi->eth_cnt; i++) {
+ if (mi->eth[i].ifc != ifc->id)
+ continue;
+
+ intl->rxmac[rxm] = nc_rxmac_open(intl->nfb, mi->eth[i].node_rxmac);
+ if (intl->rxmac[rxm])
+ rxm++;
+
+ intl->txmac[txm] = nc_txmac_open(intl->nfb, mi->eth[i].node_txmac);
+ if (intl->txmac[txm])
+ txm++;
}
+
+ intl->max_rxmac = rxm;
+ intl->max_txmac = txm;
+ return 0;
+
+err_alloc_txmac:
+ free(intl->rxmac);
+err_alloc_rxmac:
+ return ret;
}
/**
- * Close all TX DMA queues
- *
- * @param txmac
- * Pointer to array of nc_txmac
- * @param max_txmac
- * Maximum index of txmac
+ * Close all Rx+Tx MAC components
+ * @param intl
+ * Pointer to internal driver structure
*/
static void
-nfb_nc_txmac_deinit(struct nc_txmac *txmac[RTE_MAX_NC_TXMAC],
- uint16_t max_txmac)
+nfb_nc_eth_deinit(struct pmd_internals *intl)
{
uint16_t i;
- for (i = 0; i < max_txmac; i++) {
- nc_txmac_close(txmac[i]);
- txmac[i] = NULL;
- }
+ for (i = 0; i < intl->max_txmac; i++)
+ nc_txmac_close(intl->txmac[i]);
+ for (i = 0; i < intl->max_rxmac; i++)
+ nc_rxmac_close(intl->rxmac[i]);
+
+ free(intl->txmac);
+ free(intl->rxmac);
}
/**
@@ -344,7 +338,7 @@ nfb_eth_link_update(struct rte_eth_dev *dev,
link.link_duplex = RTE_ETH_LINK_FULL_DUPLEX;
link.link_autoneg = RTE_ETH_LINK_SPEED_FIXED;
- if (internals->rxmac[0] != NULL) {
+ if (internals->max_rxmac) {
nc_rxmac_read_status(internals->rxmac[0], &status);
switch (status.speed) {
@@ -569,12 +563,9 @@ nfb_eth_dev_init(struct rte_eth_dev *dev, void *init_data)
max_rx_queues = ifc->rxq_cnt;
max_tx_queues = ifc->txq_cnt;
- nfb_nc_rxmac_init(internals->nfb,
- internals->rxmac,
- &internals->max_rxmac);
- nfb_nc_txmac_init(internals->nfb,
- internals->txmac,
- &internals->max_txmac);
+ ret = nfb_nc_eth_init(internals, params);
+ if (ret)
+ goto err_nc_eth_init;
/* Set rx, tx burst functions */
dev->rx_pkt_burst = nfb_eth_ndp_rx;
@@ -640,10 +631,9 @@ nfb_eth_dev_init(struct rte_eth_dev *dev, void *init_data)
err_malloc_mac_addrs:
rte_free(priv->queue_map_rx);
err_alloc_queue_map:
- nfb_nc_rxmac_deinit(internals->rxmac, internals->max_rxmac);
- nfb_nc_txmac_deinit(internals->txmac, internals->max_txmac);
+ nfb_nc_eth_deinit(internals);
+err_nc_eth_init:
nfb_close(internals->nfb);
-
err_nfb_open:
rte_free(internals);
err_alloc_internals:
@@ -670,8 +660,7 @@ nfb_eth_dev_uninit(struct rte_eth_dev *dev)
TAILQ_REMOVE(&nfb_eth_dev_list, internals, eth_dev_list);
- nfb_nc_rxmac_deinit(internals->rxmac, internals->max_rxmac);
- nfb_nc_txmac_deinit(internals->txmac, internals->max_txmac);
+ nfb_nc_eth_deinit(internals);
nfb_close(internals->nfb);
rte_free(internals);
--
2.53.0
^ permalink raw reply related [flat|nested] 131+ messages in thread
* [PATCH v7 6/8] net/nfb: add compatible cards to driver PCI ID table
2026-02-04 12:31 ` [PATCH v7 0/8] net/nfb: rework to real multiport spinler
` (4 preceding siblings ...)
2026-02-04 12:31 ` [PATCH v7 5/8] net/nfb: init only MACs associated with device spinler
@ 2026-02-04 12:31 ` spinler
2026-02-04 12:31 ` [PATCH v7 7/8] net/nfb: report firmware version spinler
` (3 subsequent siblings)
9 siblings, 0 replies; 131+ messages in thread
From: spinler @ 2026-02-04 12:31 UTC (permalink / raw)
To: dev; +Cc: Martin Spinler
From: Martin Spinler <spinler@cesnet.cz>
The nfb driver is compatible with:
- NFB-200G2QL card secondary PCI device
- FB2CGHH card
- COMBO-400G1 card
- Common CESNET-NDK cards
Signed-off-by: Martin Spinler <spinler@cesnet.cz>
---
drivers/net/nfb/nfb.h | 5 +++++
drivers/net/nfb/nfb_ethdev.c | 4 ++++
2 files changed, 9 insertions(+)
diff --git a/drivers/net/nfb/nfb.h b/drivers/net/nfb/nfb.h
index 01241860b1..2bce71ac89 100644
--- a/drivers/net/nfb/nfb.h
+++ b/drivers/net/nfb/nfb.h
@@ -24,13 +24,18 @@ extern int nfb_logtype;
/* PCI Vendor ID */
#define PCI_VENDOR_ID_NETCOPE 0x1b26
#define PCI_VENDOR_ID_SILICOM 0x1c2c
+#define PCI_VENDOR_ID_CESNET 0x18ec
/* PCI Device IDs */
#define PCI_DEVICE_ID_NFB_40G2 0xcb80
#define PCI_DEVICE_ID_NFB_100G2 0xc2c1
#define PCI_DEVICE_ID_NFB_200G2QL 0xc250
+#define PCI_DEVICE_ID_NFB_200G2QL_E1 0xc251
#define PCI_DEVICE_ID_FB2CGG3 0x00d0
+#define PCI_DEVICE_ID_FB2CGHH 0x00d2
#define PCI_DEVICE_ID_FB2CGG3D 0xc240
+#define PCI_DEVICE_ID_COMBO400G1 0xc400
+#define PCI_DEVICE_ID_CESNET_NDK_COMMON 0xc000
/* Max index of ndp rx/tx queues */
#define RTE_ETH_NDP_MAX_RX_QUEUES 32
diff --git a/drivers/net/nfb/nfb_ethdev.c b/drivers/net/nfb/nfb_ethdev.c
index 2c3c52d7ad..56bbe75a28 100644
--- a/drivers/net/nfb/nfb_ethdev.c
+++ b/drivers/net/nfb/nfb_ethdev.c
@@ -795,8 +795,12 @@ static const struct rte_pci_id nfb_pci_id_table[] = {
{ RTE_PCI_DEVICE(PCI_VENDOR_ID_NETCOPE, PCI_DEVICE_ID_NFB_40G2) },
{ RTE_PCI_DEVICE(PCI_VENDOR_ID_NETCOPE, PCI_DEVICE_ID_NFB_100G2) },
{ RTE_PCI_DEVICE(PCI_VENDOR_ID_NETCOPE, PCI_DEVICE_ID_NFB_200G2QL) },
+ { RTE_PCI_DEVICE(PCI_VENDOR_ID_NETCOPE, PCI_DEVICE_ID_NFB_200G2QL_E1) },
{ RTE_PCI_DEVICE(PCI_VENDOR_ID_SILICOM, PCI_DEVICE_ID_FB2CGG3) },
+ { RTE_PCI_DEVICE(PCI_VENDOR_ID_SILICOM, PCI_DEVICE_ID_FB2CGHH) },
{ RTE_PCI_DEVICE(PCI_VENDOR_ID_SILICOM, PCI_DEVICE_ID_FB2CGG3D) },
+ { RTE_PCI_DEVICE(PCI_VENDOR_ID_CESNET, PCI_DEVICE_ID_COMBO400G1) },
+ { RTE_PCI_DEVICE(PCI_VENDOR_ID_CESNET, PCI_DEVICE_ID_CESNET_NDK_COMMON) },
{ .vendor_id = 0, }
};
--
2.53.0
^ permalink raw reply related [flat|nested] 131+ messages in thread
* [PATCH v7 7/8] net/nfb: report firmware version
2026-02-04 12:31 ` [PATCH v7 0/8] net/nfb: rework to real multiport spinler
` (5 preceding siblings ...)
2026-02-04 12:31 ` [PATCH v7 6/8] net/nfb: add compatible cards to driver PCI ID table spinler
@ 2026-02-04 12:31 ` spinler
2026-02-04 12:31 ` [PATCH v7 8/8] doc/nfb: cleanup and update guide spinler
` (2 subsequent siblings)
9 siblings, 0 replies; 131+ messages in thread
From: spinler @ 2026-02-04 12:31 UTC (permalink / raw)
To: dev; +Cc: Martin Spinler
From: Martin Spinler <spinler@cesnet.cz>
Report the firmware version, which is described
by project name and project version.
Signed-off-by: Martin Spinler <spinler@cesnet.cz>
---
drivers/net/nfb/nfb_ethdev.c | 27 +++++++++++++++++++++++++++
1 file changed, 27 insertions(+)
diff --git a/drivers/net/nfb/nfb_ethdev.c b/drivers/net/nfb/nfb_ethdev.c
index 56bbe75a28..157f04a891 100644
--- a/drivers/net/nfb/nfb_ethdev.c
+++ b/drivers/net/nfb/nfb_ethdev.c
@@ -488,6 +488,32 @@ nfb_eth_mac_addr_remove(struct rte_eth_dev *dev, uint32_t index)
nc_rxmac_set_mac(internals->rxmac[i], index, 0, 0);
}
+static int
+nfb_eth_fw_version_get(struct rte_eth_dev *dev, char *fw_version,
+ size_t fw_size)
+{
+ int ret;
+ const char *proj_name, *proj_vers;
+ struct pmd_internals *intl = dev->process_private;
+
+ proj_name = nc_info_get_fw_project_name(intl->nfb, NULL);
+ proj_vers = nc_info_get_fw_project_version(intl->nfb, NULL);
+
+ if (proj_name == NULL)
+ proj_name = "";
+ if (proj_vers == NULL)
+ proj_vers = "";
+
+ ret = snprintf(fw_version, fw_size, "%s;%s", proj_name, proj_vers);
+ if (ret < 0)
+ return ret;
+
+ if (ret >= (signed int)fw_size)
+ return strlen(proj_name) + 1 + strlen(proj_vers) + 1;
+
+ return 0;
+}
+
static const struct eth_dev_ops ops = {
.dev_start = nfb_eth_dev_start,
.dev_stop = nfb_eth_dev_stop,
@@ -514,6 +540,7 @@ static const struct eth_dev_ops ops = {
.mac_addr_set = nfb_eth_mac_addr_set,
.mac_addr_add = nfb_eth_mac_addr_add,
.mac_addr_remove = nfb_eth_mac_addr_remove,
+ .fw_version_get = nfb_eth_fw_version_get,
};
/**
--
2.53.0
^ permalink raw reply related [flat|nested] 131+ messages in thread
* [PATCH v7 8/8] doc/nfb: cleanup and update guide
2026-02-04 12:31 ` [PATCH v7 0/8] net/nfb: rework to real multiport spinler
` (6 preceding siblings ...)
2026-02-04 12:31 ` [PATCH v7 7/8] net/nfb: report firmware version spinler
@ 2026-02-04 12:31 ` spinler
2026-02-10 0:35 ` [PATCH v7 0/8] net/nfb: rework to real multiport Stephen Hemminger
2026-02-12 18:35 ` Stephen Hemminger
9 siblings, 0 replies; 131+ messages in thread
From: spinler @ 2026-02-04 12:31 UTC (permalink / raw)
To: dev; +Cc: Martin Spinler
From: Martin Spinler <spinler@cesnet.cz>
Clean up documentation and update the guide with:
- Current card list with firmware download links
- Updated port argument description
Signed-off-by: Martin Spinler <spinler@cesnet.cz>
---
doc/guides/nics/features/nfb.ini | 4 +
doc/guides/nics/nfb.rst | 187 +++++++++++--------------
doc/guides/rel_notes/release_26_03.rst | 6 +
3 files changed, 95 insertions(+), 102 deletions(-)
diff --git a/doc/guides/nics/features/nfb.ini b/doc/guides/nics/features/nfb.ini
index c77b592789..7ac89fc15f 100644
--- a/doc/guides/nics/features/nfb.ini
+++ b/doc/guides/nics/features/nfb.ini
@@ -9,9 +9,13 @@ Link status = Y
Queue start/stop = Y
Promiscuous mode = Y
Allmulticast mode = Y
+Unicast MAC filter = Y
+Timestamp offload = P
Basic stats = Y
Extended stats = Y
Stats per queue = Y
+FW version = Y
+Multiprocess aware = Y
Linux = Y
x86-64 = Y
Usage doc = Y
diff --git a/doc/guides/nics/nfb.rst b/doc/guides/nics/nfb.rst
index a9b4049654..55a635d8d4 100644
--- a/doc/guides/nics/nfb.rst
+++ b/doc/guides/nics/nfb.rst
@@ -2,64 +2,101 @@
Copyright 2019 Cesnet
Copyright 2019 Netcope Technologies
-NFB poll mode driver library
-=================================
+NFB Poll Mode Driver
+====================
+
+The NFB PMD implements support for the FPGA-based
+programmable NICs running `CESNET-NDK <https://www.liberouter.org/ndk/>`_
+based firmware (formerly known as the NetCOPE platform). The CESNET Network Development Kit offers
+wide spectrum of supported cards, for example:
+N6010, FB2CGG3 (Silicom Denmark),
+IA-420F, IA-440i (BittWare),
+AGI-FH400G (ReflexCES),
+and `many more <https://github.com/CESNET/ndk-fpga/tree/devel/cards>`_.
+
+The CESNET-NDK framework is open source and
+can be found on `CESNET-NDK <https://github.com/CESNET/ndk-fpga>`_.
+Ready-to-use demo firmwares can be found on the `DYNANIC page <https://dyna-nic.com/try-now/>`_.
+
+Software compatibility and firmware for
+`historical cards <https://www.liberouter.org/technologies/cards/>`_
+are left unmaintained.
+
+Software prerequisites
+----------------------
-The NFB poll mode driver library implements support for the Netcope
-FPGA Boards (**NFB-40G2, NFB-100G2, NFB-200G2QL**) and Silicom **FB2CGG3** card,
-FPGA-based programmable NICs. The NFB PMD uses interface provided by the libnfb
-library to communicate with these cards over the nfb layer.
+This PMD requires a Linux kernel module, which is responsible for initialization and
+allocation of resources needed for the nfb layer function.
+Communication between PMD and kernel modules is mediated by the libnfb library.
+The kernel module and library are not part of DPDK and must be installed
+separately. Dependencies can be found on GitHub:
+`nfb-framework <https://github.com/CESNET/ndk-sw>`_ as source code,
+or for RPM-based distributions, the prebuilt `nfb-framework` package on
+`Fedora Copr <https://copr.fedorainfracloud.org/coprs/g/CESNET/nfb-framework/>`_.
-More information about the
-`NFB cards <https://www.liberouter.org/technologies/cards/>`_
-and used technology
-(`Network Development Kit <https://www.liberouter.org/ndk/>`_)
-can be found on the `Liberouter website <http://www.liberouter.org/>`_.
+Before starting the DPDK, make sure that the kernel module is loaded (`sudo modprobe nfb`)
+and the card is running the CESNET-NDK based firmware (`nfb-info -l`).
.. note::
- Currently the driver is supported only on x86_64 architectures.
- Only x86_64 versions of the external libraries are provided.
+ Currently, the driver is supported only on x86_64 architectures.
+
+NFB card architecture
+---------------------
+
+Ethernet Ports
+~~~~~~~~~~~~~~
+
+The NFB cards are multi-port multi-queue cards, where (generally) data from any
+Ethernet port may be sent by the firmware to any queue.
-Prerequisites
--------------
+The cards were historically represented in DPDK as a single port.
+Currently each Ethernet channel is represented as one DPDK port.
-This PMD requires kernel modules which are responsible for initialization and
-allocation of resources needed for nfb layer function.
-Communication between PMD and kernel modules is mediated by libnfb library.
-These kernel modules and library are not part of DPDK and must be installed
-separately:
+.. note::
-* **libnfb library**
+ Normally, one port corresponds to one channel, but ports can often be configured in a separate
+ manner. For example one 100G port can be used as 4x25G or 4x10G independent Ethernet channels.
- The library provides API for initialization of nfb transfers, receiving and
- transmitting data segments.
+By default, all ports are initialized and used for the allowed PCI device.
+When this behaviour is limiting (e.g., for multiple instances of DPDK app on different ports
+of the same PCI device), ports can be specified by the `port` item in the `allow` argument:
-* **Kernel modules**
+.. code-block:: console
- * nfb
+ -a 0000:01:00.0,port=0,port=3
- Kernel modules manage initialization of hardware, allocation and
- sharing of resources for user space applications.
+PCIe slots
+~~~~~~~~~~
-Dependencies can be found here:
-`Netcope common <https://github.com/CESNET/ndk-sw>`_.
+Some cards employ more than one PCIe device for better data throughput. This can be achieved by
+slot bifurcation (only a minor improvement) or by an add-on cable connected to another PCIe slot.
+Both improvements can work together, as is, for example, in the case of the AGI-FH400G card.
-Versions of the packages
-~~~~~~~~~~~~~~~~~~~~~~~~
+Because primary and secondary slot(s) can be attached to different NUMA nodes
+(also applies for bifurcation on some HW), the data structures need to be correctly allocated.
+(Device-aware allocation matters also on IOMMU-enabled systems.)
+The firmware already provides DMA queue to PCI device mapping. The DPDK application just needs to
+use all PCI devices, otherwise some queues will not be available; provide all PCI endpoints
+listed in the `nfb-info -v` in the `allow` argument.
-The minimum version of the provided packages:
+.. note::
-* for DPDK from 19.05
+ For cards where the number of Ethernet ports is less than the number of PCI devices
+ (e.g., AGI-FH400G: 1 port, up to 4 PCI devices), the virtual DPDK ports are
+ created to achieve the best NUMA-aware throughput
+ (virtual ports lack a lot of configuration features).
-Configuration
--------------
+Features
+--------
Timestamps
+~~~~~~~~~~
-The PMD supports hardware timestamps of frame receipt on physical network interface. In order to use
-the timestamps, the hardware timestamping unit must be enabled (follow the documentation of the NFB
-products). The standard `RTE_ETH_RX_OFFLOAD_TIMESTAMP` flag can be used for this feature.
+The PMD supports hardware timestamps of frame receipt on physical network interface.
+In order to use the timestamps, the hardware timestamping unit must be enabled
+(follow the documentation of the NFB products).
+The standard `RTE_ETH_RX_OFFLOAD_TIMESTAMP` flag can be used for this feature.
When the timestamps are enabled, a timestamp validity flag is set in the MBUFs
containing received frames and timestamp is inserted into the `rte_mbuf` struct.
@@ -67,72 +104,18 @@ containing received frames and timestamp is inserted into the `rte_mbuf` struct.
The timestamp is an `uint64_t` field and holds the number of nanoseconds
elapsed since 1.1.1970 00:00:00 UTC.
+Simulation
+~~~~~~~~~~
-Using the NFB PMD
-----------------------
-
-Kernel modules have to be loaded before running the DPDK application.
-
-NFB card architecture
----------------------
-
-The NFB cards are multi-port multi-queue cards, where (generally) data from any
-Ethernet port may be sent to any queue.
-They are represented in DPDK as a single port.
-
-NFB-200G2QL card employs an add-on cable which allows to connect it to two
-physical PCI-E slots at the same time (see the diagram below).
-This is done to allow 200 Gbps of traffic to be transferred through the PCI-E
-bus (note that a single PCI-E 3.0 x16 slot provides only 125 Gbps theoretical
-throughput).
-
-Although each slot may be connected to a different CPU and therefore to a different
-NUMA node, the card is represented as a single port in DPDK. To work with data
-from the individual queues on the right NUMA node, connection of NUMA nodes on
-first and last queue (each NUMA node has half of the queues) need to be checked.
-
-Limitations
------------
-
-Driver is usable only on Linux architecture, namely on CentOS.
-
-Since a card is always represented as a single port, but can be connected to two
-NUMA nodes, there is need for manual check where master/slave is connected.
-
-Example of usage
-----------------
-
-Read packets from 0. and 1. receive queue and write them to 0. and 1.
-transmit queue:
+The CESNET-NDK framework offers the possibility of simulating the firmware together with DPDK.
+This allows for easy debugging of a packet flow behaviour with a specific firmware configuration.
+The DPDK NFB driver can be connected to the simulator (Questa/ModelSim/nvc) via a virtual device:
.. code-block:: console
- ./<build_dir>/app/dpdk-testpmd -l 0-3 -n 2 \
- -- --port-topology=chained --rxq=2 --txq=2 --nb-cores=2 -i -a
-
-Example output:
-
-.. code-block:: console
+ dpdk-testpmd
+ --vdev=eth_vdev_nfb,dev=libnfb-ext-grpc.so:grpc+dma_vas:localhost:50051,queue_driver=native
+ --iova-mode=va -- -i
- [...]
- EAL: PCI device 0000:06:00.0 on NUMA socket -1
- EAL: probe driver: 1b26:c1c1 net_nfb
- PMD: Initializing NFB device (0000:06:00.0)
- PMD: Available DMA queues RX: 8 TX: 8
- PMD: NFB device (0000:06:00.0) successfully initialized
- Interactive-mode selected
- Auto-start selected
- Configuring Port 0 (socket 0)
- Port 0: 00:11:17:00:00:00
- Checking link statuses...
- Port 0 Link Up - speed 10000 Mbps - full-duplex
- Done
- Start automatic packet forwarding
- io packet forwarding - CRC stripping disabled - packets/burst=32
- nb forwarding cores=2 - nb forwarding ports=1
- RX queues=2 - RX desc=128 - RX free threshold=0
- RX threshold registers: pthresh=0 hthresh=0 wthresh=0
- TX queues=2 - TX desc=512 - TX free threshold=0
- TX threshold registers: pthresh=0 hthresh=0 wthresh=0
- TX RS bit threshold=0 - TXQ flags=0x0
- testpmd>
+More info about the simulation can be found int the CESNET-NDK `documentation
+<https://cesnet.github.io/ndk-fpga/devel/ndk_apps/minimal/tests/cocotb/readme.html>`_.
diff --git a/doc/guides/rel_notes/release_26_03.rst b/doc/guides/rel_notes/release_26_03.rst
index ff02c294d0..7678cf1a49 100644
--- a/doc/guides/rel_notes/release_26_03.rst
+++ b/doc/guides/rel_notes/release_26_03.rst
@@ -62,6 +62,12 @@ New Features
* **Updated CESNET NFB ethernet driver.**
* The timestamp value has been updated to make it usable.
+ * The DPDK port now represents just one Ethernet port instead of all Ethernet ports on the NIC.
+ * All ports are used by default, but a subset can be selected using the ``port`` argument.
+ * Report firmware version.
+ * Common CESNET-NDK-based adapters have been added,
+ including the FB2CGHH (Silicom Denmark) and XpressSX AGI-FH400G (Reflex CES).
+
Removed Items
-------------
--
2.53.0
^ permalink raw reply related [flat|nested] 131+ messages in thread
* Re: [PATCH v7 0/8] net/nfb: rework to real multiport
2026-02-04 12:31 ` [PATCH v7 0/8] net/nfb: rework to real multiport spinler
` (7 preceding siblings ...)
2026-02-04 12:31 ` [PATCH v7 8/8] doc/nfb: cleanup and update guide spinler
@ 2026-02-10 0:35 ` Stephen Hemminger
2026-02-13 18:53 ` Martin Špinler
2026-02-12 18:35 ` Stephen Hemminger
9 siblings, 1 reply; 131+ messages in thread
From: Stephen Hemminger @ 2026-02-10 0:35 UTC (permalink / raw)
To: spinler; +Cc: dev
[-- Attachment #1: Type: text/plain, Size: 1271 bytes --]
On Wed, 4 Feb 2026 13:31:29 +0100
spinler@cesnet.cz wrote:
> From: Martin Spinler <spinler@cesnet.cz>
>
> This series implements real multiport for better user experience.
>
> The existing driver creates one ethdev/port for one PCI device.
> As the CESNET-NDK based cards aren't capable to represent each
> Ethernet port by own PCI device, new driver implementation
> processes real port configuration from firmware/card and switches
> from rte_eth_dev_pci_generic_probe to multiple rte_eth_dev_create calls.
>
> ---
Patch 1/8 (Queue Mapping):
Missing NULL checks before accessing queue_map_rx/tx arrays (could crash in secondary process)
Missing bounds checks on queue indices (buffer overflow risk)
Patch 2/8 (Core Refactoring):
Resource leak: nfb_dev not closed on error path
NULL pointer dereference: no check after rte_eth_dev_get_by_name()
Array overflow in queue mapping initialization
Partial device cleanup missing on errors
Patch 4/8 (Port Argument):
Missing errno check after strtoul() conversion
Quick Summary: The architectural refactoring is well-designed but needs defensive programming additions. Most issues are missing validation checks that could cause crashes or leaks on error paths.
Detailed review attached.
[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: bundle-1748-review.md --]
[-- Type: text/markdown, Size: 10676 bytes --]
# NFB Driver Review - Bundle 1748 (Multiport Architecture)
## Series v7: 8 patches refactoring driver for one ethdev per physical port
---
## PATCH v7 1/8: net/nfb: prepare for indirect queue mapping scheme
**Subject**: net/nfb: prepare for indirect queue mapping scheme (57 chars) ✓
### Summary
Adds `queue_map_rx` and `queue_map_tx` arrays to `struct pmd_priv` to enable indirect mapping between DPDK queue indices and firmware queue IDs. Allocates single buffer for both arrays, initializes with 1:1 mapping.
### Errors
**Error 1: Missing NULL check before array access**
```c
qid = priv->queue_map_rx[rx_queue_id];
```
Location: `nfb_eth_rx_queue_setup()` in `nfb_rx.c`
If `priv->queue_map_rx` is NULL (allocation failed in primary process, or not yet initialized in secondary process), this dereferences NULL causing segfault.
**Suggested fix**: Add NULL check at start of function:
```c
if (priv->queue_map_rx == NULL) {
NFB_LOG(ERR, "Queue mapping not initialized");
return -EINVAL;
}
qid = priv->queue_map_rx[rx_queue_id];
```
Same issue exists in `nfb_eth_tx_queue_setup()` in `nfb_tx.c`:
```c
qid = priv->queue_map_tx[tx_queue_id];
```
**Error 2: Missing bounds check on queue index**
```c
qid = priv->queue_map_rx[rx_queue_id];
```
Location: `nfb_eth_rx_queue_setup()` in `nfb_rx.c`
If `rx_queue_id >= priv->max_rx_queues`, this accesses beyond allocated array bounds. The array is allocated with size `max_rx_queues + max_tx_queues`, and TX mapping starts at offset `max_rx_queues`.
**Suggested fix**: Add bounds check:
```c
if (rx_queue_id >= priv->max_rx_queues) {
NFB_LOG(ERR, "RX queue index %u exceeds max %u",
rx_queue_id, priv->max_rx_queues);
return -EINVAL;
}
```
Same for TX queues in `nfb_tx.c`.
---
## PATCH v7 2/8: net/nfb: create one ethdev per ethernet port
**Subject**: net/nfb: create one ethdev per ethernet port (52 chars) ✓
### Summary
Major architectural refactoring: creates one `rte_eth_dev` per physical Ethernet port instead of one per PCI device. Uses libnfb's `nc_ifc_map_info` to discover port-to-queue mappings. Switches from single `rte_eth_dev_pci_generic_probe()` to loop calling `rte_eth_dev_create()` for each port.
### Errors
**Error 1: Resource leak - nfb_dev not closed on error**
```c
nfb_dev = nfb_open(params->path);
if (nfb_dev == NULL) {
NFB_LOG(ERR, "nfb_open(): failed to open %s", params->path);
return -EINVAL;
}
ret = nc_ifc_map_info_create_ordinary(nfb_dev, &ifc_params.map_info);
if (ret)
goto err_map_info_create;
```
Location: `nfb_eth_common_probe()` in `nfb_ethdev.c`
The label `err_map_info_create` does not close `nfb_dev`:
```c
err_map_info_create:
nfb_close(nfb_dev); // <-- MISSING
return ret;
```
**Suggested fix**: Add cleanup:
```c
err_map_info_create:
nfb_close(nfb_dev);
return ret;
```
**Error 2: Missing NULL check after rte_eth_dev_get_by_name()**
```c
ret = rte_eth_dev_create(pp->device, pp->name, ...);
if (ret)
goto out;
eth_dev = rte_eth_dev_get_by_name(pp->name);
p = eth_dev->process_private; // <-- NULL dereference if get_by_name() fails
```
Location: `nfb_eth_dev_create_for_ifc()` in `nfb_ethdev.c`
If `rte_eth_dev_get_by_name()` returns NULL (device creation succeeded but lookup failed), dereferencing `eth_dev` causes crash.
**Suggested fix**: Add NULL check:
```c
eth_dev = rte_eth_dev_get_by_name(pp->name);
if (eth_dev == NULL) {
NFB_LOG(ERR, "Failed to get created device %s", pp->name);
ret = -ENODEV;
goto out;
}
```
**Error 3: Array overflow in queue mapping - no bounds check**
```c
cnt = 0;
for (i = 0; i < mi->rxq_cnt; i++) {
if (mi->rxq[i].ifc == ifc->id)
priv->queue_map_rx[cnt++] = mi->rxq[i].id; // <-- no bounds check on cnt
}
```
Location: `nfb_eth_dev_init()` in `nfb_ethdev.c`
If firmware mapping data is inconsistent (more queues mapped to this interface than `ifc->rxq_cnt` claims), `cnt` exceeds `max_rx_queues` and writes beyond allocated array.
**Suggested fix**: Add bounds check:
```c
if (cnt >= max_rx_queues) {
NFB_LOG(ERR, "RX queue count exceeds maximum %u", max_rx_queues);
ret = -EINVAL;
goto err_alloc_queue_map;
}
priv->queue_map_rx[cnt++] = mi->rxq[i].id;
```
Apply same fix for TX queue loop.
**Error 4: Potential buffer overflow in snprintf**
```c
ret = snprintf(pp->name + cp->basename_len, sizeof(pp->name) - cp->basename_len,
"_eth%d", ifc->id);
if (ret < 0 || ret >= (signed int)sizeof(pp->name) - cp->basename_len)
return -EINVAL;
```
Location: `nfb_eth_dev_create_for_ifc()` in `nfb_ethdev.c`
While there's a check after `snprintf`, if it triggers and returns -EINVAL, any ethdevs created in previous loop iterations are not cleaned up. This leaves partially initialized devices.
**Suggested fix**: Document that cleanup happens at caller level via `nfb_eth_common_remove()`, or add cleanup loop before returning error.
---
## PATCH v7 3/8: net/nfb: add vdev as alternative device probe method
**Subject**: net/nfb: add vdev as alternative device probe method (55 chars) ✓
### Summary
Adds virtual device (vdev) probe support alongside PCI. Allows specifying NFB device path via `dev=<path>` argument. Parses vdev arguments and calls common probe function.
### Status: ✓ No errors found
Error handling is correct:
- `strdup()` allocation failure handled properly
- `kvargs` parsing failure handled
- All error paths properly free allocated resources
- `snprintf()` truncation checked and handled
---
## PATCH v7 4/8: net/nfb: add device argument "port" to limit used ports
**Subject**: net/nfb: add device argument "port" to limit used (52 chars + continuation) ✓
### Summary
Adds `port=<number>` device argument to create ethdevs only for specified ports instead of all available ports. Argument can be repeated multiple times. Adds `rte_kvargs` parsing infrastructure.
### Errors
**Error 1: Integer overflow in port validation**
```c
port = strtoul(value, &end, 0);
if (*end != '\0' || port >= (unsigned long)ifc_params->map_info.ifc_cnt)
return -EINVAL;
ifc_params->ifc_info = &ifc_params->map_info.ifc[port];
```
Location: `nfb_eth_dev_create_for_ifc_by_port()` in `nfb_ethdev.c`
The cast `(unsigned long)ifc_params->map_info.ifc_cnt` could overflow if `ifc_cnt` is `INT_MAX` and cast to unsigned long. However, in practice `ifc_cnt` is small (number of interfaces), so this is extremely unlikely.
More concerning: if `strtoul()` returns `ULONG_MAX` on error (and sets errno), the comparison might pass incorrectly. Should check errno.
**Suggested fix**: Check for strtoul errors:
```c
errno = 0;
port = strtoul(value, &end, 0);
if (errno != 0 || *end != '\0' || port >= (unsigned long)ifc_params->map_info.ifc_cnt)
return -EINVAL;
```
### Warnings
**Warning 1: kvargs leaked on some error paths**
The main probe function allocates kvargs but may not free it on all error paths. Need to verify `err_dev_create` path includes `rte_kvargs_free(kvlist)`.
Looking at the code:
```c
err_dev_create:
nfb_eth_common_remove(params->device);
rte_kvargs_free(kvlist); // <-- present, good
err_parse_args:
```
This is correct.
---
## PATCH v7 5/8: net/nfb: init only MACs associated with device
**Subject**: net/nfb: init only MACs associated with device (50 chars) ✓
### Summary
Changes MAC initialization to only open MACs associated with specific interface instead of all MACs in system. Filters MACs by checking `mi->eth[i].ifc == ifc->id`.
### Status: Need to review full patch
Cannot review without seeing full diff. Key areas to check:
- Array bounds when filtering MACs
- NULL checks after nc_rxmac_open/nc_txmac_open
- Error path cleanup for partially opened MACs
---
## PATCH v7 6/8: net/nfb: add compatible cards to driver PCI ID table
**Subject**: net/nfb: add compatible cards to driver PCI ID table (59 chars) ✓
### Status: ✓ Likely no errors (PCI ID table additions)
Patch should be simple PCI ID additions to the device table. Low risk for bugs.
---
## PATCH v7 7/8: net/nfb: report firmware version
**Subject**: net/nfb: report firmware version (38 chars) ✓
### Status: Need to review implementation
Should check:
- Buffer overflow in version string formatting
- NULL checks on firmware version string
- Proper error handling if version retrieval fails
---
## PATCH v7 8/8: doc/nfb: cleanup and update guide
**Subject**: doc/nfb: cleanup and update guide (37 chars) ✓
### Status: ✓ Not reviewed (documentation)
Documentation update - should verify it accurately describes the new multi-port architecture.
---
## Summary - Bundle 1748
### Critical Issues: 7
**Patch 1/8:**
1. Missing NULL check before accessing queue_map arrays (2 locations: RX and TX)
2. Missing bounds check on queue indices (2 locations: RX and TX)
**Patch 2/8:**
3. Resource leak - nfb_dev not closed on error path
4. Missing NULL check after rte_eth_dev_get_by_name()
5. Array overflow in queue mapping loop (no bounds check on cnt variable)
6. Partial device cleanup missing on probe error
**Patch 4/8:**
7. Missing errno check after strtoul() for proper error detection
### Medium Priority: 1
**Patch 2/8:**
- Missing documentation for new internal APIs
### All Patches: Format ✓
- Proper Signed-off-by tags ✓
- Subject lines within 60 chars ✓
- Commit message format correct ✓
---
## Recommended Actions
### Must Fix - Patch 1/8:
Add NULL and bounds checks at start of queue setup functions:
```c
if (priv->queue_map_rx == NULL) {
NFB_LOG(ERR, "Queue mapping not initialized");
return -EINVAL;
}
if (rx_queue_id >= priv->max_rx_queues) {
NFB_LOG(ERR, "RX queue index %u exceeds max %u",
rx_queue_id, priv->max_rx_queues);
return -EINVAL;
}
```
### Must Fix - Patch 2/8:
1. Close nfb_dev on err_map_info_create path
2. Check NULL after rte_eth_dev_get_by_name()
3. Add bounds check in queue mapping loops before `cnt++`
4. Document or implement cleanup for partial probe failures
### Must Fix - Patch 4/8:
Check errno after strtoul() to catch conversion errors properly
### Architecture Review Needed:
This is a major architectural change. Beyond code correctness, recommend:
- Performance testing with multi-port configurations
- Multi-process testing (primary/secondary)
- Backward compatibility verification
- Documentation review for completeness
^ permalink raw reply [flat|nested] 131+ messages in thread
* Re: [PATCH v7 0/8] net/nfb: rework to real multiport
2026-02-04 12:31 ` [PATCH v7 0/8] net/nfb: rework to real multiport spinler
` (8 preceding siblings ...)
2026-02-10 0:35 ` [PATCH v7 0/8] net/nfb: rework to real multiport Stephen Hemminger
@ 2026-02-12 18:35 ` Stephen Hemminger
2026-02-13 18:53 ` Martin Špinler
9 siblings, 1 reply; 131+ messages in thread
From: Stephen Hemminger @ 2026-02-12 18:35 UTC (permalink / raw)
To: spinler; +Cc: dev
On Wed, 4 Feb 2026 13:31:29 +0100
spinler@cesnet.cz wrote:
> From: Martin Spinler <spinler@cesnet.cz>
>
> This series implements real multiport for better user experience.
>
> The existing driver creates one ethdev/port for one PCI device.
> As the CESNET-NDK based cards aren't capable to represent each
> Ethernet port by own PCI device, new driver implementation
> processes real port configuration from firmware/card and switches
> from rte_eth_dev_pci_generic_probe to multiple rte_eth_dev_create calls.
>
> ---
> Depends-on: patch-37245 ("doc/nfb: update release notes for nfb driver")
>
> v7:
> * Rebased to patch-37245
> * Removed timestamp update info from release notes
> * Added missing ctype include for isdigit()
> * Updated style: removed extra brackets
>
> Martin Spinler (8):
> net/nfb: prepare for indirect queue mapping scheme
> net/nfb: create one ethdev per ethernet port
> net/nfb: add vdev as alternative device probe method
> net/nfb: add device argument "port" to limit used ports
> net/nfb: init only MACs associated with device
> net/nfb: add compatible cards to driver PCI ID table
> net/nfb: report firmware version
> doc/nfb: cleanup and update guide
>
> doc/guides/nics/features/nfb.ini | 4 +
> doc/guides/nics/nfb.rst | 187 +++++------
> doc/guides/rel_notes/release_26_03.rst | 6 +
> drivers/net/nfb/meson.build | 1 +
> drivers/net/nfb/nfb.h | 56 +++-
> drivers/net/nfb/nfb_ethdev.c | 445 +++++++++++++++++++------
> drivers/net/nfb/nfb_rx.c | 30 +-
> drivers/net/nfb/nfb_rx.h | 9 +-
> drivers/net/nfb/nfb_tx.c | 27 +-
> drivers/net/nfb/nfb_tx.h | 7 +-
> drivers/net/nfb/nfb_vdev.c | 107 ++++++
> 11 files changed, 622 insertions(+), 257 deletions(-)
> create mode 100644 drivers/net/nfb/nfb_vdev.c
>
Improved the AI review process and it saw this.
I think you should fix and retest before merge to next-net.
Here's the review. The most significant finding is in patch 2/8:
Double TAILQ_REMOVE — nfb_eth_common_remove() explicitly removes the
entry from the list, then calls rte_eth_dev_destroy() which invokes
nfb_eth_dev_uninit(), which also does TAILQ_REMOVE on the same entry.
This will corrupt the linked list. The fix is to remove the
TAILQ_REMOVE from nfb_eth_common_remove() and let uninit handle it,
since uninit is also called from the dev_close path.
Other notable findings include unbounded loop counters when populating
queue maps and MAC arrays (patches 2 and 5 — trusting firmware
consistency without defensive bounds checks), strtoul with base 0
allowing accidental octal parsing of port numbers (patch 4), and a
"DYNANIC" typo in the documentation (patch 8).
The overall architecture of the series is solid — the indirect queue
mapping, per-interface ethdev creation, and TAILQ-based device tracking
are well-designed patterns for multi-port NICs.
Full report is:
# Deep Dive Review: NFB Multi-Port Patch Series (v7, 8 patches)
**Series**: `[PATCH v7 1/8]` through `[PATCH v7 8/8]`
**Author**: Martin Spinler `<spinler@cesnet.cz>`
**Delegate**: Stephen Hemminger
---
## Series Overview
This patch series transforms the NFB PMD from a "one ethdev per PCI device" model to "one ethdev per physical Ethernet port." This is a significant architectural change requiring:
1. An indirect queue mapping layer (DPDK queue index → firmware queue ID)
2. Multiple `rte_eth_dev_create()` calls per PCI probe instead of `rte_eth_dev_pci_generic_probe()`
3. A TAILQ-based device list to track all created ethdevs for proper removal
4. A vdev probe path for non-PCI (simulated/virtual) devices
5. Per-port MAC initialization instead of global
6. New PCI IDs and documentation updates
The design is generally sound — the libnfb framework's `nc_ifc_map_info` provides the firmware-side port/queue topology, and this series maps it cleanly into DPDK's model.
---
## Patch-by-Patch Analysis
### Patch 1/8: `net/nfb: prepare for indirect queue mapping scheme`
**What it does**: Adds `queue_map_rx` / `queue_map_tx` arrays to `struct pmd_priv`, allocated as a single `rte_calloc` block. The RX/TX queue setup functions now look up the firmware queue ID via these maps instead of using the DPDK queue index directly.
**Correctness findings**:
**Error: Potential out-of-bounds access on `queue_map_rx` / `queue_map_tx`**
In `nfb_eth_rx_queue_setup()`:
```c
qid = priv->queue_map_rx[rx_queue_id];
```
There is no bounds check that `rx_queue_id < max_rx_queues` before indexing into the map array. The same applies to `nfb_eth_tx_queue_setup()` with `queue_map_tx[tx_queue_id]`. While ethdev may enforce queue count limits configured via `dev_info`, a defensive check would prevent corruption if the upper layer has a bug.
**Confidence**: ~60%. Ethdev's `rte_eth_rx_queue_setup` does validate `rx_queue_id < dev->data->nb_rx_queues`, but `nb_rx_queues` is set by `rte_eth_dev_configure()` which should match `max_rx_queues`. If a mismatch ever occurs (e.g., bug in configure path), this would silently corrupt memory.
**Info: Clean refactoring of error paths**
The conversion from:
```c
if (ret == 0)
dev->data->rx_queues[rx_queue_id] = rxq;
else
rte_free(rxq);
```
to the `goto err_queue_init` pattern is cleaner and more consistent with DPDK conventions.
---
### Patch 2/8: `net/nfb: create one ethdev per ethernet port`
This is the core architectural patch. Key changes:
- `nfb_eth_dev_init()` signature changes to accept `void *init_data` (for `rte_eth_dev_create()`)
- `nfb_eth_common_probe()` opens the NFB device, gets interface map, iterates interfaces
- TAILQ list tracks all created ethdevs for removal
- `nfb_eth_common_remove()` iterates the TAILQ to destroy matching ethdevs
**Correctness findings**:
**Error: Double TAILQ_REMOVE in `nfb_eth_common_remove()`**
```c
RTE_TAILQ_FOREACH_SAFE(entry, &nfb_eth_dev_list, eth_dev_list, temp) {
if (dev == entry->eth_dev->device) {
TAILQ_REMOVE(&nfb_eth_dev_list, entry, eth_dev_list);
rte_eth_dev_destroy(entry->eth_dev, nfb_eth_dev_uninit);
}
}
```
`rte_eth_dev_destroy()` calls `nfb_eth_dev_uninit()`, which also does:
```c
TAILQ_REMOVE(&nfb_eth_dev_list, internals, eth_dev_list);
```
This is a **double TAILQ_REMOVE** on the same entry. The first `TAILQ_REMOVE` in `nfb_eth_common_remove()` unlinks the entry, then `nfb_eth_dev_uninit()` attempts to remove the already-unlinked entry again. Depending on the TAILQ implementation, this could corrupt the list or cause undefined behavior.
**Confidence**: ~85%. The `nfb_eth_dev_uninit()` always does the TAILQ_REMOVE (it was added in this same patch), and `nfb_eth_common_remove()` also does it before calling destroy. One of these must be removed. The cleanest fix is to remove the TAILQ_REMOVE from `nfb_eth_common_remove()` and let `nfb_eth_dev_uninit()` handle it exclusively — that way `dev_close` → `uninit` also works correctly.
**Error: `nfb_eth_common_probe()` opens NFB device twice**
`nfb_eth_common_probe()` opens the NFB device with `nfb_open()` to get the interface map, then closes it. But then `nfb_eth_dev_init()` (called via `rte_eth_dev_create()`) opens the **same device again** per-interface:
```c
internals->nfb = nfb_open(params->probe_params->path);
```
Each ethdev gets its own `nfb_open()` handle, which is probably intentional (each port needs its own handle for independent NDP queue operations). But the probe-level open is done solely to read the interface map and is then closed. This is not a bug per se, but worth confirming the design intent: **N+1 opens** for N ports seems intentional.
**Confidence**: 50% — this is likely intentional but should be confirmed.
**Warning: `__rte_internal` in header file**
```c
__rte_internal
int nfb_eth_common_probe(struct nfb_probe_params *params);
__rte_internal
int nfb_eth_common_remove(struct rte_device *dev);
```
These are declared in `nfb.h` (a private driver header), which is fine for cross-file use within the driver. However, per AGENTS.md guidelines, `__rte_internal` should be alone on its own line before the function — and it is here, so this is correct. The `RTE_EXPORT_INTERNAL_SYMBOL` macros are present in the .c file. No issue.
**Warning: `nfb_eth_dev_create_for_ifc()` queue map population may write beyond allocated bounds**
In patch 2, the queue map population loop:
```c
cnt = 0;
for (i = 0; i < mi->rxq_cnt; i++) {
if (mi->rxq[i].ifc == ifc->id)
priv->queue_map_rx[cnt++] = mi->rxq[i].id;
}
```
The array `queue_map_rx` was allocated with size `max_rx_queues` (which is `ifc->rxq_cnt`). If the firmware map reports more RX queues for this interface than `ifc->rxq_cnt`, `cnt` could exceed the allocation. This depends on the libnfb library's consistency guarantees between `ifc->rxq_cnt` and the actual count of `mi->rxq[i].ifc == ifc->id` matches.
**Confidence**: ~55%. If `nc_ifc_map_info_create_ordinary()` guarantees consistency, this is safe. If not, a bounds check on `cnt < max_rx_queues` would be prudent.
**Info: Static TAILQ without synchronization**
The `nfb_eth_dev_list` TAILQ is a static global with no locking. This is safe as long as probe/remove are only called from the EAL main thread (which is the normal DPDK model), but worth noting.
---
### Patch 3/8: `net/nfb: add vdev as alternative device probe method`
Adds a vdev driver (`net_vdev_nfb`) for non-PCI devices (simulated/virtual).
**Correctness findings**:
**Warning: `nfb_default_dev_path()` used without NULL check**
```c
params.path = nfb_default_dev_path();
```
If `nfb_default_dev_path()` returns NULL (e.g., no NFB device present), this will be passed to `nfb_open()` which will likely segfault. The "dev" argument parsing later may override it, but if no `dev=` argument is given, the default path is used.
**Confidence**: ~65%. Depends on `nfb_default_dev_path()` semantics. If it always returns a valid string (even for non-existent devices), this is fine. If it can return NULL, a check is needed.
**Info: Mixed `free()` and `rte_kvargs_free()` cleanup**
The error paths correctly use `free()` for `strdup()`'d memory and `rte_kvargs_free()` for kvargs. This is correct.
---
### Patch 4/8: `net/nfb: add device argument "port" to limit used ports`
Adds ability to select specific ports via `port=N` arguments.
**Correctness findings**:
**Error: `strtoul` with base 0 allows octal/hex input — potential user confusion**
```c
port = strtoul(value, &end, 0);
```
Base 0 means `port=010` is parsed as octal (8), not decimal 10. For a port number argument, base 10 would be more predictable:
```c
port = strtoul(value, &end, 10);
```
**Confidence**: 70%. Not a crash bug, but a usability issue that could cause silent misconfiguration.
**Info: The `nfb_eth_dev_create_for_ifc_by_port()` validation is good** — it checks for empty string, non-digit start, and out-of-range values.
---
### Patch 5/8: `net/nfb: init only MACs associated with device`
Replaces the global "open all MACs" approach with per-interface MAC initialization.
**Correctness findings**:
**Warning: Uses `calloc()` instead of `rte_calloc()` for rxmac/txmac arrays**
```c
intl->rxmac = calloc(ifc->eth_cnt, sizeof(*intl->rxmac));
```
These are pointer arrays (not DMA-accessible), so `calloc()` is actually appropriate per DPDK guidelines (don't use `rte_malloc` for non-DMA control structures). However, `nfb_eth_dev_uninit()` is freeing the `internals` struct with `rte_free()` while these sub-allocations use plain `free()`. This mixing is fine — they're separate allocations — but worth noting for consistency.
**Error: `nfb_nc_eth_init()` doesn't validate `cnt` against allocated array size**
```c
intl->rxmac = calloc(ifc->eth_cnt, sizeof(*intl->rxmac));
// ...
rxm = 0;
for (i = 0; i < mi->eth_cnt; i++) {
if (mi->eth[i].ifc != ifc->id)
continue;
intl->rxmac[rxm] = nc_rxmac_open(intl->nfb, mi->eth[i].node_rxmac);
if (intl->rxmac[rxm])
rxm++;
// ...
}
```
The array is allocated with size `ifc->eth_cnt`, but the loop counts matches of `mi->eth[i].ifc == ifc->id` without bounding `rxm` against `ifc->eth_cnt`. If the firmware map has more eth entries for this interface than `ifc->eth_cnt` indicates, this overflows the allocation.
**Confidence**: ~55%. Same trust-the-firmware question as in patch 2's queue map population.
**Info: Early return for `ifc->eth_cnt == 0` is good defensive coding.**
---
### Patch 6/8: `net/nfb: add compatible cards to driver PCI ID table`
Adds PCI IDs for NFB-200G2QL secondary endpoint, FB2CGHH, COMBO-400G1, and generic CESNET-NDK.
No correctness issues. Straightforward PCI ID table additions.
---
### Patch 7/8: `net/nfb: report firmware version`
Implements `fw_version_get` ethdev op.
**Correctness findings**:
**Info: Good NULL handling for `proj_name` / `proj_vers`** — both are defaulted to empty string if NULL.
**Info: The return value semantics are correct** — returns 0 on success, the required buffer size on truncation, and negative on error, matching the `fw_version_get` API contract.
---
### Patch 8/8: `doc/nfb: cleanup and update guide`
Documentation rewrite and release notes update.
**Warning: Typo in documentation**
```rst
Ready-to-use demo firmwares can be found on the `DYNANIC page <https://dyna-nic.com/try-now/>`_.
```
"DYNANIC" should be "DYNANIC" or more likely "DyNANIC" or whatever the correct product name is — but the URL suggests it might be "dyna-nic", so the display text appears misspelled. Worth verifying the correct branding.
**Warning: Feature matrix additions should match implemented features**
The patch adds to `features/nfb.ini`:
```ini
Unicast MAC filter = Y
Timestamp offload = P
FW version = Y
Multiprocess aware = Y
```
"Unicast MAC filter" and "Timestamp offload" were presumably already supported but undocumented. "FW version" is added in patch 7. "Multiprocess aware" — the series adds `rte_eal_process_type()` checks in some places but the full multiprocess story should be verified.
---
## Cross-Cutting Concerns
### Thread Safety of Static TAILQ
The `nfb_eth_dev_list` TAILQ has no locking. DPDK's device management is typically single-threaded (EAL main thread), but if hotplug events could race with probe/remove, this could be problematic. This is consistent with how other DPDK drivers handle similar lists, so it's acceptable.
### Memory Allocation Strategy
The series uses an appropriate mix of `rte_calloc`/`rte_zmalloc` for data in the DPDK-managed dev_private area and plain `calloc`/`malloc` for driver-internal control structures (MAC handle arrays). This follows DPDK best practices.
### Error Path Completeness
Error paths are generally well-handled across the series. The `goto` cleanup chains in `nfb_eth_dev_init()` are consistent and cover the allocation sequence.
---
## Summary of Findings
| Severity | Patch | Finding |
|----------|-------|---------|
| **Error** | 2/8 | Double `TAILQ_REMOVE` — `nfb_eth_common_remove()` removes from list, then `nfb_eth_dev_uninit()` (called via `rte_eth_dev_destroy()`) removes again |
| **Warning** | 2/8 | Queue map population loop lacks bounds check against allocated size |
| **Warning** | 5/8 | `nfb_nc_eth_init()` rxmac/txmac population loop lacks bounds check against `ifc->eth_cnt` |
| **Warning** | 4/8 | `strtoul` with base 0 allows unintended octal/hex parsing of port numbers |
| **Warning** | 3/8 | `nfb_default_dev_path()` return value not checked for NULL before use |
| **Warning** | 8/8 | "DYNANIC" appears to be a typo in documentation |
| **Info** | 1/8 | No bounds check on `rx_queue_id` / `tx_queue_id` before indexing queue maps |
The **double TAILQ_REMOVE** in patch 2 is the most significant finding and will cause list corruption when removing devices. The fix is straightforward: remove the `TAILQ_REMOVE` call from `nfb_eth_common_remove()` and let `nfb_eth_dev_uninit()` handle list removal exclusively.
^ permalink raw reply [flat|nested] 131+ messages in thread
* [PATCH v8 0/8] net/nfb: rework to real multiport
2026-01-15 15:16 [PATCH 0/8] net/nfb: rework to real multiport spinler
` (14 preceding siblings ...)
2026-02-04 12:31 ` [PATCH v7 0/8] net/nfb: rework to real multiport spinler
@ 2026-02-13 18:53 ` spinler
2026-02-13 18:53 ` [PATCH v8 1/8] net/nfb: prepare for indirect queue mapping scheme spinler
` (8 more replies)
2026-02-16 16:24 ` [PATCH v9 " spinler
2026-02-17 7:10 ` [PATCH v10 " spinler
17 siblings, 9 replies; 131+ messages in thread
From: spinler @ 2026-02-13 18:53 UTC (permalink / raw)
To: dev; +Cc: Martin Spinler
From: Martin Spinler <spinler@cesnet.cz>
This series implements real multiport for better user experience.
The existing driver creates one ethdev/port for one PCI device.
As the CESNET-NDK based cards aren't capable to represent each
Ethernet port by own PCI device, new driver implementation
processes real port configuration from firmware/card and switches
from rte_eth_dev_pci_generic_probe to multiple rte_eth_dev_create calls.
---
Depends-on: patch-37245 ("doc/nfb: update release notes for nfb driver")
v8:
* removed double usage of TAILQ_REMOVE
* added "ready" bit to private struct for syncing of the data
shared with secondary processes on init
* added "ret" to nfb_ifc_create_params for use in failed
nfb_eth_dev_create_for_ifc() called from rte_kvargs_process();
now returns the right error value
* added bounds check on queue and MAC indexes in mapping
* added bounds check on queue index in queue setup
* added NULL check after rte_eth_dev_get_by_name()
* added errno check after strtoul()
* added integer overflow check in port validation
* added comment with usage of separate nfb handles
* used base 10 for port number as strtoul() param
Martin Spinler (8):
net/nfb: prepare for indirect queue mapping scheme
net/nfb: create one ethdev per ethernet port
net/nfb: add vdev as alternative device probe method
net/nfb: add device argument "port" to limit used ports
net/nfb: init only MACs associated with device
net/nfb: add compatible cards to driver PCI ID table
net/nfb: report firmware version
doc/nfb: cleanup and update guide
doc/guides/nics/features/nfb.ini | 4 +
doc/guides/nics/nfb.rst | 187 +++++-----
doc/guides/rel_notes/release_26_03.rst | 6 +
drivers/net/nfb/meson.build | 1 +
drivers/net/nfb/nfb.h | 57 ++-
drivers/net/nfb/nfb_ethdev.c | 487 +++++++++++++++++++------
drivers/net/nfb/nfb_rx.c | 33 +-
drivers/net/nfb/nfb_rx.h | 9 +-
drivers/net/nfb/nfb_tx.c | 30 +-
drivers/net/nfb/nfb_tx.h | 7 +-
drivers/net/nfb/nfb_vdev.c | 107 ++++++
11 files changed, 672 insertions(+), 256 deletions(-)
create mode 100644 drivers/net/nfb/nfb_vdev.c
--
2.53.0
^ permalink raw reply [flat|nested] 131+ messages in thread
* [PATCH v8 1/8] net/nfb: prepare for indirect queue mapping scheme
2026-02-13 18:53 ` [PATCH v8 " spinler
@ 2026-02-13 18:53 ` spinler
2026-02-13 18:53 ` [PATCH v8 2/8] net/nfb: create one ethdev per ethernet port spinler
` (7 subsequent siblings)
8 siblings, 0 replies; 131+ messages in thread
From: spinler @ 2026-02-13 18:53 UTC (permalink / raw)
To: dev; +Cc: Martin Spinler
From: Martin Spinler <spinler@cesnet.cz>
The NFB driver is being enhanced to create one ethdev per physical
Ethernet port instead of one ethdev per PCI device. This requires
an indirect mapping layer because DPDK queue indices no longer
directly correspond to firmware queue indices.
This change adds queue_map_rx and queue_map_tx arrays to track the
mapping between DPDK queue indices and firmware queue IDs, preparing
for the multi-port implementation in subsequent patches.
Signed-off-by: Martin Spinler <spinler@cesnet.cz>
---
drivers/net/nfb/nfb.h | 6 ++++++
drivers/net/nfb/nfb_ethdev.c | 28 ++++++++++++++++++++++++++++
drivers/net/nfb/nfb_rx.c | 33 ++++++++++++++++++---------------
drivers/net/nfb/nfb_rx.h | 9 ++++-----
drivers/net/nfb/nfb_tx.c | 30 ++++++++++++++++++------------
drivers/net/nfb/nfb_tx.h | 7 +++----
6 files changed, 77 insertions(+), 36 deletions(-)
diff --git a/drivers/net/nfb/nfb.h b/drivers/net/nfb/nfb.h
index 90b04c6151..fa51afc7de 100644
--- a/drivers/net/nfb/nfb.h
+++ b/drivers/net/nfb/nfb.h
@@ -60,6 +60,12 @@ struct pmd_internals {
struct pmd_priv {
uint16_t max_rx_queues;
uint16_t max_tx_queues;
+
+ /** Mapping from DPDK RX queue index to firmware queue ID */
+ int *queue_map_rx;
+ /** Mapping from DPDK TX queue index to firmware queue ID */
+ int *queue_map_tx;
+ bool ready; /**< This structure is initialized for usage in secondary process */
};
#endif /* _NFB_H_ */
diff --git a/drivers/net/nfb/nfb_ethdev.c b/drivers/net/nfb/nfb_ethdev.c
index 967f127f40..c0a2905249 100644
--- a/drivers/net/nfb/nfb_ethdev.c
+++ b/drivers/net/nfb/nfb_ethdev.c
@@ -509,6 +509,7 @@ static const struct eth_dev_ops ops = {
static int
nfb_eth_dev_init(struct rte_eth_dev *dev)
{
+ int i;
int ret;
uint32_t mac_count;
struct rte_eth_dev_data *data = dev->data;
@@ -577,6 +578,20 @@ nfb_eth_dev_init(struct rte_eth_dev *dev)
priv->max_rx_queues = max_rx_queues;
priv->max_tx_queues = max_tx_queues;
+ priv->queue_map_rx = rte_calloc("NFB queue map", max_rx_queues + max_tx_queues,
+ sizeof(*priv->queue_map_rx), 0);
+ if (priv->queue_map_rx == NULL) {
+ ret = -ENOMEM;
+ goto err_alloc_queue_map;
+ }
+ priv->queue_map_tx = priv->queue_map_rx + max_rx_queues;
+
+ /* default queue mapping is 1:1 */
+ for (i = 0; i < max_rx_queues; i++)
+ priv->queue_map_rx[i] = i;
+ for (i = 0; i < max_tx_queues; i++)
+ priv->queue_map_tx[i] = i;
+
/* Allocate space for MAC addresses */
mac_count = nfb_eth_get_max_mac_address_count(dev);
data->mac_addrs = rte_zmalloc(data->name,
@@ -599,6 +614,12 @@ nfb_eth_dev_init(struct rte_eth_dev *dev)
data->all_multicast = nfb_eth_allmulticast_get(dev);
data->dev_flags |= RTE_ETH_DEV_AUTOFILL_QUEUE_XSTATS;
+ priv->ready = true;
+ } else {
+ if (!priv->ready) {
+ ret = -EBADFD;
+ goto err_secondary_not_ready;
+ }
}
NFB_LOG(INFO, "NFB device (" PCI_PRI_FMT ") successfully initialized",
@@ -608,6 +629,9 @@ nfb_eth_dev_init(struct rte_eth_dev *dev)
return 0;
err_malloc_mac_addrs:
+ rte_free(priv->queue_map_rx);
+err_alloc_queue_map:
+err_secondary_not_ready:
nfb_nc_rxmac_deinit(internals->rxmac, internals->max_rxmac);
nfb_nc_txmac_deinit(internals->txmac, internals->max_txmac);
nfb_close(internals->nfb);
@@ -633,6 +657,10 @@ nfb_eth_dev_uninit(struct rte_eth_dev *dev)
struct rte_pci_device *pci_dev = RTE_ETH_DEV_TO_PCI(dev);
struct rte_pci_addr *pci_addr = &pci_dev->addr;
struct pmd_internals *internals = dev->process_private;
+ struct pmd_priv *priv = dev->data->dev_private;
+
+ if (rte_eal_process_type() == RTE_PROC_PRIMARY)
+ rte_free(priv->queue_map_rx);
nfb_nc_rxmac_deinit(internals->rxmac, internals->max_rxmac);
nfb_nc_txmac_deinit(internals->txmac, internals->max_txmac);
diff --git a/drivers/net/nfb/nfb_rx.c b/drivers/net/nfb/nfb_rx.c
index 413d275853..5b8579e280 100644
--- a/drivers/net/nfb/nfb_rx.c
+++ b/drivers/net/nfb/nfb_rx.c
@@ -61,12 +61,16 @@ nfb_eth_rx_queue_setup(struct rte_eth_dev *dev,
struct rte_mempool *mb_pool)
{
struct pmd_internals *internals = dev->process_private;
+ struct pmd_priv *priv = dev->data->dev_private;
- struct ndp_rx_queue *rxq;
int ret;
+ int qid;
+ struct ndp_rx_queue *rxq;
+
+ if (rx_queue_id >= priv->max_rx_queues)
+ return -EINVAL;
- rxq = rte_zmalloc_socket("ndp rx queue",
- sizeof(struct ndp_rx_queue),
+ rxq = rte_zmalloc_socket("ndp rx queue", sizeof(struct ndp_rx_queue),
RTE_CACHE_LINE_SIZE, socket_id);
if (rxq == NULL) {
@@ -77,23 +81,23 @@ nfb_eth_rx_queue_setup(struct rte_eth_dev *dev,
rxq->flags = 0;
- ret = nfb_eth_rx_queue_init(internals->nfb,
- rx_queue_id,
- dev->data->port_id,
- mb_pool,
- rxq);
+ qid = priv->queue_map_rx[rx_queue_id];
- if (ret == 0)
- dev->data->rx_queues[rx_queue_id] = rxq;
- else
- rte_free(rxq);
+ ret = nfb_eth_rx_queue_init(internals->nfb, qid, dev->data->port_id, mb_pool, rxq);
+ if (ret)
+ goto err_queue_init;
+
+ dev->data->rx_queues[rx_queue_id] = rxq;
+ return 0;
+err_queue_init:
+ rte_free(rxq);
return ret;
}
int
nfb_eth_rx_queue_init(struct nfb_device *nfb,
- uint16_t rx_queue_id,
+ int qid,
uint16_t port_id,
struct rte_mempool *mb_pool,
struct ndp_rx_queue *rxq)
@@ -104,12 +108,11 @@ nfb_eth_rx_queue_init(struct nfb_device *nfb,
if (nfb == NULL)
return -EINVAL;
- rxq->queue = ndp_open_rx_queue(nfb, rx_queue_id);
+ rxq->queue = ndp_open_rx_queue(nfb, qid);
if (rxq->queue == NULL)
return -EINVAL;
rxq->nfb = nfb;
- rxq->rx_queue_id = rx_queue_id;
rxq->in_port = port_id;
rxq->mb_pool = mb_pool;
rxq->buf_size = (uint16_t)(mbp_priv->mbuf_data_room_size -
diff --git a/drivers/net/nfb/nfb_rx.h b/drivers/net/nfb/nfb_rx.h
index 67b3b00e2a..831945c577 100644
--- a/drivers/net/nfb/nfb_rx.h
+++ b/drivers/net/nfb/nfb_rx.h
@@ -30,7 +30,6 @@ nfb_timestamp_dynfield(struct rte_mbuf *mbuf)
struct ndp_rx_queue {
struct nfb_device *nfb; /* nfb dev structure */
struct ndp_queue *queue; /* rx queue */
- uint16_t rx_queue_id; /* index */
uint8_t in_port; /* port */
uint8_t flags; /* setup flags */
@@ -47,8 +46,8 @@ struct ndp_rx_queue {
*
* @param nfb
* Pointer to nfb device structure.
- * @param rx_queue_id
- * RX queue index.
+ * @param qid
+ * RX queue ID.
* @param port_id
* Device [external] port identifier.
* @param mb_pool
@@ -60,7 +59,7 @@ struct ndp_rx_queue {
*/
int
nfb_eth_rx_queue_init(struct nfb_device *nfb,
- uint16_t rx_queue_id,
+ int qid,
uint16_t port_id,
struct rte_mempool *mb_pool,
struct ndp_rx_queue *rxq);
@@ -70,7 +69,7 @@ nfb_eth_rx_queue_init(struct nfb_device *nfb,
*
* @param dev
* Pointer to Ethernet device structure.
- * @param idx
+ * @param rx_queue_id
* RX queue index.
* @param desc
* Number of descriptors to configure in queue.
diff --git a/drivers/net/nfb/nfb_tx.c b/drivers/net/nfb/nfb_tx.c
index 1f997ce22f..1a7dcc3d30 100644
--- a/drivers/net/nfb/nfb_tx.c
+++ b/drivers/net/nfb/nfb_tx.c
@@ -54,11 +54,16 @@ nfb_eth_tx_queue_setup(struct rte_eth_dev *dev,
const struct rte_eth_txconf *tx_conf __rte_unused)
{
struct pmd_internals *internals = dev->process_private;
+ struct pmd_priv *priv = dev->data->dev_private;
+
int ret;
+ int qid;
struct ndp_tx_queue *txq;
- txq = rte_zmalloc_socket("ndp tx queue",
- sizeof(struct ndp_tx_queue),
+ if (tx_queue_id >= priv->max_tx_queues)
+ return -EINVAL;
+
+ txq = rte_zmalloc_socket("ndp tx queue", sizeof(struct ndp_tx_queue),
RTE_CACHE_LINE_SIZE, socket_id);
if (txq == NULL) {
@@ -67,32 +72,33 @@ nfb_eth_tx_queue_setup(struct rte_eth_dev *dev,
return -ENOMEM;
}
- ret = nfb_eth_tx_queue_init(internals->nfb,
- tx_queue_id,
- txq);
+ qid = priv->queue_map_tx[tx_queue_id];
- if (ret == 0)
- dev->data->tx_queues[tx_queue_id] = txq;
- else
- rte_free(txq);
+ ret = nfb_eth_tx_queue_init(internals->nfb, qid, txq);
+ if (ret)
+ goto err_queue_init;
+ dev->data->tx_queues[tx_queue_id] = txq;
+ return 0;
+
+err_queue_init:
+ rte_free(txq);
return ret;
}
int
nfb_eth_tx_queue_init(struct nfb_device *nfb,
- uint16_t tx_queue_id,
+ int qid,
struct ndp_tx_queue *txq)
{
if (nfb == NULL)
return -EINVAL;
- txq->queue = ndp_open_tx_queue(nfb, tx_queue_id);
+ txq->queue = ndp_open_tx_queue(nfb, qid);
if (txq->queue == NULL)
return -EINVAL;
txq->nfb = nfb;
- txq->tx_queue_id = tx_queue_id;
txq->tx_pkts = 0;
txq->tx_bytes = 0;
diff --git a/drivers/net/nfb/nfb_tx.h b/drivers/net/nfb/nfb_tx.h
index f107cf914b..c253af1a86 100644
--- a/drivers/net/nfb/nfb_tx.h
+++ b/drivers/net/nfb/nfb_tx.h
@@ -17,7 +17,6 @@
struct ndp_tx_queue {
struct nfb_device *nfb; /* nfb dev structure */
struct ndp_queue *queue; /* tx queue */
- uint16_t tx_queue_id; /* index */
volatile uint64_t tx_pkts; /* packets transmitted */
volatile uint64_t tx_bytes; /* bytes transmitted */
volatile uint64_t err_pkts; /* erroneous packets */
@@ -54,8 +53,8 @@ nfb_eth_tx_queue_setup(struct rte_eth_dev *dev,
*
* @param nfb
* Pointer to nfb device structure.
- * @param tx_queue_id
- * TX queue index.
+ * @param qid
+ * TX queue ID.
* @param[out] txq
* Pointer to ndp_tx_queue output structure
*
@@ -64,7 +63,7 @@ nfb_eth_tx_queue_setup(struct rte_eth_dev *dev,
*/
int
nfb_eth_tx_queue_init(struct nfb_device *nfb,
- uint16_t tx_queue_id,
+ int qid,
struct ndp_tx_queue *txq);
/**
--
2.53.0
^ permalink raw reply related [flat|nested] 131+ messages in thread
* [PATCH v8 2/8] net/nfb: create one ethdev per ethernet port
2026-02-13 18:53 ` [PATCH v8 " spinler
2026-02-13 18:53 ` [PATCH v8 1/8] net/nfb: prepare for indirect queue mapping scheme spinler
@ 2026-02-13 18:53 ` spinler
2026-02-13 19:33 ` Stephen Hemminger
2026-02-13 18:53 ` [PATCH v8 3/8] net/nfb: add vdev as alternative device probe method spinler
` (6 subsequent siblings)
8 siblings, 1 reply; 131+ messages in thread
From: spinler @ 2026-02-13 18:53 UTC (permalink / raw)
To: dev; +Cc: Martin Spinler
From: Martin Spinler <spinler@cesnet.cz>
The existing driver creates one ethdev/port for one PCI device.
As the CESNET-NDK-based cards are mostly unable to represent
each physical port with one PCI device, this atypical behaviour
often limits the user.
The nfb-framework comes with generic port representation API,
which provides information about ports and their mapping to firmware
resource (physical ports, DMA queues, processing pipelines).
This driver design switches from one rte_eth_dev_pci_generic_probe call
to multiple rte_eth_dev_create calls, while parses the firmware mapping
information.
Signed-off-by: Martin Spinler <spinler@cesnet.cz>
---
drivers/net/nfb/nfb.h | 27 ++++
drivers/net/nfb/nfb_ethdev.c | 234 +++++++++++++++++++++++++++++------
2 files changed, 220 insertions(+), 41 deletions(-)
diff --git a/drivers/net/nfb/nfb.h b/drivers/net/nfb/nfb.h
index fa51afc7de..3f758da7be 100644
--- a/drivers/net/nfb/nfb.h
+++ b/drivers/net/nfb/nfb.h
@@ -11,6 +11,7 @@
#include <nfb/ndp.h>
#include <netcope/rxmac.h>
#include <netcope/txmac.h>
+#include <netcope/info.h>
extern int nfb_logtype;
#define RTE_LOGTYPE_NFB nfb_logtype
@@ -51,6 +52,9 @@ struct pmd_internals {
struct nc_rxmac *rxmac[RTE_MAX_NC_RXMAC];
struct nc_txmac *txmac[RTE_MAX_NC_TXMAC];
struct nfb_device *nfb;
+
+ TAILQ_ENTRY(pmd_internals) eth_dev_list; /**< Item in list of all devices */
+ struct rte_eth_dev *eth_dev; /**< Handle for matching the device being removed */
};
/*
@@ -68,4 +72,27 @@ struct pmd_priv {
bool ready; /**< This structure is initialized for usage in secondary process */
};
+/* Data for common device probing */
+struct nfb_probe_params {
+ /** Generic device information */
+ struct rte_device *device;
+ /** Port bus specific initialisation callback function */
+ ethdev_bus_specific_init specific_init;
+ /** Port bus specific initialisation parameters */
+ void *specific_device;
+
+ const char *path; /**< libnfb device string */
+ const char *args; /**< Device arguments */
+ int nfb_id; /**< ID of NFB device in system */
+ int ep_index; /**< (PCIe) endpoint index for matching assigned interfaces */
+
+ char name[RTE_DEV_NAME_MAX_LEN]; /**< Probed name (e.g. PCI device name) */
+};
+
+
+__rte_internal
+int nfb_eth_common_probe(struct nfb_probe_params *params);
+__rte_internal
+int nfb_eth_common_remove(struct rte_device *dev);
+
#endif /* _NFB_H_ */
diff --git a/drivers/net/nfb/nfb_ethdev.c b/drivers/net/nfb/nfb_ethdev.c
index c0a2905249..5758e22426 100644
--- a/drivers/net/nfb/nfb_ethdev.c
+++ b/drivers/net/nfb/nfb_ethdev.c
@@ -4,6 +4,10 @@
* All rights reserved.
*/
+#include <sys/queue.h>
+#include <eal_export.h>
+#include <rte_tailq.h>
+
#include <nfb/nfb.h>
#include <nfb/ndp.h>
#include <netcope/rxmac.h>
@@ -18,6 +22,21 @@
#include "nfb_rxmode.h"
#include "nfb.h"
+struct nfb_ifc_create_params {
+ struct nfb_probe_params *probe_params;
+ struct nc_ifc_map_info map_info;
+ struct nc_ifc_info *ifc_info;
+
+ int basename_len; /* Cached real length of original probe_params->name */
+};
+
+/* The TAILQ entries are used for cleanup of allocated resources
+ * in situations, where dev_close is not called.
+ */
+TAILQ_HEAD(nfb_pmd_internals_head, pmd_internals);
+static struct nfb_pmd_internals_head nfb_eth_dev_list =
+ TAILQ_HEAD_INITIALIZER(nfb_eth_dev_list);
+
static int nfb_eth_dev_uninit(struct rte_eth_dev *dev);
/**
@@ -507,23 +526,20 @@ static const struct eth_dev_ops ops = {
* 0 on success, a negative errno value otherwise.
*/
static int
-nfb_eth_dev_init(struct rte_eth_dev *dev)
+nfb_eth_dev_init(struct rte_eth_dev *dev, void *init_data)
{
int i;
+ int cnt;
int ret;
uint32_t mac_count;
struct rte_eth_dev_data *data = dev->data;
struct pmd_internals *internals;
+ struct nfb_ifc_create_params *params = init_data;
+ struct nc_ifc_info *ifc = params->ifc_info;
+ struct nc_ifc_map_info *mi = ¶ms->map_info;
struct pmd_priv *priv = data->dev_private;
- struct rte_pci_device *pci_dev = RTE_ETH_DEV_TO_PCI(dev);
- struct rte_pci_addr *pci_addr = &pci_dev->addr;
struct rte_ether_addr eth_addr_init;
uint16_t max_rx_queues, max_tx_queues;
- char nfb_dev[PATH_MAX];
-
- NFB_LOG(INFO, "Initializing NFB device (" PCI_PRI_FMT ")",
- pci_addr->domain, pci_addr->bus, pci_addr->devid,
- pci_addr->function);
internals = rte_zmalloc_socket("nfb_internals",
sizeof(struct pmd_internals), RTE_CACHE_LINE_SIZE,
@@ -535,27 +551,17 @@ nfb_eth_dev_init(struct rte_eth_dev *dev)
dev->process_private = internals;
- snprintf(nfb_dev, sizeof(nfb_dev),
- "/dev/nfb/by-pci-slot/" PCI_PRI_FMT,
- pci_addr->domain, pci_addr->bus, pci_addr->devid,
- pci_addr->function);
-
- /*
- * Get number of available DMA RX and TX queues, which is maximum
- * number of queues that can be created and store it in private device
- * data structure.
- */
- internals->nfb = nfb_open(nfb_dev);
+ /* Open device handle */
+ internals->nfb = nfb_open(params->probe_params->path);
if (internals->nfb == NULL) {
- NFB_LOG(ERR, "nfb_open(): failed to open %s", nfb_dev);
+ NFB_LOG(ERR, "nfb_open(): failed to open %s", params->probe_params->path);
ret = -EINVAL;
goto err_nfb_open;
}
- max_rx_queues = ndp_get_rx_queue_available_count(internals->nfb);
- max_tx_queues = ndp_get_tx_queue_available_count(internals->nfb);
- NFB_LOG(INFO, "Available NDP queues RX: %u TX: %u",
- max_rx_queues, max_tx_queues);
+ /* Get number of available DMA RX and TX queues */
+ max_rx_queues = ifc->rxq_cnt;
+ max_tx_queues = ifc->txq_cnt;
nfb_nc_rxmac_init(internals->nfb,
internals->rxmac,
@@ -586,11 +592,30 @@ nfb_eth_dev_init(struct rte_eth_dev *dev)
}
priv->queue_map_tx = priv->queue_map_rx + max_rx_queues;
- /* default queue mapping is 1:1 */
- for (i = 0; i < max_rx_queues; i++)
- priv->queue_map_rx[i] = i;
- for (i = 0; i < max_tx_queues; i++)
- priv->queue_map_tx[i] = i;
+ /* Use queue mapping provided by libnfb */
+ cnt = 0;
+ for (i = 0; i < mi->rxq_cnt; i++) {
+ if (mi->rxq[i].ifc == ifc->id) {
+ if (cnt >= max_rx_queues) {
+ NFB_LOG(ERR, "RX queue mapping inconsistent");
+ ret = -EINVAL;
+ goto err_queue_map;
+ }
+ priv->queue_map_rx[cnt++] = mi->rxq[i].id;
+ }
+ }
+
+ cnt = 0;
+ for (i = 0; i < mi->txq_cnt; i++) {
+ if (mi->txq[i].ifc == ifc->id) {
+ if (cnt >= max_rx_queues) {
+ NFB_LOG(ERR, "TX queue mapping inconsistent");
+ ret = -EINVAL;
+ goto err_queue_map;
+ }
+ priv->queue_map_tx[cnt++] = mi->txq[i].id;
+ }
+ }
/* Allocate space for MAC addresses */
mac_count = nfb_eth_get_max_mac_address_count(dev);
@@ -622,13 +647,10 @@ nfb_eth_dev_init(struct rte_eth_dev *dev)
}
}
- NFB_LOG(INFO, "NFB device (" PCI_PRI_FMT ") successfully initialized",
- pci_addr->domain, pci_addr->bus, pci_addr->devid,
- pci_addr->function);
-
return 0;
err_malloc_mac_addrs:
+err_queue_map:
rte_free(priv->queue_map_rx);
err_alloc_queue_map:
err_secondary_not_ready:
@@ -654,25 +676,112 @@ nfb_eth_dev_init(struct rte_eth_dev *dev)
static int
nfb_eth_dev_uninit(struct rte_eth_dev *dev)
{
- struct rte_pci_device *pci_dev = RTE_ETH_DEV_TO_PCI(dev);
- struct rte_pci_addr *pci_addr = &pci_dev->addr;
struct pmd_internals *internals = dev->process_private;
struct pmd_priv *priv = dev->data->dev_private;
if (rte_eal_process_type() == RTE_PROC_PRIMARY)
rte_free(priv->queue_map_rx);
+ TAILQ_REMOVE(&nfb_eth_dev_list, internals, eth_dev_list);
+
nfb_nc_rxmac_deinit(internals->rxmac, internals->max_rxmac);
nfb_nc_txmac_deinit(internals->txmac, internals->max_txmac);
nfb_close(internals->nfb);
rte_free(internals);
- NFB_LOG(INFO, "NFB device (" PCI_PRI_FMT ") successfully uninitialized",
- pci_addr->domain, pci_addr->bus, pci_addr->devid,
- pci_addr->function);
+ return 0;
+}
+
+static int
+nfb_eth_dev_create_for_ifc(struct nfb_ifc_create_params *cp)
+{
+ int ret;
+ struct nc_ifc_info *ifc;
+ struct nfb_probe_params *pp;
+ struct rte_eth_dev *eth_dev;
+ struct pmd_internals *p;
+
+ ifc = cp->ifc_info;
+ pp = cp->probe_params;
+
+ /* Skip interfaces which doesn't belong to the probed PCI device */
+ if ((pp->ep_index != -1 && ifc->ep != pp->ep_index) ||
+ (ifc->flags & NC_IFC_INFO_FLAG_ACTIVE) == 0)
+ return 0;
+
+ ret = snprintf(pp->name + cp->basename_len, sizeof(pp->name) - cp->basename_len,
+ "_eth%d", ifc->id);
+ if (ret < 0 || ret >= (signed int)sizeof(pp->name) - cp->basename_len)
+ return -EINVAL;
+
+ ret = rte_eth_dev_create(pp->device, pp->name,
+ sizeof(struct pmd_priv),
+ pp->specific_init, pp->specific_device,
+ nfb_eth_dev_init, cp);
+ if (ret)
+ goto out;
+
+ eth_dev = rte_eth_dev_get_by_name(pp->name);
+ if (eth_dev == NULL) {
+ NFB_LOG(ERR, "Failed to get created device %s", pp->name);
+ ret = -ENODEV;
+ goto out;
+ }
+ p = eth_dev->process_private;
+ p->eth_dev = eth_dev;
+ TAILQ_INSERT_TAIL(&nfb_eth_dev_list, p, eth_dev_list);
+
+out:
+ /* return to original name (just for clarity) */
+ pp->name[cp->basename_len] = '\0';
+ return ret;
+}
+
+RTE_EXPORT_INTERNAL_SYMBOL(nfb_eth_common_probe)
+int
+nfb_eth_common_probe(struct nfb_probe_params *params)
+{
+ int i;
+ int ret;
+
+ struct nfb_device *nfb_dev;
+ struct nfb_ifc_create_params ifc_params;
+
+ /* Open the device handle just for parsing ifc_params.
+ * A separate handle is used later for each netdev.
+ */
+ nfb_dev = nfb_open(params->path);
+ if (nfb_dev == NULL) {
+ NFB_LOG(ERR, "nfb_open(): failed to open %s", params->path);
+ return -EINVAL;
+ }
+
+ ret = nc_ifc_map_info_create_ordinary(nfb_dev, &ifc_params.map_info);
+ if (ret)
+ goto err_map_info_create;
+
+ ifc_params.probe_params = params;
+ ifc_params.basename_len = strlen(params->name);
+
+ for (i = 0; i < ifc_params.map_info.ifc_cnt; i++) {
+ ifc_params.ifc_info = &ifc_params.map_info.ifc[i];
+ ret = nfb_eth_dev_create_for_ifc(&ifc_params);
+ if (ret)
+ goto err_dev_create;
+ }
+
+ nc_map_info_destroy(&ifc_params.map_info);
+ nfb_close(nfb_dev);
return 0;
+
+err_dev_create:
+ nfb_eth_common_remove(params->device);
+ nc_map_info_destroy(&ifc_params.map_info);
+err_map_info_create:
+ nfb_close(nfb_dev);
+ return ret;
}
static const struct rte_pci_id nfb_pci_id_table[] = {
@@ -701,8 +810,51 @@ static int
nfb_eth_pci_probe(struct rte_pci_driver *pci_drv __rte_unused,
struct rte_pci_device *pci_dev)
{
- return rte_eth_dev_pci_generic_probe(pci_dev,
- sizeof(struct pmd_priv), nfb_eth_dev_init);
+ int ret;
+ char path[PATH_MAX];
+
+ struct nc_composed_device_info comp_dev_info;
+ struct nfb_probe_params params = {0};
+
+ rte_pci_device_name(&pci_dev->addr, params.name, sizeof(params.name));
+
+ /*
+ * NFB device can be composed from multiple PCI devices,
+ * find the base char device ID for the current PCI device
+ */
+ ret = nc_get_composed_device_info_by_pci(NULL, params.name, &comp_dev_info);
+ if (ret) {
+ NFB_LOG(ERR, "Could not find NFB device for %s", params.name);
+ return -ENODEV;
+ }
+
+ ret = snprintf(path, sizeof(path), NFB_BASE_DEV_PATH "%d", comp_dev_info.nfb_id);
+ if (ret < 0 || ret >= (signed int)sizeof(path))
+ return -EINVAL;
+
+ params.args = pci_dev->device.devargs ? pci_dev->device.devargs->args : NULL;
+ params.path = path;
+ params.nfb_id = comp_dev_info.nfb_id;
+ params.ep_index = comp_dev_info.ep_index;
+
+ params.device = &pci_dev->device;
+ params.specific_init = eth_dev_pci_specific_init;
+ params.specific_device = pci_dev;
+
+ return nfb_eth_common_probe(¶ms);
+}
+
+RTE_EXPORT_INTERNAL_SYMBOL(nfb_eth_common_remove)
+int
+nfb_eth_common_remove(struct rte_device *dev)
+{
+ struct pmd_internals *entry, *temp;
+
+ RTE_TAILQ_FOREACH_SAFE(entry, &nfb_eth_dev_list, eth_dev_list, temp) {
+ if (dev == entry->eth_dev->device)
+ rte_eth_dev_destroy(entry->eth_dev, nfb_eth_dev_uninit);
+ }
+ return 0;
}
/**
@@ -719,7 +871,7 @@ nfb_eth_pci_probe(struct rte_pci_driver *pci_drv __rte_unused,
static int
nfb_eth_pci_remove(struct rte_pci_device *pci_dev)
{
- return rte_eth_dev_pci_generic_remove(pci_dev, nfb_eth_dev_uninit);
+ return nfb_eth_common_remove(&pci_dev->device);
}
static struct rte_pci_driver nfb_eth_driver = {
--
2.53.0
^ permalink raw reply related [flat|nested] 131+ messages in thread
* [PATCH v8 3/8] net/nfb: add vdev as alternative device probe method
2026-02-13 18:53 ` [PATCH v8 " spinler
2026-02-13 18:53 ` [PATCH v8 1/8] net/nfb: prepare for indirect queue mapping scheme spinler
2026-02-13 18:53 ` [PATCH v8 2/8] net/nfb: create one ethdev per ethernet port spinler
@ 2026-02-13 18:53 ` spinler
2026-02-13 18:53 ` [PATCH v8 4/8] net/nfb: add device argument "port" to limit used ports spinler
` (5 subsequent siblings)
8 siblings, 0 replies; 131+ messages in thread
From: spinler @ 2026-02-13 18:53 UTC (permalink / raw)
To: dev; +Cc: Martin Spinler
From: Martin Spinler <spinler@cesnet.cz>
VDEV are useful for NFB devices not represented by PCI device,
e.g. virtual or simulated devices.
Signed-off-by: Martin Spinler <spinler@cesnet.cz>
---
drivers/net/nfb/meson.build | 1 +
drivers/net/nfb/nfb_vdev.c | 106 ++++++++++++++++++++++++++++++++++++
2 files changed, 107 insertions(+)
create mode 100644 drivers/net/nfb/nfb_vdev.c
diff --git a/drivers/net/nfb/meson.build b/drivers/net/nfb/meson.build
index d7a255c928..9e458dfb4a 100644
--- a/drivers/net/nfb/meson.build
+++ b/drivers/net/nfb/meson.build
@@ -16,6 +16,7 @@ ext_deps += dep
sources = files(
'nfb_ethdev.c',
+ 'nfb_vdev.c',
'nfb_rx.c',
'nfb_rxmode.c',
'nfb_stats.c',
diff --git a/drivers/net/nfb/nfb_vdev.c b/drivers/net/nfb/nfb_vdev.c
new file mode 100644
index 0000000000..b79f7ac416
--- /dev/null
+++ b/drivers/net/nfb/nfb_vdev.c
@@ -0,0 +1,106 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2026 Cesnet
+ */
+
+#include <rte_kvargs.h>
+#include <ethdev_vdev.h>
+
+#include "nfb.h"
+
+#define VDEV_NFB_DRIVER net_vdev_nfb
+#define VDEV_NFB_ARG_DEV "dev"
+
+static int
+vdev_nfb_vdev_probe(struct rte_vdev_device *dev)
+{
+ unsigned int count;
+ int ret = 0;
+ size_t len, pos;
+
+ struct nfb_probe_params params;
+
+ const char *vdev_name = rte_vdev_device_name(dev);
+ const char *vdev_args = rte_vdev_device_args(dev);
+ char *dev_params, *dev_params_mod;
+ struct rte_kvargs *kvargs;
+
+ kvargs = rte_kvargs_parse(vdev_args, NULL);
+ if (kvargs == NULL) {
+ NFB_LOG(ERR, "Failed to parse device arguments %s", vdev_args);
+ ret = -EINVAL;
+ goto err_parse_args;
+ }
+
+ dev_params = strdup(vdev_args);
+ if (dev_params == NULL) {
+ ret = -ENOMEM;
+ goto err_strdup_params;
+ }
+
+ params.device = &dev->device;
+ params.specific_init = NULL;
+ params.specific_device = NULL;
+ params.path = nfb_default_dev_path();
+ params.args = dev_params;
+ params.nfb_id = 0;
+ params.ep_index = -1;
+
+ len = strlen(dev_params) + 1;
+ pos = 0;
+ dev_params[pos] = '\0';
+
+ /* Parse parameters for virtual device */
+ for (count = 0; count != kvargs->count; ++count) {
+ const struct rte_kvargs_pair *pair = &kvargs->pairs[count];
+
+ if (!strcmp(pair->key, VDEV_NFB_ARG_DEV)) {
+ params.path = pair->value;
+ } else {
+ /* Clone non-vdev arguments, result is shorter or equal length */
+ dev_params_mod = dev_params + pos;
+ ret = snprintf(dev_params_mod, len, "%s%s=%s",
+ pos == 0 ? "" : ",", pair->key, pair->value);
+ if (ret < 0 || ret >= (signed int)len)
+ goto err_clone_args;
+ pos += ret;
+ len -= ret;
+ }
+ }
+
+ strlcpy(params.name, vdev_name, sizeof(params.name));
+
+ ret = nfb_eth_common_probe(¶ms);
+ if (ret)
+ goto err_nfb_common_probe;
+
+ free(dev_params);
+ rte_kvargs_free(kvargs);
+
+ return ret;
+
+err_nfb_common_probe:
+err_clone_args:
+ free(dev_params);
+err_strdup_params:
+ rte_kvargs_free(kvargs);
+err_parse_args:
+ return ret;
+}
+
+static int
+vdev_nfb_vdev_remove(struct rte_vdev_device *dev)
+{
+ return nfb_eth_common_remove(&dev->device);
+}
+
+/** Virtual device descriptor. */
+static struct rte_vdev_driver vdev_nfb_vdev = {
+ .probe = vdev_nfb_vdev_probe,
+ .remove = vdev_nfb_vdev_remove,
+};
+
+RTE_PMD_REGISTER_VDEV(VDEV_NFB_DRIVER, vdev_nfb_vdev);
+RTE_PMD_REGISTER_ALIAS(VDEV_NFB_DRIVER, eth_vdev_nfb);
+RTE_PMD_REGISTER_PARAM_STRING(net_vdev_nfb,
+ VDEV_NFB_ARG_DEV "=<string>"
+ );
--
2.53.0
^ permalink raw reply related [flat|nested] 131+ messages in thread
* [PATCH v8 4/8] net/nfb: add device argument "port" to limit used ports
2026-02-13 18:53 ` [PATCH v8 " spinler
` (2 preceding siblings ...)
2026-02-13 18:53 ` [PATCH v8 3/8] net/nfb: add vdev as alternative device probe method spinler
@ 2026-02-13 18:53 ` spinler
2026-02-13 18:53 ` [PATCH v8 5/8] net/nfb: init only MACs associated with device spinler
` (4 subsequent siblings)
8 siblings, 0 replies; 131+ messages in thread
From: spinler @ 2026-02-13 18:53 UTC (permalink / raw)
To: dev; +Cc: Martin Spinler
From: Martin Spinler <spinler@cesnet.cz>
NFB devices do not use separate PCI device for each port; one PCI device
contains handles for all ports instead. For some application this approach
can be limiting.
The "port" argument can be used to select only desired ports.
It can be used multiple times. When is not used at all, the driver
selects all ports.
Signed-off-by: Martin Spinler <spinler@cesnet.cz>
---
drivers/net/nfb/nfb.h | 7 ++++
drivers/net/nfb/nfb_ethdev.c | 67 +++++++++++++++++++++++++++++++++---
drivers/net/nfb/nfb_vdev.c | 3 +-
3 files changed, 72 insertions(+), 5 deletions(-)
diff --git a/drivers/net/nfb/nfb.h b/drivers/net/nfb/nfb.h
index 3f758da7be..0ff49929c1 100644
--- a/drivers/net/nfb/nfb.h
+++ b/drivers/net/nfb/nfb.h
@@ -42,6 +42,13 @@ extern int nfb_logtype;
#define RTE_NFB_DRIVER_NAME net_nfb
+/* Device arguments */
+#define NFB_ARG_PORT "port"
+
+#define NFB_COMMON_ARGS \
+ NFB_ARG_PORT "=<number>" \
+ ""
+
/*
* Handles obtained from the libnfb: each process must use own instance.
* Stored inside dev->process_private.
diff --git a/drivers/net/nfb/nfb_ethdev.c b/drivers/net/nfb/nfb_ethdev.c
index 5758e22426..6ff343488d 100644
--- a/drivers/net/nfb/nfb_ethdev.c
+++ b/drivers/net/nfb/nfb_ethdev.c
@@ -4,6 +4,7 @@
* All rights reserved.
*/
+#include <ctype.h>
#include <sys/queue.h>
#include <eal_export.h>
#include <rte_tailq.h>
@@ -22,12 +23,19 @@
#include "nfb_rxmode.h"
#include "nfb.h"
+static const char * const VALID_KEYS[] = {
+ NFB_ARG_PORT,
+ NULL
+};
+
struct nfb_ifc_create_params {
struct nfb_probe_params *probe_params;
struct nc_ifc_map_info map_info;
struct nc_ifc_info *ifc_info;
int basename_len; /* Cached real length of original probe_params->name */
+ /* Return value of failed nfb_eth_dev_create_for_ifc when rte_kvargs_process is used */
+ int ret;
};
/* The TAILQ entries are used for cleanup of allocated resources
@@ -738,6 +746,29 @@ nfb_eth_dev_create_for_ifc(struct nfb_ifc_create_params *cp)
return ret;
}
+static int nfb_eth_dev_create_for_ifc_by_port(const char *key __rte_unused,
+ const char *value, void *opaque)
+{
+ char *end;
+ unsigned long port;
+ struct nfb_ifc_create_params *ifc_params = opaque;
+
+ if (value == NULL || strlen(value) == 0 || !isdigit(*value))
+ return -EINVAL;
+
+ errno = 0;
+ port = strtoul(value, &end, 10);
+ if (errno != 0 || *end != '\0')
+ return -EINVAL;
+
+ if (port >= LONG_MAX || port >= (unsigned long)ifc_params->map_info.ifc_cnt)
+ return -EINVAL;
+
+ ifc_params->ifc_info = &ifc_params->map_info.ifc[port];
+ ifc_params->ret = nfb_eth_dev_create_for_ifc(ifc_params);
+ return ifc_params->ret;
+}
+
RTE_EXPORT_INTERNAL_SYMBOL(nfb_eth_common_probe)
int
nfb_eth_common_probe(struct nfb_probe_params *params)
@@ -747,6 +778,7 @@ nfb_eth_common_probe(struct nfb_probe_params *params)
struct nfb_device *nfb_dev;
struct nfb_ifc_create_params ifc_params;
+ struct rte_kvargs *kvlist = NULL;
/* Open the device handle just for parsing ifc_params.
* A separate handle is used later for each netdev.
@@ -761,16 +793,38 @@ nfb_eth_common_probe(struct nfb_probe_params *params)
if (ret)
goto err_map_info_create;
+ if (params->args != NULL && strlen(params->args) > 0) {
+ kvlist = rte_kvargs_parse(params->args, VALID_KEYS);
+ if (kvlist == NULL) {
+ NFB_LOG(ERR, "Failed to parse device arguments %s", params->args);
+ ret = -EINVAL;
+ goto err_parse_args;
+ }
+ }
+
+ ifc_params.ret = 0;
ifc_params.probe_params = params;
ifc_params.basename_len = strlen(params->name);
- for (i = 0; i < ifc_params.map_info.ifc_cnt; i++) {
- ifc_params.ifc_info = &ifc_params.map_info.ifc[i];
- ret = nfb_eth_dev_create_for_ifc(&ifc_params);
- if (ret)
+ /* When at least one port argument is specified, create only selected ports */
+ if (kvlist && rte_kvargs_count(kvlist, NFB_ARG_PORT)) {
+ ret = rte_kvargs_process(kvlist, NFB_ARG_PORT,
+ nfb_eth_dev_create_for_ifc_by_port, (void *)&ifc_params);
+ if (ret) {
+ ret = ifc_params.ret;
goto err_dev_create;
+ }
+ } else {
+ for (i = 0; i < ifc_params.map_info.ifc_cnt; i++) {
+ ifc_params.ifc_info = &ifc_params.map_info.ifc[i];
+ ret = nfb_eth_dev_create_for_ifc(&ifc_params);
+ if (ret)
+ goto err_dev_create;
+ }
}
+ rte_kvargs_free(kvlist);
+
nc_map_info_destroy(&ifc_params.map_info);
nfb_close(nfb_dev);
@@ -778,6 +832,8 @@ nfb_eth_common_probe(struct nfb_probe_params *params)
err_dev_create:
nfb_eth_common_remove(params->device);
+ rte_kvargs_free(kvlist);
+err_parse_args:
nc_map_info_destroy(&ifc_params.map_info);
err_map_info_create:
nfb_close(nfb_dev);
@@ -884,3 +940,6 @@ RTE_PMD_REGISTER_PCI(RTE_NFB_DRIVER_NAME, nfb_eth_driver);
RTE_PMD_REGISTER_PCI_TABLE(RTE_NFB_DRIVER_NAME, nfb_pci_id_table);
RTE_PMD_REGISTER_KMOD_DEP(RTE_NFB_DRIVER_NAME, "* nfb");
RTE_LOG_REGISTER_DEFAULT(nfb_logtype, NOTICE);
+RTE_PMD_REGISTER_PARAM_STRING(RTE_NFB_DRIVER_NAME,
+ NFB_COMMON_ARGS
+ );
diff --git a/drivers/net/nfb/nfb_vdev.c b/drivers/net/nfb/nfb_vdev.c
index b79f7ac416..57f10d2068 100644
--- a/drivers/net/nfb/nfb_vdev.c
+++ b/drivers/net/nfb/nfb_vdev.c
@@ -102,5 +102,6 @@ static struct rte_vdev_driver vdev_nfb_vdev = {
RTE_PMD_REGISTER_VDEV(VDEV_NFB_DRIVER, vdev_nfb_vdev);
RTE_PMD_REGISTER_ALIAS(VDEV_NFB_DRIVER, eth_vdev_nfb);
RTE_PMD_REGISTER_PARAM_STRING(net_vdev_nfb,
- VDEV_NFB_ARG_DEV "=<string>"
+ VDEV_NFB_ARG_DEV "=<string> "
+ NFB_COMMON_ARGS
);
--
2.53.0
^ permalink raw reply related [flat|nested] 131+ messages in thread
* [PATCH v8 5/8] net/nfb: init only MACs associated with device
2026-02-13 18:53 ` [PATCH v8 " spinler
` (3 preceding siblings ...)
2026-02-13 18:53 ` [PATCH v8 4/8] net/nfb: add device argument "port" to limit used ports spinler
@ 2026-02-13 18:53 ` spinler
2026-02-13 18:53 ` [PATCH v8 6/8] net/nfb: add compatible cards to driver PCI ID table spinler
` (3 subsequent siblings)
8 siblings, 0 replies; 131+ messages in thread
From: spinler @ 2026-02-13 18:53 UTC (permalink / raw)
To: dev; +Cc: Martin Spinler
From: Martin Spinler <spinler@cesnet.cz>
Use information obtained from the new manner of creating NFB ethdevs
and initialize only RxMACs / TxMACs associated with actual ethdev.
This enables separated management and configuration of interface ports.
Signed-off-by: Martin Spinler <spinler@cesnet.cz>
---
drivers/net/nfb/nfb.h | 12 +--
drivers/net/nfb/nfb_ethdev.c | 145 +++++++++++++++++------------------
2 files changed, 75 insertions(+), 82 deletions(-)
diff --git a/drivers/net/nfb/nfb.h b/drivers/net/nfb/nfb.h
index 0ff49929c1..b064b45c47 100644
--- a/drivers/net/nfb/nfb.h
+++ b/drivers/net/nfb/nfb.h
@@ -36,10 +36,6 @@ extern int nfb_logtype;
#define RTE_ETH_NDP_MAX_RX_QUEUES 32
#define RTE_ETH_NDP_MAX_TX_QUEUES 32
-/* Max index of rx/tx dmas */
-#define RTE_MAX_NC_RXMAC 256
-#define RTE_MAX_NC_TXMAC 256
-
#define RTE_NFB_DRIVER_NAME net_nfb
/* Device arguments */
@@ -54,10 +50,10 @@ extern int nfb_logtype;
* Stored inside dev->process_private.
*/
struct pmd_internals {
- uint16_t max_rxmac;
- uint16_t max_txmac;
- struct nc_rxmac *rxmac[RTE_MAX_NC_RXMAC];
- struct nc_txmac *txmac[RTE_MAX_NC_TXMAC];
+ uint16_t max_rxmac; /**< Count of valid rxmac items */
+ uint16_t max_txmac; /**< Count of valid txmac items */
+ struct nc_rxmac **rxmac; /**< Array of Rx MAC handles */
+ struct nc_txmac **txmac; /**< Array of Tx MAC handles */
struct nfb_device *nfb;
TAILQ_ENTRY(pmd_internals) eth_dev_list; /**< Item in list of all devices */
diff --git a/drivers/net/nfb/nfb_ethdev.c b/drivers/net/nfb/nfb_ethdev.c
index 6ff343488d..826a2504c7 100644
--- a/drivers/net/nfb/nfb_ethdev.c
+++ b/drivers/net/nfb/nfb_ethdev.c
@@ -55,81 +55,83 @@ static const struct rte_ether_addr eth_addr = {
};
/**
- * Open all RX DMA queues
+ * Open Rx and Tx MAC components associated with current ifc
*
- * @param dev
- * Pointer to nfb device.
- * @param[out] rxmac
- * Pointer to output array of nc_rxmac
- * @param[out] max_rxmac
- * Pointer to output max index of rxmac
+ * @param intl
+ * Pointer to driver internal structure
+ * @param params
+ * Pointer to init parameters structure
*/
-static void
-nfb_nc_rxmac_init(struct nfb_device *nfb,
- struct nc_rxmac *rxmac[RTE_MAX_NC_RXMAC],
- uint16_t *max_rxmac)
+static int
+nfb_nc_eth_init(struct pmd_internals *intl, struct nfb_ifc_create_params *params)
{
- *max_rxmac = 0;
- while ((rxmac[*max_rxmac] = nc_rxmac_open_index(nfb, *max_rxmac)))
- ++(*max_rxmac);
-}
+ int ret;
+ int i, rxm, txm;
+ struct nc_ifc_info *ifc = params->ifc_info;
+ struct nc_ifc_map_info *mi = ¶ms->map_info;
-/**
- * Open all TX DMA queues
- *
- * @param dev
- * Pointer to nfb device.
- * @param[out] txmac
- * Pointer to output array of nc_txmac
- * @param[out] max_rxmac
- * Pointer to output max index of txmac
- */
-static void
-nfb_nc_txmac_init(struct nfb_device *nfb,
- struct nc_txmac *txmac[RTE_MAX_NC_TXMAC],
- uint16_t *max_txmac)
-{
- *max_txmac = 0;
- while ((txmac[*max_txmac] = nc_txmac_open_index(nfb, *max_txmac)))
- ++(*max_txmac);
-}
+ ret = -ENOMEM;
+ if (ifc->eth_cnt == 0)
+ return 0;
-/**
- * Close all RX DMA queues
- *
- * @param rxmac
- * Pointer to array of nc_rxmac
- * @param max_rxmac
- * Maximum index of rxmac
- */
-static void
-nfb_nc_rxmac_deinit(struct nc_rxmac *rxmac[RTE_MAX_NC_RXMAC],
- uint16_t max_rxmac)
-{
- uint16_t i;
- for (i = 0; i < max_rxmac; i++) {
- nc_rxmac_close(rxmac[i]);
- rxmac[i] = NULL;
+ intl->rxmac = calloc(ifc->eth_cnt, sizeof(*intl->rxmac));
+ if (intl->rxmac == NULL)
+ goto err_alloc_rxmac;
+
+ intl->txmac = calloc(ifc->eth_cnt, sizeof(*intl->txmac));
+ if (intl->txmac == NULL)
+ goto err_alloc_txmac;
+
+ /* Some eths may not have assigned MAC nodes, hence use separate var for indexing */
+ rxm = 0;
+ txm = 0;
+ for (i = 0; i < mi->eth_cnt; i++) {
+ if (mi->eth[i].ifc != ifc->id)
+ continue;
+
+ if (rxm >= ifc->eth_cnt || txm >= ifc->eth_cnt) {
+ NFB_LOG(ERR, "MAC mapping inconsistent");
+ ret = -EINVAL;
+ goto err_map_inconsistent;
+ }
+
+ intl->rxmac[rxm] = nc_rxmac_open(intl->nfb, mi->eth[i].node_rxmac);
+ if (intl->rxmac[rxm])
+ rxm++;
+
+ intl->txmac[txm] = nc_txmac_open(intl->nfb, mi->eth[i].node_txmac);
+ if (intl->txmac[txm])
+ txm++;
}
+
+ intl->max_rxmac = rxm;
+ intl->max_txmac = txm;
+ return 0;
+
+err_map_inconsistent:
+ free(intl->txmac);
+err_alloc_txmac:
+ free(intl->rxmac);
+err_alloc_rxmac:
+ return ret;
}
/**
- * Close all TX DMA queues
- *
- * @param txmac
- * Pointer to array of nc_txmac
- * @param max_txmac
- * Maximum index of txmac
+ * Close all Rx+Tx MAC components
+ * @param intl
+ * Pointer to internal driver structure
*/
static void
-nfb_nc_txmac_deinit(struct nc_txmac *txmac[RTE_MAX_NC_TXMAC],
- uint16_t max_txmac)
+nfb_nc_eth_deinit(struct pmd_internals *intl)
{
uint16_t i;
- for (i = 0; i < max_txmac; i++) {
- nc_txmac_close(txmac[i]);
- txmac[i] = NULL;
- }
+ for (i = 0; i < intl->max_txmac; i++)
+ nc_txmac_close(intl->txmac[i]);
+ for (i = 0; i < intl->max_rxmac; i++)
+ nc_rxmac_close(intl->rxmac[i]);
+
+ free(intl->txmac);
+ free(intl->rxmac);
}
/**
@@ -346,7 +348,7 @@ nfb_eth_link_update(struct rte_eth_dev *dev,
link.link_duplex = RTE_ETH_LINK_FULL_DUPLEX;
link.link_autoneg = RTE_ETH_LINK_SPEED_FIXED;
- if (internals->rxmac[0] != NULL) {
+ if (internals->max_rxmac) {
nc_rxmac_read_status(internals->rxmac[0], &status);
switch (status.speed) {
@@ -571,12 +573,9 @@ nfb_eth_dev_init(struct rte_eth_dev *dev, void *init_data)
max_rx_queues = ifc->rxq_cnt;
max_tx_queues = ifc->txq_cnt;
- nfb_nc_rxmac_init(internals->nfb,
- internals->rxmac,
- &internals->max_rxmac);
- nfb_nc_txmac_init(internals->nfb,
- internals->txmac,
- &internals->max_txmac);
+ ret = nfb_nc_eth_init(internals, params);
+ if (ret)
+ goto err_nc_eth_init;
/* Set rx, tx burst functions */
dev->rx_pkt_burst = nfb_eth_ndp_rx;
@@ -662,10 +661,9 @@ nfb_eth_dev_init(struct rte_eth_dev *dev, void *init_data)
rte_free(priv->queue_map_rx);
err_alloc_queue_map:
err_secondary_not_ready:
- nfb_nc_rxmac_deinit(internals->rxmac, internals->max_rxmac);
- nfb_nc_txmac_deinit(internals->txmac, internals->max_txmac);
+ nfb_nc_eth_deinit(internals);
+err_nc_eth_init:
nfb_close(internals->nfb);
-
err_nfb_open:
rte_free(internals);
err_alloc_internals:
@@ -692,8 +690,7 @@ nfb_eth_dev_uninit(struct rte_eth_dev *dev)
TAILQ_REMOVE(&nfb_eth_dev_list, internals, eth_dev_list);
- nfb_nc_rxmac_deinit(internals->rxmac, internals->max_rxmac);
- nfb_nc_txmac_deinit(internals->txmac, internals->max_txmac);
+ nfb_nc_eth_deinit(internals);
nfb_close(internals->nfb);
rte_free(internals);
--
2.53.0
^ permalink raw reply related [flat|nested] 131+ messages in thread
* [PATCH v8 6/8] net/nfb: add compatible cards to driver PCI ID table
2026-02-13 18:53 ` [PATCH v8 " spinler
` (4 preceding siblings ...)
2026-02-13 18:53 ` [PATCH v8 5/8] net/nfb: init only MACs associated with device spinler
@ 2026-02-13 18:53 ` spinler
2026-02-13 18:53 ` [PATCH v8 7/8] net/nfb: report firmware version spinler
` (2 subsequent siblings)
8 siblings, 0 replies; 131+ messages in thread
From: spinler @ 2026-02-13 18:53 UTC (permalink / raw)
To: dev; +Cc: Martin Spinler
From: Martin Spinler <spinler@cesnet.cz>
The nfb driver is compatible with:
- NFB-200G2QL card secondary PCI device
- FB2CGHH card
- COMBO-400G1 card
- Common CESNET-NDK cards
Signed-off-by: Martin Spinler <spinler@cesnet.cz>
---
drivers/net/nfb/nfb.h | 5 +++++
drivers/net/nfb/nfb_ethdev.c | 4 ++++
2 files changed, 9 insertions(+)
diff --git a/drivers/net/nfb/nfb.h b/drivers/net/nfb/nfb.h
index b064b45c47..72580e4fcb 100644
--- a/drivers/net/nfb/nfb.h
+++ b/drivers/net/nfb/nfb.h
@@ -24,13 +24,18 @@ extern int nfb_logtype;
/* PCI Vendor ID */
#define PCI_VENDOR_ID_NETCOPE 0x1b26
#define PCI_VENDOR_ID_SILICOM 0x1c2c
+#define PCI_VENDOR_ID_CESNET 0x18ec
/* PCI Device IDs */
#define PCI_DEVICE_ID_NFB_40G2 0xcb80
#define PCI_DEVICE_ID_NFB_100G2 0xc2c1
#define PCI_DEVICE_ID_NFB_200G2QL 0xc250
+#define PCI_DEVICE_ID_NFB_200G2QL_E1 0xc251
#define PCI_DEVICE_ID_FB2CGG3 0x00d0
+#define PCI_DEVICE_ID_FB2CGHH 0x00d2
#define PCI_DEVICE_ID_FB2CGG3D 0xc240
+#define PCI_DEVICE_ID_COMBO400G1 0xc400
+#define PCI_DEVICE_ID_CESNET_NDK_COMMON 0xc000
/* Max index of ndp rx/tx queues */
#define RTE_ETH_NDP_MAX_RX_QUEUES 32
diff --git a/drivers/net/nfb/nfb_ethdev.c b/drivers/net/nfb/nfb_ethdev.c
index 826a2504c7..1b481c0312 100644
--- a/drivers/net/nfb/nfb_ethdev.c
+++ b/drivers/net/nfb/nfb_ethdev.c
@@ -841,8 +841,12 @@ static const struct rte_pci_id nfb_pci_id_table[] = {
{ RTE_PCI_DEVICE(PCI_VENDOR_ID_NETCOPE, PCI_DEVICE_ID_NFB_40G2) },
{ RTE_PCI_DEVICE(PCI_VENDOR_ID_NETCOPE, PCI_DEVICE_ID_NFB_100G2) },
{ RTE_PCI_DEVICE(PCI_VENDOR_ID_NETCOPE, PCI_DEVICE_ID_NFB_200G2QL) },
+ { RTE_PCI_DEVICE(PCI_VENDOR_ID_NETCOPE, PCI_DEVICE_ID_NFB_200G2QL_E1) },
{ RTE_PCI_DEVICE(PCI_VENDOR_ID_SILICOM, PCI_DEVICE_ID_FB2CGG3) },
+ { RTE_PCI_DEVICE(PCI_VENDOR_ID_SILICOM, PCI_DEVICE_ID_FB2CGHH) },
{ RTE_PCI_DEVICE(PCI_VENDOR_ID_SILICOM, PCI_DEVICE_ID_FB2CGG3D) },
+ { RTE_PCI_DEVICE(PCI_VENDOR_ID_CESNET, PCI_DEVICE_ID_COMBO400G1) },
+ { RTE_PCI_DEVICE(PCI_VENDOR_ID_CESNET, PCI_DEVICE_ID_CESNET_NDK_COMMON) },
{ .vendor_id = 0, }
};
--
2.53.0
^ permalink raw reply related [flat|nested] 131+ messages in thread
* [PATCH v8 7/8] net/nfb: report firmware version
2026-02-13 18:53 ` [PATCH v8 " spinler
` (5 preceding siblings ...)
2026-02-13 18:53 ` [PATCH v8 6/8] net/nfb: add compatible cards to driver PCI ID table spinler
@ 2026-02-13 18:53 ` spinler
2026-02-13 18:53 ` [PATCH v8 8/8] doc/nfb: cleanup and update guide spinler
2026-02-13 19:39 ` [PATCH v8 0/8] net/nfb: rework to real multiport Stephen Hemminger
8 siblings, 0 replies; 131+ messages in thread
From: spinler @ 2026-02-13 18:53 UTC (permalink / raw)
To: dev; +Cc: Martin Spinler
From: Martin Spinler <spinler@cesnet.cz>
Report the firmware version, which is described
by project name and project version.
Signed-off-by: Martin Spinler <spinler@cesnet.cz>
---
drivers/net/nfb/nfb_ethdev.c | 27 +++++++++++++++++++++++++++
1 file changed, 27 insertions(+)
diff --git a/drivers/net/nfb/nfb_ethdev.c b/drivers/net/nfb/nfb_ethdev.c
index 1b481c0312..a3b9565e54 100644
--- a/drivers/net/nfb/nfb_ethdev.c
+++ b/drivers/net/nfb/nfb_ethdev.c
@@ -498,6 +498,32 @@ nfb_eth_mac_addr_remove(struct rte_eth_dev *dev, uint32_t index)
nc_rxmac_set_mac(internals->rxmac[i], index, 0, 0);
}
+static int
+nfb_eth_fw_version_get(struct rte_eth_dev *dev, char *fw_version,
+ size_t fw_size)
+{
+ int ret;
+ const char *proj_name, *proj_vers;
+ struct pmd_internals *intl = dev->process_private;
+
+ proj_name = nc_info_get_fw_project_name(intl->nfb, NULL);
+ proj_vers = nc_info_get_fw_project_version(intl->nfb, NULL);
+
+ if (proj_name == NULL)
+ proj_name = "";
+ if (proj_vers == NULL)
+ proj_vers = "";
+
+ ret = snprintf(fw_version, fw_size, "%s;%s", proj_name, proj_vers);
+ if (ret < 0)
+ return ret;
+
+ if (ret >= (signed int)fw_size)
+ return strlen(proj_name) + 1 + strlen(proj_vers) + 1;
+
+ return 0;
+}
+
static const struct eth_dev_ops ops = {
.dev_start = nfb_eth_dev_start,
.dev_stop = nfb_eth_dev_stop,
@@ -524,6 +550,7 @@ static const struct eth_dev_ops ops = {
.mac_addr_set = nfb_eth_mac_addr_set,
.mac_addr_add = nfb_eth_mac_addr_add,
.mac_addr_remove = nfb_eth_mac_addr_remove,
+ .fw_version_get = nfb_eth_fw_version_get,
};
/**
--
2.53.0
^ permalink raw reply related [flat|nested] 131+ messages in thread
* [PATCH v8 8/8] doc/nfb: cleanup and update guide
2026-02-13 18:53 ` [PATCH v8 " spinler
` (6 preceding siblings ...)
2026-02-13 18:53 ` [PATCH v8 7/8] net/nfb: report firmware version spinler
@ 2026-02-13 18:53 ` spinler
2026-02-13 19:39 ` [PATCH v8 0/8] net/nfb: rework to real multiport Stephen Hemminger
8 siblings, 0 replies; 131+ messages in thread
From: spinler @ 2026-02-13 18:53 UTC (permalink / raw)
To: dev; +Cc: Martin Spinler
From: Martin Spinler <spinler@cesnet.cz>
Clean up documentation and update the guide with:
- Current card list with firmware download links
- Updated port argument description
Signed-off-by: Martin Spinler <spinler@cesnet.cz>
---
doc/guides/nics/features/nfb.ini | 4 +
doc/guides/nics/nfb.rst | 187 +++++++++++--------------
doc/guides/rel_notes/release_26_03.rst | 6 +
3 files changed, 95 insertions(+), 102 deletions(-)
diff --git a/doc/guides/nics/features/nfb.ini b/doc/guides/nics/features/nfb.ini
index c77b592789..7ac89fc15f 100644
--- a/doc/guides/nics/features/nfb.ini
+++ b/doc/guides/nics/features/nfb.ini
@@ -9,9 +9,13 @@ Link status = Y
Queue start/stop = Y
Promiscuous mode = Y
Allmulticast mode = Y
+Unicast MAC filter = Y
+Timestamp offload = P
Basic stats = Y
Extended stats = Y
Stats per queue = Y
+FW version = Y
+Multiprocess aware = Y
Linux = Y
x86-64 = Y
Usage doc = Y
diff --git a/doc/guides/nics/nfb.rst b/doc/guides/nics/nfb.rst
index a9b4049654..55a635d8d4 100644
--- a/doc/guides/nics/nfb.rst
+++ b/doc/guides/nics/nfb.rst
@@ -2,64 +2,101 @@
Copyright 2019 Cesnet
Copyright 2019 Netcope Technologies
-NFB poll mode driver library
-=================================
+NFB Poll Mode Driver
+====================
+
+The NFB PMD implements support for the FPGA-based
+programmable NICs running `CESNET-NDK <https://www.liberouter.org/ndk/>`_
+based firmware (formerly known as the NetCOPE platform). The CESNET Network Development Kit offers
+wide spectrum of supported cards, for example:
+N6010, FB2CGG3 (Silicom Denmark),
+IA-420F, IA-440i (BittWare),
+AGI-FH400G (ReflexCES),
+and `many more <https://github.com/CESNET/ndk-fpga/tree/devel/cards>`_.
+
+The CESNET-NDK framework is open source and
+can be found on `CESNET-NDK <https://github.com/CESNET/ndk-fpga>`_.
+Ready-to-use demo firmwares can be found on the `DYNANIC page <https://dyna-nic.com/try-now/>`_.
+
+Software compatibility and firmware for
+`historical cards <https://www.liberouter.org/technologies/cards/>`_
+are left unmaintained.
+
+Software prerequisites
+----------------------
-The NFB poll mode driver library implements support for the Netcope
-FPGA Boards (**NFB-40G2, NFB-100G2, NFB-200G2QL**) and Silicom **FB2CGG3** card,
-FPGA-based programmable NICs. The NFB PMD uses interface provided by the libnfb
-library to communicate with these cards over the nfb layer.
+This PMD requires a Linux kernel module, which is responsible for initialization and
+allocation of resources needed for the nfb layer function.
+Communication between PMD and kernel modules is mediated by the libnfb library.
+The kernel module and library are not part of DPDK and must be installed
+separately. Dependencies can be found on GitHub:
+`nfb-framework <https://github.com/CESNET/ndk-sw>`_ as source code,
+or for RPM-based distributions, the prebuilt `nfb-framework` package on
+`Fedora Copr <https://copr.fedorainfracloud.org/coprs/g/CESNET/nfb-framework/>`_.
-More information about the
-`NFB cards <https://www.liberouter.org/technologies/cards/>`_
-and used technology
-(`Network Development Kit <https://www.liberouter.org/ndk/>`_)
-can be found on the `Liberouter website <http://www.liberouter.org/>`_.
+Before starting the DPDK, make sure that the kernel module is loaded (`sudo modprobe nfb`)
+and the card is running the CESNET-NDK based firmware (`nfb-info -l`).
.. note::
- Currently the driver is supported only on x86_64 architectures.
- Only x86_64 versions of the external libraries are provided.
+ Currently, the driver is supported only on x86_64 architectures.
+
+NFB card architecture
+---------------------
+
+Ethernet Ports
+~~~~~~~~~~~~~~
+
+The NFB cards are multi-port multi-queue cards, where (generally) data from any
+Ethernet port may be sent by the firmware to any queue.
-Prerequisites
--------------
+The cards were historically represented in DPDK as a single port.
+Currently each Ethernet channel is represented as one DPDK port.
-This PMD requires kernel modules which are responsible for initialization and
-allocation of resources needed for nfb layer function.
-Communication between PMD and kernel modules is mediated by libnfb library.
-These kernel modules and library are not part of DPDK and must be installed
-separately:
+.. note::
-* **libnfb library**
+ Normally, one port corresponds to one channel, but ports can often be configured in a separate
+ manner. For example one 100G port can be used as 4x25G or 4x10G independent Ethernet channels.
- The library provides API for initialization of nfb transfers, receiving and
- transmitting data segments.
+By default, all ports are initialized and used for the allowed PCI device.
+When this behaviour is limiting (e.g., for multiple instances of DPDK app on different ports
+of the same PCI device), ports can be specified by the `port` item in the `allow` argument:
-* **Kernel modules**
+.. code-block:: console
- * nfb
+ -a 0000:01:00.0,port=0,port=3
- Kernel modules manage initialization of hardware, allocation and
- sharing of resources for user space applications.
+PCIe slots
+~~~~~~~~~~
-Dependencies can be found here:
-`Netcope common <https://github.com/CESNET/ndk-sw>`_.
+Some cards employ more than one PCIe device for better data throughput. This can be achieved by
+slot bifurcation (only a minor improvement) or by an add-on cable connected to another PCIe slot.
+Both improvements can work together, as is, for example, in the case of the AGI-FH400G card.
-Versions of the packages
-~~~~~~~~~~~~~~~~~~~~~~~~
+Because primary and secondary slot(s) can be attached to different NUMA nodes
+(also applies for bifurcation on some HW), the data structures need to be correctly allocated.
+(Device-aware allocation matters also on IOMMU-enabled systems.)
+The firmware already provides DMA queue to PCI device mapping. The DPDK application just needs to
+use all PCI devices, otherwise some queues will not be available; provide all PCI endpoints
+listed in the `nfb-info -v` in the `allow` argument.
-The minimum version of the provided packages:
+.. note::
-* for DPDK from 19.05
+ For cards where the number of Ethernet ports is less than the number of PCI devices
+ (e.g., AGI-FH400G: 1 port, up to 4 PCI devices), the virtual DPDK ports are
+ created to achieve the best NUMA-aware throughput
+ (virtual ports lack a lot of configuration features).
-Configuration
--------------
+Features
+--------
Timestamps
+~~~~~~~~~~
-The PMD supports hardware timestamps of frame receipt on physical network interface. In order to use
-the timestamps, the hardware timestamping unit must be enabled (follow the documentation of the NFB
-products). The standard `RTE_ETH_RX_OFFLOAD_TIMESTAMP` flag can be used for this feature.
+The PMD supports hardware timestamps of frame receipt on physical network interface.
+In order to use the timestamps, the hardware timestamping unit must be enabled
+(follow the documentation of the NFB products).
+The standard `RTE_ETH_RX_OFFLOAD_TIMESTAMP` flag can be used for this feature.
When the timestamps are enabled, a timestamp validity flag is set in the MBUFs
containing received frames and timestamp is inserted into the `rte_mbuf` struct.
@@ -67,72 +104,18 @@ containing received frames and timestamp is inserted into the `rte_mbuf` struct.
The timestamp is an `uint64_t` field and holds the number of nanoseconds
elapsed since 1.1.1970 00:00:00 UTC.
+Simulation
+~~~~~~~~~~
-Using the NFB PMD
-----------------------
-
-Kernel modules have to be loaded before running the DPDK application.
-
-NFB card architecture
----------------------
-
-The NFB cards are multi-port multi-queue cards, where (generally) data from any
-Ethernet port may be sent to any queue.
-They are represented in DPDK as a single port.
-
-NFB-200G2QL card employs an add-on cable which allows to connect it to two
-physical PCI-E slots at the same time (see the diagram below).
-This is done to allow 200 Gbps of traffic to be transferred through the PCI-E
-bus (note that a single PCI-E 3.0 x16 slot provides only 125 Gbps theoretical
-throughput).
-
-Although each slot may be connected to a different CPU and therefore to a different
-NUMA node, the card is represented as a single port in DPDK. To work with data
-from the individual queues on the right NUMA node, connection of NUMA nodes on
-first and last queue (each NUMA node has half of the queues) need to be checked.
-
-Limitations
------------
-
-Driver is usable only on Linux architecture, namely on CentOS.
-
-Since a card is always represented as a single port, but can be connected to two
-NUMA nodes, there is need for manual check where master/slave is connected.
-
-Example of usage
-----------------
-
-Read packets from 0. and 1. receive queue and write them to 0. and 1.
-transmit queue:
+The CESNET-NDK framework offers the possibility of simulating the firmware together with DPDK.
+This allows for easy debugging of a packet flow behaviour with a specific firmware configuration.
+The DPDK NFB driver can be connected to the simulator (Questa/ModelSim/nvc) via a virtual device:
.. code-block:: console
- ./<build_dir>/app/dpdk-testpmd -l 0-3 -n 2 \
- -- --port-topology=chained --rxq=2 --txq=2 --nb-cores=2 -i -a
-
-Example output:
-
-.. code-block:: console
+ dpdk-testpmd
+ --vdev=eth_vdev_nfb,dev=libnfb-ext-grpc.so:grpc+dma_vas:localhost:50051,queue_driver=native
+ --iova-mode=va -- -i
- [...]
- EAL: PCI device 0000:06:00.0 on NUMA socket -1
- EAL: probe driver: 1b26:c1c1 net_nfb
- PMD: Initializing NFB device (0000:06:00.0)
- PMD: Available DMA queues RX: 8 TX: 8
- PMD: NFB device (0000:06:00.0) successfully initialized
- Interactive-mode selected
- Auto-start selected
- Configuring Port 0 (socket 0)
- Port 0: 00:11:17:00:00:00
- Checking link statuses...
- Port 0 Link Up - speed 10000 Mbps - full-duplex
- Done
- Start automatic packet forwarding
- io packet forwarding - CRC stripping disabled - packets/burst=32
- nb forwarding cores=2 - nb forwarding ports=1
- RX queues=2 - RX desc=128 - RX free threshold=0
- RX threshold registers: pthresh=0 hthresh=0 wthresh=0
- TX queues=2 - TX desc=512 - TX free threshold=0
- TX threshold registers: pthresh=0 hthresh=0 wthresh=0
- TX RS bit threshold=0 - TXQ flags=0x0
- testpmd>
+More info about the simulation can be found int the CESNET-NDK `documentation
+<https://cesnet.github.io/ndk-fpga/devel/ndk_apps/minimal/tests/cocotb/readme.html>`_.
diff --git a/doc/guides/rel_notes/release_26_03.rst b/doc/guides/rel_notes/release_26_03.rst
index ff02c294d0..7678cf1a49 100644
--- a/doc/guides/rel_notes/release_26_03.rst
+++ b/doc/guides/rel_notes/release_26_03.rst
@@ -62,6 +62,12 @@ New Features
* **Updated CESNET NFB ethernet driver.**
* The timestamp value has been updated to make it usable.
+ * The DPDK port now represents just one Ethernet port instead of all Ethernet ports on the NIC.
+ * All ports are used by default, but a subset can be selected using the ``port`` argument.
+ * Report firmware version.
+ * Common CESNET-NDK-based adapters have been added,
+ including the FB2CGHH (Silicom Denmark) and XpressSX AGI-FH400G (Reflex CES).
+
Removed Items
-------------
--
2.53.0
^ permalink raw reply related [flat|nested] 131+ messages in thread
* Re: [PATCH v7 0/8] net/nfb: rework to real multiport
2026-02-10 0:35 ` [PATCH v7 0/8] net/nfb: rework to real multiport Stephen Hemminger
@ 2026-02-13 18:53 ` Martin Špinler
0 siblings, 0 replies; 131+ messages in thread
From: Martin Špinler @ 2026-02-13 18:53 UTC (permalink / raw)
To: Stephen Hemminger; +Cc: dev
> Patch 1/8 (Queue Mapping):
> Missing NULL checks before accessing queue_map_rx/tx arrays
> (could crash in secondary process)
Fixed with the new "ready" bit in device private struct.
However, I'm not sure, if/how the secondary process can pass init
before the primary.
> Patch 2/8 (Core Refactoring):
> Resource leak: nfb_dev not closed on error path
Nonsense, the nfb_close is already exactly there.
> Partial device cleanup missing on errors
The title and partially the description doesn't make sense.
The cleanup in nfb_eth_common_remove() for failed
nfb_eth_dev_create_for_ifc() is clear in my opinion.
All other problems fixed in v8 and mentioned in cover letter, thanks.
^ permalink raw reply [flat|nested] 131+ messages in thread
* Re: [PATCH v7 0/8] net/nfb: rework to real multiport
2026-02-12 18:35 ` Stephen Hemminger
@ 2026-02-13 18:53 ` Martin Špinler
0 siblings, 0 replies; 131+ messages in thread
From: Martin Špinler @ 2026-02-13 18:53 UTC (permalink / raw)
To: Stephen Hemminger; +Cc: dev
> Improved the AI review process and it saw this.
> I think you should fix and retest before merge to next-net.
I've retested patch v8 on hw.
> Patch 3/8: net/nfb: add vdev as alternative device probe method
> Warning: nfb_default_dev_path() used without NULL check
This is false.
The main problem from this thread: Double TAILQ_REMOVE is fixed.
Other issues were yet reported by AI in prev thread (also fixed).
^ permalink raw reply [flat|nested] 131+ messages in thread
* Re: [PATCH v8 2/8] net/nfb: create one ethdev per ethernet port
2026-02-13 18:53 ` [PATCH v8 2/8] net/nfb: create one ethdev per ethernet port spinler
@ 2026-02-13 19:33 ` Stephen Hemminger
0 siblings, 0 replies; 131+ messages in thread
From: Stephen Hemminger @ 2026-02-13 19:33 UTC (permalink / raw)
To: spinler; +Cc: dev
On Fri, 13 Feb 2026 19:53:11 +0100
spinler@cesnet.cz wrote:
> +__rte_internal
> +int nfb_eth_common_probe(struct nfb_probe_params *params);
> +__rte_internal
> +int nfb_eth_common_remove(struct rte_device *dev);
> +
Not sure of __rte_internal is needed on driver only functions.
Other drivers don't do it. The purpose of __rte_internal is to guard
against applications randomly picking up functions out of exported
headers. Since this header is not exposed it should not be an issue.
^ permalink raw reply [flat|nested] 131+ messages in thread
* Re: [PATCH v8 0/8] net/nfb: rework to real multiport
2026-02-13 18:53 ` [PATCH v8 " spinler
` (7 preceding siblings ...)
2026-02-13 18:53 ` [PATCH v8 8/8] doc/nfb: cleanup and update guide spinler
@ 2026-02-13 19:39 ` Stephen Hemminger
2026-02-13 20:13 ` Martin Špinler
8 siblings, 1 reply; 131+ messages in thread
From: Stephen Hemminger @ 2026-02-13 19:39 UTC (permalink / raw)
To: spinler; +Cc: dev
On Fri, 13 Feb 2026 19:53:09 +0100
spinler@cesnet.cz wrote:
> From: Martin Spinler <spinler@cesnet.cz>
>
> This series implements real multiport for better user experience.
>
> The existing driver creates one ethdev/port for one PCI device.
> As the CESNET-NDK based cards aren't capable to represent each
> Ethernet port by own PCI device, new driver implementation
> processes real port configuration from firmware/card and switches
> from rte_eth_dev_pci_generic_probe to multiple rte_eth_dev_create calls.
>
> ---
There is one copy-paste bug (found by AI review).
I can fix that during merge.
Summary of Findings
Patch Severity Issue
2/8 Error TX queue map bounds check uses max_rx_queues instead of max_tx_queues — copy-paste bug
2/8 Warning __rte_internal / RTE_EXPORT_INTERNAL_SYMBOL misuse for driver-internal functions
5/8 Warning err_map_inconsistent error path leaks already-opened MAC handles
Full report
# Deep Dive Review: net/nfb multiport patch series (v8, 8 patches)
**Author:** Martin Spinler <spinler@cesnet.cz>
**Series:** [PATCH v8 1/8] through [PATCH v8 8/8]
**Reviewer:** AI review per AGENTS.md guidelines
---
## Changes from v7
Several issues from the v7 review have been addressed:
- **Fixed:** The double `TAILQ_REMOVE` bug — `nfb_eth_common_remove()` no longer removes from the list; only `nfb_eth_dev_uninit()` does.
- **Fixed:** Buffer overflow in queue map filling — bounds checks added with `cnt >= max_rx_queues` guard and `goto err_queue_map`.
- **Fixed:** `strtoul` base changed from 0 to 10, preventing octal/hex port numbers.
- **Fixed:** Bounds check added in `nfb_nc_eth_init()` for MAC mapping consistency.
- **New:** Added `priv->ready` flag for secondary process safety in patch 1.
- **New:** Added bounds checks in `nfb_eth_rx_queue_setup` and `nfb_eth_tx_queue_setup` (`rx_queue_id >= priv->max_rx_queues`).
- **New:** Added NULL check on `rte_eth_dev_get_by_name()` return in `nfb_eth_dev_create_for_ifc`.
- **New:** Added `ifc_params.ret` field to pass back error codes from `rte_kvargs_process` callback.
- **New:** Added `errno = 0` before `strtoul` and `errno` check after.
---
## Patch 2/8: net/nfb: create one ethdev per ethernet port
### Error: TX queue map bounds check uses `max_rx_queues` instead of `max_tx_queues`
In the TX queue mapping loop:
```c
cnt = 0;
for (i = 0; i < mi->txq_cnt; i++) {
if (mi->txq[i].ifc == ifc->id) {
if (cnt >= max_rx_queues) { /* <--- BUG: should be max_tx_queues */
NFB_LOG(ERR, "TX queue mapping inconsistent");
ret = -EINVAL;
goto err_queue_map;
}
priv->queue_map_tx[cnt++] = mi->txq[i].id;
}
}
```
The bounds check compares `cnt` against `max_rx_queues` but this is filling `queue_map_tx` which was allocated with `max_tx_queues` slots. If `max_tx_queues < max_rx_queues`, this allows writing past the end of the TX portion of the buffer. If `max_tx_queues > max_rx_queues`, it rejects valid mappings prematurely.
**Suggested fix:** Change `max_rx_queues` to `max_tx_queues`:
```c
if (cnt >= max_tx_queues) {
```
### Warning: `__rte_internal` + `RTE_EXPORT_INTERNAL_SYMBOL` for driver-internal functions
`nfb.h` declares `nfb_eth_common_probe` and `nfb_eth_common_remove` with `__rte_internal`, and the `.c` file uses `RTE_EXPORT_INTERNAL_SYMBOL`. These functions are shared between `nfb_ethdev.c` and `nfb_vdev.c` within the same driver — they are not cross-library internal symbols. `__rte_internal` is meant for functions exported between DPDK libraries/drivers via the linker map, not for functions shared within a single PMD. Plain `extern` declarations would be more appropriate.
---
## Patch 5/8: net/nfb: init only MACs associated with device
### Warning: `err_map_inconsistent` path doesn't close already-opened MACs
In `nfb_nc_eth_init()`, if the bounds check triggers:
```c
if (rxm >= ifc->eth_cnt || txm >= ifc->eth_cnt) {
NFB_LOG(ERR, "MAC mapping inconsistent");
ret = -EINVAL;
goto err_map_inconsistent;
}
```
The error path frees `intl->txmac` and `intl->rxmac` arrays but does not close any MAC handles already opened via `nc_rxmac_open` / `nc_txmac_open` in previous loop iterations. These handles would be leaked.
**Suggested fix:** Close all opened MACs before freeing the arrays:
```c
err_map_inconsistent:
for (int j = 0; j < txm; j++)
nc_txmac_close(intl->txmac[j]);
for (int j = 0; j < rxm; j++)
nc_rxmac_close(intl->rxmac[j]);
free(intl->txmac);
err_alloc_txmac:
free(intl->rxmac);
err_alloc_rxmac:
return ret;
```
Alternatively, set `intl->max_rxmac = rxm; intl->max_txmac = txm;` before the goto and call `nfb_nc_eth_deinit(intl)` instead.
---
## Summary of Findings
| Patch | Severity | Issue |
|-------|----------|-------|
| 2/8 | **Error** | TX queue map bounds check uses `max_rx_queues` instead of `max_tx_queues` — copy-paste bug |
| 2/8 | Warning | `__rte_internal` / `RTE_EXPORT_INTERNAL_SYMBOL` misuse for driver-internal functions |
| 5/8 | Warning | `err_map_inconsistent` error path leaks already-opened MAC handles |
^ permalink raw reply [flat|nested] 131+ messages in thread
* Re: [PATCH v8 0/8] net/nfb: rework to real multiport
2026-02-13 19:39 ` [PATCH v8 0/8] net/nfb: rework to real multiport Stephen Hemminger
@ 2026-02-13 20:13 ` Martin Špinler
0 siblings, 0 replies; 131+ messages in thread
From: Martin Špinler @ 2026-02-13 20:13 UTC (permalink / raw)
To: Stephen Hemminger; +Cc: dev
> There is one copy-paste bug (found by AI review).
> I can fix that during merge.
Oh man, I didn't notice that.
> 2/8 Warning __rte_internal / RTE_EXPORT_INTERNAL_SYMBOL
> misuse for driver-internal functions
The missing __rte_internal was an error for AI earlier (some drivers
use it too, but rarely).
So if your final verdict is no __rte_internal,
I will remove that, Okay?
> 5/8 Warning err_map_inconsistent error path leaks
> already-opened MAC handles
Oh no, again, missed that.
I have to fix this and resubmit as v9.
^ permalink raw reply [flat|nested] 131+ messages in thread
* [PATCH v9 0/8] net/nfb: rework to real multiport
2026-01-15 15:16 [PATCH 0/8] net/nfb: rework to real multiport spinler
` (15 preceding siblings ...)
2026-02-13 18:53 ` [PATCH v8 " spinler
@ 2026-02-16 16:24 ` spinler
2026-02-16 16:24 ` [PATCH v9 1/8] net/nfb: prepare for indirect queue mapping scheme spinler
` (8 more replies)
2026-02-17 7:10 ` [PATCH v10 " spinler
17 siblings, 9 replies; 131+ messages in thread
From: spinler @ 2026-02-16 16:24 UTC (permalink / raw)
To: dev; +Cc: Martin Spinler
From: Martin Spinler <spinler@cesnet.cz>
This series implements real multiport for better user experience.
The existing driver creates one ethdev/port for one PCI device.
As the CESNET-NDK based cards aren't capable to represent each
Ethernet port by own PCI device, new driver implementation
processes real port configuration from firmware/card and switches
from rte_eth_dev_pci_generic_probe to multiple rte_eth_dev_create calls.
---
Depends-on: patch-37245 ("doc/nfb: update release notes for nfb driver")
v9:
* fixed error path in nfb_nc_eth_init() that did not close MAC handles
* fixed wrong variable usage (max_rx_queues instead of max_tx_queues)
* removed __rte_internal + RTE_EXPORT_INTERNAL_SYMBOL for
driver-internal functions
Martin Spinler (8):
net/nfb: prepare for indirect queue mapping scheme
net/nfb: create one ethdev per ethernet port
net/nfb: add vdev as alternative device probe method
net/nfb: add device argument "port" to limit used ports
net/nfb: init only MACs associated with device
net/nfb: add compatible cards to driver PCI ID table
net/nfb: report firmware version
doc/nfb: cleanup and update guide
doc/guides/nics/features/nfb.ini | 4 +
doc/guides/nics/nfb.rst | 187 +++++-----
doc/guides/rel_notes/release_26_03.rst | 6 +
drivers/net/nfb/meson.build | 1 +
drivers/net/nfb/nfb.h | 55 ++-
drivers/net/nfb/nfb_ethdev.c | 489 +++++++++++++++++++------
drivers/net/nfb/nfb_rx.c | 33 +-
drivers/net/nfb/nfb_rx.h | 9 +-
drivers/net/nfb/nfb_tx.c | 30 +-
drivers/net/nfb/nfb_tx.h | 7 +-
drivers/net/nfb/nfb_vdev.c | 107 ++++++
11 files changed, 672 insertions(+), 256 deletions(-)
create mode 100644 drivers/net/nfb/nfb_vdev.c
--
2.53.0
^ permalink raw reply [flat|nested] 131+ messages in thread
* [PATCH v9 1/8] net/nfb: prepare for indirect queue mapping scheme
2026-02-16 16:24 ` [PATCH v9 " spinler
@ 2026-02-16 16:24 ` spinler
2026-02-16 16:25 ` [PATCH v9 2/8] net/nfb: create one ethdev per ethernet port spinler
` (7 subsequent siblings)
8 siblings, 0 replies; 131+ messages in thread
From: spinler @ 2026-02-16 16:24 UTC (permalink / raw)
To: dev; +Cc: Martin Spinler
From: Martin Spinler <spinler@cesnet.cz>
The NFB driver is being enhanced to create one ethdev per physical
Ethernet port instead of one ethdev per PCI device. This requires
an indirect mapping layer because DPDK queue indices no longer
directly correspond to firmware queue indices.
This change adds queue_map_rx and queue_map_tx arrays to track the
mapping between DPDK queue indices and firmware queue IDs, preparing
for the multi-port implementation in subsequent patches.
Signed-off-by: Martin Spinler <spinler@cesnet.cz>
---
drivers/net/nfb/nfb.h | 6 ++++++
drivers/net/nfb/nfb_ethdev.c | 28 ++++++++++++++++++++++++++++
drivers/net/nfb/nfb_rx.c | 33 ++++++++++++++++++---------------
drivers/net/nfb/nfb_rx.h | 9 ++++-----
drivers/net/nfb/nfb_tx.c | 30 ++++++++++++++++++------------
drivers/net/nfb/nfb_tx.h | 7 +++----
6 files changed, 77 insertions(+), 36 deletions(-)
diff --git a/drivers/net/nfb/nfb.h b/drivers/net/nfb/nfb.h
index 90b04c6151..fa51afc7de 100644
--- a/drivers/net/nfb/nfb.h
+++ b/drivers/net/nfb/nfb.h
@@ -60,6 +60,12 @@ struct pmd_internals {
struct pmd_priv {
uint16_t max_rx_queues;
uint16_t max_tx_queues;
+
+ /** Mapping from DPDK RX queue index to firmware queue ID */
+ int *queue_map_rx;
+ /** Mapping from DPDK TX queue index to firmware queue ID */
+ int *queue_map_tx;
+ bool ready; /**< This structure is initialized for usage in secondary process */
};
#endif /* _NFB_H_ */
diff --git a/drivers/net/nfb/nfb_ethdev.c b/drivers/net/nfb/nfb_ethdev.c
index 967f127f40..c0a2905249 100644
--- a/drivers/net/nfb/nfb_ethdev.c
+++ b/drivers/net/nfb/nfb_ethdev.c
@@ -509,6 +509,7 @@ static const struct eth_dev_ops ops = {
static int
nfb_eth_dev_init(struct rte_eth_dev *dev)
{
+ int i;
int ret;
uint32_t mac_count;
struct rte_eth_dev_data *data = dev->data;
@@ -577,6 +578,20 @@ nfb_eth_dev_init(struct rte_eth_dev *dev)
priv->max_rx_queues = max_rx_queues;
priv->max_tx_queues = max_tx_queues;
+ priv->queue_map_rx = rte_calloc("NFB queue map", max_rx_queues + max_tx_queues,
+ sizeof(*priv->queue_map_rx), 0);
+ if (priv->queue_map_rx == NULL) {
+ ret = -ENOMEM;
+ goto err_alloc_queue_map;
+ }
+ priv->queue_map_tx = priv->queue_map_rx + max_rx_queues;
+
+ /* default queue mapping is 1:1 */
+ for (i = 0; i < max_rx_queues; i++)
+ priv->queue_map_rx[i] = i;
+ for (i = 0; i < max_tx_queues; i++)
+ priv->queue_map_tx[i] = i;
+
/* Allocate space for MAC addresses */
mac_count = nfb_eth_get_max_mac_address_count(dev);
data->mac_addrs = rte_zmalloc(data->name,
@@ -599,6 +614,12 @@ nfb_eth_dev_init(struct rte_eth_dev *dev)
data->all_multicast = nfb_eth_allmulticast_get(dev);
data->dev_flags |= RTE_ETH_DEV_AUTOFILL_QUEUE_XSTATS;
+ priv->ready = true;
+ } else {
+ if (!priv->ready) {
+ ret = -EBADFD;
+ goto err_secondary_not_ready;
+ }
}
NFB_LOG(INFO, "NFB device (" PCI_PRI_FMT ") successfully initialized",
@@ -608,6 +629,9 @@ nfb_eth_dev_init(struct rte_eth_dev *dev)
return 0;
err_malloc_mac_addrs:
+ rte_free(priv->queue_map_rx);
+err_alloc_queue_map:
+err_secondary_not_ready:
nfb_nc_rxmac_deinit(internals->rxmac, internals->max_rxmac);
nfb_nc_txmac_deinit(internals->txmac, internals->max_txmac);
nfb_close(internals->nfb);
@@ -633,6 +657,10 @@ nfb_eth_dev_uninit(struct rte_eth_dev *dev)
struct rte_pci_device *pci_dev = RTE_ETH_DEV_TO_PCI(dev);
struct rte_pci_addr *pci_addr = &pci_dev->addr;
struct pmd_internals *internals = dev->process_private;
+ struct pmd_priv *priv = dev->data->dev_private;
+
+ if (rte_eal_process_type() == RTE_PROC_PRIMARY)
+ rte_free(priv->queue_map_rx);
nfb_nc_rxmac_deinit(internals->rxmac, internals->max_rxmac);
nfb_nc_txmac_deinit(internals->txmac, internals->max_txmac);
diff --git a/drivers/net/nfb/nfb_rx.c b/drivers/net/nfb/nfb_rx.c
index 413d275853..5b8579e280 100644
--- a/drivers/net/nfb/nfb_rx.c
+++ b/drivers/net/nfb/nfb_rx.c
@@ -61,12 +61,16 @@ nfb_eth_rx_queue_setup(struct rte_eth_dev *dev,
struct rte_mempool *mb_pool)
{
struct pmd_internals *internals = dev->process_private;
+ struct pmd_priv *priv = dev->data->dev_private;
- struct ndp_rx_queue *rxq;
int ret;
+ int qid;
+ struct ndp_rx_queue *rxq;
+
+ if (rx_queue_id >= priv->max_rx_queues)
+ return -EINVAL;
- rxq = rte_zmalloc_socket("ndp rx queue",
- sizeof(struct ndp_rx_queue),
+ rxq = rte_zmalloc_socket("ndp rx queue", sizeof(struct ndp_rx_queue),
RTE_CACHE_LINE_SIZE, socket_id);
if (rxq == NULL) {
@@ -77,23 +81,23 @@ nfb_eth_rx_queue_setup(struct rte_eth_dev *dev,
rxq->flags = 0;
- ret = nfb_eth_rx_queue_init(internals->nfb,
- rx_queue_id,
- dev->data->port_id,
- mb_pool,
- rxq);
+ qid = priv->queue_map_rx[rx_queue_id];
- if (ret == 0)
- dev->data->rx_queues[rx_queue_id] = rxq;
- else
- rte_free(rxq);
+ ret = nfb_eth_rx_queue_init(internals->nfb, qid, dev->data->port_id, mb_pool, rxq);
+ if (ret)
+ goto err_queue_init;
+
+ dev->data->rx_queues[rx_queue_id] = rxq;
+ return 0;
+err_queue_init:
+ rte_free(rxq);
return ret;
}
int
nfb_eth_rx_queue_init(struct nfb_device *nfb,
- uint16_t rx_queue_id,
+ int qid,
uint16_t port_id,
struct rte_mempool *mb_pool,
struct ndp_rx_queue *rxq)
@@ -104,12 +108,11 @@ nfb_eth_rx_queue_init(struct nfb_device *nfb,
if (nfb == NULL)
return -EINVAL;
- rxq->queue = ndp_open_rx_queue(nfb, rx_queue_id);
+ rxq->queue = ndp_open_rx_queue(nfb, qid);
if (rxq->queue == NULL)
return -EINVAL;
rxq->nfb = nfb;
- rxq->rx_queue_id = rx_queue_id;
rxq->in_port = port_id;
rxq->mb_pool = mb_pool;
rxq->buf_size = (uint16_t)(mbp_priv->mbuf_data_room_size -
diff --git a/drivers/net/nfb/nfb_rx.h b/drivers/net/nfb/nfb_rx.h
index 67b3b00e2a..831945c577 100644
--- a/drivers/net/nfb/nfb_rx.h
+++ b/drivers/net/nfb/nfb_rx.h
@@ -30,7 +30,6 @@ nfb_timestamp_dynfield(struct rte_mbuf *mbuf)
struct ndp_rx_queue {
struct nfb_device *nfb; /* nfb dev structure */
struct ndp_queue *queue; /* rx queue */
- uint16_t rx_queue_id; /* index */
uint8_t in_port; /* port */
uint8_t flags; /* setup flags */
@@ -47,8 +46,8 @@ struct ndp_rx_queue {
*
* @param nfb
* Pointer to nfb device structure.
- * @param rx_queue_id
- * RX queue index.
+ * @param qid
+ * RX queue ID.
* @param port_id
* Device [external] port identifier.
* @param mb_pool
@@ -60,7 +59,7 @@ struct ndp_rx_queue {
*/
int
nfb_eth_rx_queue_init(struct nfb_device *nfb,
- uint16_t rx_queue_id,
+ int qid,
uint16_t port_id,
struct rte_mempool *mb_pool,
struct ndp_rx_queue *rxq);
@@ -70,7 +69,7 @@ nfb_eth_rx_queue_init(struct nfb_device *nfb,
*
* @param dev
* Pointer to Ethernet device structure.
- * @param idx
+ * @param rx_queue_id
* RX queue index.
* @param desc
* Number of descriptors to configure in queue.
diff --git a/drivers/net/nfb/nfb_tx.c b/drivers/net/nfb/nfb_tx.c
index 1f997ce22f..1a7dcc3d30 100644
--- a/drivers/net/nfb/nfb_tx.c
+++ b/drivers/net/nfb/nfb_tx.c
@@ -54,11 +54,16 @@ nfb_eth_tx_queue_setup(struct rte_eth_dev *dev,
const struct rte_eth_txconf *tx_conf __rte_unused)
{
struct pmd_internals *internals = dev->process_private;
+ struct pmd_priv *priv = dev->data->dev_private;
+
int ret;
+ int qid;
struct ndp_tx_queue *txq;
- txq = rte_zmalloc_socket("ndp tx queue",
- sizeof(struct ndp_tx_queue),
+ if (tx_queue_id >= priv->max_tx_queues)
+ return -EINVAL;
+
+ txq = rte_zmalloc_socket("ndp tx queue", sizeof(struct ndp_tx_queue),
RTE_CACHE_LINE_SIZE, socket_id);
if (txq == NULL) {
@@ -67,32 +72,33 @@ nfb_eth_tx_queue_setup(struct rte_eth_dev *dev,
return -ENOMEM;
}
- ret = nfb_eth_tx_queue_init(internals->nfb,
- tx_queue_id,
- txq);
+ qid = priv->queue_map_tx[tx_queue_id];
- if (ret == 0)
- dev->data->tx_queues[tx_queue_id] = txq;
- else
- rte_free(txq);
+ ret = nfb_eth_tx_queue_init(internals->nfb, qid, txq);
+ if (ret)
+ goto err_queue_init;
+ dev->data->tx_queues[tx_queue_id] = txq;
+ return 0;
+
+err_queue_init:
+ rte_free(txq);
return ret;
}
int
nfb_eth_tx_queue_init(struct nfb_device *nfb,
- uint16_t tx_queue_id,
+ int qid,
struct ndp_tx_queue *txq)
{
if (nfb == NULL)
return -EINVAL;
- txq->queue = ndp_open_tx_queue(nfb, tx_queue_id);
+ txq->queue = ndp_open_tx_queue(nfb, qid);
if (txq->queue == NULL)
return -EINVAL;
txq->nfb = nfb;
- txq->tx_queue_id = tx_queue_id;
txq->tx_pkts = 0;
txq->tx_bytes = 0;
diff --git a/drivers/net/nfb/nfb_tx.h b/drivers/net/nfb/nfb_tx.h
index f107cf914b..c253af1a86 100644
--- a/drivers/net/nfb/nfb_tx.h
+++ b/drivers/net/nfb/nfb_tx.h
@@ -17,7 +17,6 @@
struct ndp_tx_queue {
struct nfb_device *nfb; /* nfb dev structure */
struct ndp_queue *queue; /* tx queue */
- uint16_t tx_queue_id; /* index */
volatile uint64_t tx_pkts; /* packets transmitted */
volatile uint64_t tx_bytes; /* bytes transmitted */
volatile uint64_t err_pkts; /* erroneous packets */
@@ -54,8 +53,8 @@ nfb_eth_tx_queue_setup(struct rte_eth_dev *dev,
*
* @param nfb
* Pointer to nfb device structure.
- * @param tx_queue_id
- * TX queue index.
+ * @param qid
+ * TX queue ID.
* @param[out] txq
* Pointer to ndp_tx_queue output structure
*
@@ -64,7 +63,7 @@ nfb_eth_tx_queue_setup(struct rte_eth_dev *dev,
*/
int
nfb_eth_tx_queue_init(struct nfb_device *nfb,
- uint16_t tx_queue_id,
+ int qid,
struct ndp_tx_queue *txq);
/**
--
2.53.0
^ permalink raw reply related [flat|nested] 131+ messages in thread
* [PATCH v9 2/8] net/nfb: create one ethdev per ethernet port
2026-02-16 16:24 ` [PATCH v9 " spinler
2026-02-16 16:24 ` [PATCH v9 1/8] net/nfb: prepare for indirect queue mapping scheme spinler
@ 2026-02-16 16:25 ` spinler
2026-02-16 16:25 ` [PATCH v9 3/8] net/nfb: add vdev as alternative device probe method spinler
` (6 subsequent siblings)
8 siblings, 0 replies; 131+ messages in thread
From: spinler @ 2026-02-16 16:25 UTC (permalink / raw)
To: dev; +Cc: Martin Spinler
From: Martin Spinler <spinler@cesnet.cz>
The existing driver creates one ethdev/port for one PCI device.
As the CESNET-NDK-based cards are mostly unable to represent
each physical port with one PCI device, this atypical behaviour
often limits the user.
The nfb-framework comes with generic port representation API,
which provides information about ports and their mapping to firmware
resource (physical ports, DMA queues, processing pipelines).
This driver design switches from one rte_eth_dev_pci_generic_probe call
to multiple rte_eth_dev_create calls, while parses the firmware mapping
information.
Signed-off-by: Martin Spinler <spinler@cesnet.cz>
---
drivers/net/nfb/nfb.h | 25 ++++
drivers/net/nfb/nfb_ethdev.c | 232 ++++++++++++++++++++++++++++-------
2 files changed, 216 insertions(+), 41 deletions(-)
diff --git a/drivers/net/nfb/nfb.h b/drivers/net/nfb/nfb.h
index fa51afc7de..a854d16426 100644
--- a/drivers/net/nfb/nfb.h
+++ b/drivers/net/nfb/nfb.h
@@ -11,6 +11,7 @@
#include <nfb/ndp.h>
#include <netcope/rxmac.h>
#include <netcope/txmac.h>
+#include <netcope/info.h>
extern int nfb_logtype;
#define RTE_LOGTYPE_NFB nfb_logtype
@@ -51,6 +52,9 @@ struct pmd_internals {
struct nc_rxmac *rxmac[RTE_MAX_NC_RXMAC];
struct nc_txmac *txmac[RTE_MAX_NC_TXMAC];
struct nfb_device *nfb;
+
+ TAILQ_ENTRY(pmd_internals) eth_dev_list; /**< Item in list of all devices */
+ struct rte_eth_dev *eth_dev; /**< Handle for matching the device being removed */
};
/*
@@ -68,4 +72,25 @@ struct pmd_priv {
bool ready; /**< This structure is initialized for usage in secondary process */
};
+/* Data for common device probing */
+struct nfb_probe_params {
+ /** Generic device information */
+ struct rte_device *device;
+ /** Port bus specific initialisation callback function */
+ ethdev_bus_specific_init specific_init;
+ /** Port bus specific initialisation parameters */
+ void *specific_device;
+
+ const char *path; /**< libnfb device string */
+ const char *args; /**< Device arguments */
+ int nfb_id; /**< ID of NFB device in system */
+ int ep_index; /**< (PCIe) endpoint index for matching assigned interfaces */
+
+ char name[RTE_DEV_NAME_MAX_LEN]; /**< Probed name (e.g. PCI device name) */
+};
+
+
+int nfb_eth_common_probe(struct nfb_probe_params *params);
+int nfb_eth_common_remove(struct rte_device *dev);
+
#endif /* _NFB_H_ */
diff --git a/drivers/net/nfb/nfb_ethdev.c b/drivers/net/nfb/nfb_ethdev.c
index c0a2905249..233eb91e18 100644
--- a/drivers/net/nfb/nfb_ethdev.c
+++ b/drivers/net/nfb/nfb_ethdev.c
@@ -4,6 +4,10 @@
* All rights reserved.
*/
+#include <sys/queue.h>
+#include <eal_export.h>
+#include <rte_tailq.h>
+
#include <nfb/nfb.h>
#include <nfb/ndp.h>
#include <netcope/rxmac.h>
@@ -18,6 +22,21 @@
#include "nfb_rxmode.h"
#include "nfb.h"
+struct nfb_ifc_create_params {
+ struct nfb_probe_params *probe_params;
+ struct nc_ifc_map_info map_info;
+ struct nc_ifc_info *ifc_info;
+
+ int basename_len; /* Cached real length of original probe_params->name */
+};
+
+/* The TAILQ entries are used for cleanup of allocated resources
+ * in situations, where dev_close is not called.
+ */
+TAILQ_HEAD(nfb_pmd_internals_head, pmd_internals);
+static struct nfb_pmd_internals_head nfb_eth_dev_list =
+ TAILQ_HEAD_INITIALIZER(nfb_eth_dev_list);
+
static int nfb_eth_dev_uninit(struct rte_eth_dev *dev);
/**
@@ -507,23 +526,20 @@ static const struct eth_dev_ops ops = {
* 0 on success, a negative errno value otherwise.
*/
static int
-nfb_eth_dev_init(struct rte_eth_dev *dev)
+nfb_eth_dev_init(struct rte_eth_dev *dev, void *init_data)
{
int i;
+ int cnt;
int ret;
uint32_t mac_count;
struct rte_eth_dev_data *data = dev->data;
struct pmd_internals *internals;
+ struct nfb_ifc_create_params *params = init_data;
+ struct nc_ifc_info *ifc = params->ifc_info;
+ struct nc_ifc_map_info *mi = ¶ms->map_info;
struct pmd_priv *priv = data->dev_private;
- struct rte_pci_device *pci_dev = RTE_ETH_DEV_TO_PCI(dev);
- struct rte_pci_addr *pci_addr = &pci_dev->addr;
struct rte_ether_addr eth_addr_init;
uint16_t max_rx_queues, max_tx_queues;
- char nfb_dev[PATH_MAX];
-
- NFB_LOG(INFO, "Initializing NFB device (" PCI_PRI_FMT ")",
- pci_addr->domain, pci_addr->bus, pci_addr->devid,
- pci_addr->function);
internals = rte_zmalloc_socket("nfb_internals",
sizeof(struct pmd_internals), RTE_CACHE_LINE_SIZE,
@@ -535,27 +551,17 @@ nfb_eth_dev_init(struct rte_eth_dev *dev)
dev->process_private = internals;
- snprintf(nfb_dev, sizeof(nfb_dev),
- "/dev/nfb/by-pci-slot/" PCI_PRI_FMT,
- pci_addr->domain, pci_addr->bus, pci_addr->devid,
- pci_addr->function);
-
- /*
- * Get number of available DMA RX and TX queues, which is maximum
- * number of queues that can be created and store it in private device
- * data structure.
- */
- internals->nfb = nfb_open(nfb_dev);
+ /* Open device handle */
+ internals->nfb = nfb_open(params->probe_params->path);
if (internals->nfb == NULL) {
- NFB_LOG(ERR, "nfb_open(): failed to open %s", nfb_dev);
+ NFB_LOG(ERR, "nfb_open(): failed to open %s", params->probe_params->path);
ret = -EINVAL;
goto err_nfb_open;
}
- max_rx_queues = ndp_get_rx_queue_available_count(internals->nfb);
- max_tx_queues = ndp_get_tx_queue_available_count(internals->nfb);
- NFB_LOG(INFO, "Available NDP queues RX: %u TX: %u",
- max_rx_queues, max_tx_queues);
+ /* Get number of available DMA RX and TX queues */
+ max_rx_queues = ifc->rxq_cnt;
+ max_tx_queues = ifc->txq_cnt;
nfb_nc_rxmac_init(internals->nfb,
internals->rxmac,
@@ -586,11 +592,30 @@ nfb_eth_dev_init(struct rte_eth_dev *dev)
}
priv->queue_map_tx = priv->queue_map_rx + max_rx_queues;
- /* default queue mapping is 1:1 */
- for (i = 0; i < max_rx_queues; i++)
- priv->queue_map_rx[i] = i;
- for (i = 0; i < max_tx_queues; i++)
- priv->queue_map_tx[i] = i;
+ /* Use queue mapping provided by libnfb */
+ cnt = 0;
+ for (i = 0; i < mi->rxq_cnt; i++) {
+ if (mi->rxq[i].ifc == ifc->id) {
+ if (cnt >= max_rx_queues) {
+ NFB_LOG(ERR, "RX queue mapping inconsistent");
+ ret = -EINVAL;
+ goto err_queue_map;
+ }
+ priv->queue_map_rx[cnt++] = mi->rxq[i].id;
+ }
+ }
+
+ cnt = 0;
+ for (i = 0; i < mi->txq_cnt; i++) {
+ if (mi->txq[i].ifc == ifc->id) {
+ if (cnt >= max_tx_queues) {
+ NFB_LOG(ERR, "TX queue mapping inconsistent");
+ ret = -EINVAL;
+ goto err_queue_map;
+ }
+ priv->queue_map_tx[cnt++] = mi->txq[i].id;
+ }
+ }
/* Allocate space for MAC addresses */
mac_count = nfb_eth_get_max_mac_address_count(dev);
@@ -622,13 +647,10 @@ nfb_eth_dev_init(struct rte_eth_dev *dev)
}
}
- NFB_LOG(INFO, "NFB device (" PCI_PRI_FMT ") successfully initialized",
- pci_addr->domain, pci_addr->bus, pci_addr->devid,
- pci_addr->function);
-
return 0;
err_malloc_mac_addrs:
+err_queue_map:
rte_free(priv->queue_map_rx);
err_alloc_queue_map:
err_secondary_not_ready:
@@ -654,25 +676,111 @@ nfb_eth_dev_init(struct rte_eth_dev *dev)
static int
nfb_eth_dev_uninit(struct rte_eth_dev *dev)
{
- struct rte_pci_device *pci_dev = RTE_ETH_DEV_TO_PCI(dev);
- struct rte_pci_addr *pci_addr = &pci_dev->addr;
struct pmd_internals *internals = dev->process_private;
struct pmd_priv *priv = dev->data->dev_private;
if (rte_eal_process_type() == RTE_PROC_PRIMARY)
rte_free(priv->queue_map_rx);
+ TAILQ_REMOVE(&nfb_eth_dev_list, internals, eth_dev_list);
+
nfb_nc_rxmac_deinit(internals->rxmac, internals->max_rxmac);
nfb_nc_txmac_deinit(internals->txmac, internals->max_txmac);
nfb_close(internals->nfb);
rte_free(internals);
- NFB_LOG(INFO, "NFB device (" PCI_PRI_FMT ") successfully uninitialized",
- pci_addr->domain, pci_addr->bus, pci_addr->devid,
- pci_addr->function);
+ return 0;
+}
+
+static int
+nfb_eth_dev_create_for_ifc(struct nfb_ifc_create_params *cp)
+{
+ int ret;
+ struct nc_ifc_info *ifc;
+ struct nfb_probe_params *pp;
+ struct rte_eth_dev *eth_dev;
+ struct pmd_internals *p;
+
+ ifc = cp->ifc_info;
+ pp = cp->probe_params;
+
+ /* Skip interfaces which doesn't belong to the probed PCI device */
+ if ((pp->ep_index != -1 && ifc->ep != pp->ep_index) ||
+ (ifc->flags & NC_IFC_INFO_FLAG_ACTIVE) == 0)
+ return 0;
+
+ ret = snprintf(pp->name + cp->basename_len, sizeof(pp->name) - cp->basename_len,
+ "_eth%d", ifc->id);
+ if (ret < 0 || ret >= (signed int)sizeof(pp->name) - cp->basename_len)
+ return -EINVAL;
+
+ ret = rte_eth_dev_create(pp->device, pp->name,
+ sizeof(struct pmd_priv),
+ pp->specific_init, pp->specific_device,
+ nfb_eth_dev_init, cp);
+ if (ret)
+ goto out;
+
+ eth_dev = rte_eth_dev_get_by_name(pp->name);
+ if (eth_dev == NULL) {
+ NFB_LOG(ERR, "Failed to get created device %s", pp->name);
+ ret = -ENODEV;
+ goto out;
+ }
+ p = eth_dev->process_private;
+ p->eth_dev = eth_dev;
+ TAILQ_INSERT_TAIL(&nfb_eth_dev_list, p, eth_dev_list);
+
+out:
+ /* return to original name (just for clarity) */
+ pp->name[cp->basename_len] = '\0';
+ return ret;
+}
+
+int
+nfb_eth_common_probe(struct nfb_probe_params *params)
+{
+ int i;
+ int ret;
+
+ struct nfb_device *nfb_dev;
+ struct nfb_ifc_create_params ifc_params;
+
+ /* Open the device handle just for parsing ifc_params.
+ * A separate handle is used later for each netdev.
+ */
+ nfb_dev = nfb_open(params->path);
+ if (nfb_dev == NULL) {
+ NFB_LOG(ERR, "nfb_open(): failed to open %s", params->path);
+ return -EINVAL;
+ }
+
+ ret = nc_ifc_map_info_create_ordinary(nfb_dev, &ifc_params.map_info);
+ if (ret)
+ goto err_map_info_create;
+
+ ifc_params.probe_params = params;
+ ifc_params.basename_len = strlen(params->name);
+
+ for (i = 0; i < ifc_params.map_info.ifc_cnt; i++) {
+ ifc_params.ifc_info = &ifc_params.map_info.ifc[i];
+ ret = nfb_eth_dev_create_for_ifc(&ifc_params);
+ if (ret)
+ goto err_dev_create;
+ }
+
+ nc_map_info_destroy(&ifc_params.map_info);
+ nfb_close(nfb_dev);
return 0;
+
+err_dev_create:
+ nfb_eth_common_remove(params->device);
+ nc_map_info_destroy(&ifc_params.map_info);
+err_map_info_create:
+ nfb_close(nfb_dev);
+ return ret;
}
static const struct rte_pci_id nfb_pci_id_table[] = {
@@ -701,8 +809,50 @@ static int
nfb_eth_pci_probe(struct rte_pci_driver *pci_drv __rte_unused,
struct rte_pci_device *pci_dev)
{
- return rte_eth_dev_pci_generic_probe(pci_dev,
- sizeof(struct pmd_priv), nfb_eth_dev_init);
+ int ret;
+ char path[PATH_MAX];
+
+ struct nc_composed_device_info comp_dev_info;
+ struct nfb_probe_params params = {0};
+
+ rte_pci_device_name(&pci_dev->addr, params.name, sizeof(params.name));
+
+ /*
+ * NFB device can be composed from multiple PCI devices,
+ * find the base char device ID for the current PCI device
+ */
+ ret = nc_get_composed_device_info_by_pci(NULL, params.name, &comp_dev_info);
+ if (ret) {
+ NFB_LOG(ERR, "Could not find NFB device for %s", params.name);
+ return -ENODEV;
+ }
+
+ ret = snprintf(path, sizeof(path), NFB_BASE_DEV_PATH "%d", comp_dev_info.nfb_id);
+ if (ret < 0 || ret >= (signed int)sizeof(path))
+ return -EINVAL;
+
+ params.args = pci_dev->device.devargs ? pci_dev->device.devargs->args : NULL;
+ params.path = path;
+ params.nfb_id = comp_dev_info.nfb_id;
+ params.ep_index = comp_dev_info.ep_index;
+
+ params.device = &pci_dev->device;
+ params.specific_init = eth_dev_pci_specific_init;
+ params.specific_device = pci_dev;
+
+ return nfb_eth_common_probe(¶ms);
+}
+
+int
+nfb_eth_common_remove(struct rte_device *dev)
+{
+ struct pmd_internals *entry, *temp;
+
+ RTE_TAILQ_FOREACH_SAFE(entry, &nfb_eth_dev_list, eth_dev_list, temp) {
+ if (dev == entry->eth_dev->device)
+ rte_eth_dev_destroy(entry->eth_dev, nfb_eth_dev_uninit);
+ }
+ return 0;
}
/**
@@ -719,7 +869,7 @@ nfb_eth_pci_probe(struct rte_pci_driver *pci_drv __rte_unused,
static int
nfb_eth_pci_remove(struct rte_pci_device *pci_dev)
{
- return rte_eth_dev_pci_generic_remove(pci_dev, nfb_eth_dev_uninit);
+ return nfb_eth_common_remove(&pci_dev->device);
}
static struct rte_pci_driver nfb_eth_driver = {
--
2.53.0
^ permalink raw reply related [flat|nested] 131+ messages in thread
* [PATCH v9 3/8] net/nfb: add vdev as alternative device probe method
2026-02-16 16:24 ` [PATCH v9 " spinler
2026-02-16 16:24 ` [PATCH v9 1/8] net/nfb: prepare for indirect queue mapping scheme spinler
2026-02-16 16:25 ` [PATCH v9 2/8] net/nfb: create one ethdev per ethernet port spinler
@ 2026-02-16 16:25 ` spinler
2026-02-16 16:25 ` [PATCH v9 4/8] net/nfb: add device argument "port" to limit used ports spinler
` (5 subsequent siblings)
8 siblings, 0 replies; 131+ messages in thread
From: spinler @ 2026-02-16 16:25 UTC (permalink / raw)
To: dev; +Cc: Martin Spinler
From: Martin Spinler <spinler@cesnet.cz>
VDEV are useful for NFB devices not represented by PCI device,
e.g. virtual or simulated devices.
Signed-off-by: Martin Spinler <spinler@cesnet.cz>
---
drivers/net/nfb/meson.build | 1 +
drivers/net/nfb/nfb_vdev.c | 106 ++++++++++++++++++++++++++++++++++++
2 files changed, 107 insertions(+)
create mode 100644 drivers/net/nfb/nfb_vdev.c
diff --git a/drivers/net/nfb/meson.build b/drivers/net/nfb/meson.build
index d7a255c928..9e458dfb4a 100644
--- a/drivers/net/nfb/meson.build
+++ b/drivers/net/nfb/meson.build
@@ -16,6 +16,7 @@ ext_deps += dep
sources = files(
'nfb_ethdev.c',
+ 'nfb_vdev.c',
'nfb_rx.c',
'nfb_rxmode.c',
'nfb_stats.c',
diff --git a/drivers/net/nfb/nfb_vdev.c b/drivers/net/nfb/nfb_vdev.c
new file mode 100644
index 0000000000..b79f7ac416
--- /dev/null
+++ b/drivers/net/nfb/nfb_vdev.c
@@ -0,0 +1,106 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2026 Cesnet
+ */
+
+#include <rte_kvargs.h>
+#include <ethdev_vdev.h>
+
+#include "nfb.h"
+
+#define VDEV_NFB_DRIVER net_vdev_nfb
+#define VDEV_NFB_ARG_DEV "dev"
+
+static int
+vdev_nfb_vdev_probe(struct rte_vdev_device *dev)
+{
+ unsigned int count;
+ int ret = 0;
+ size_t len, pos;
+
+ struct nfb_probe_params params;
+
+ const char *vdev_name = rte_vdev_device_name(dev);
+ const char *vdev_args = rte_vdev_device_args(dev);
+ char *dev_params, *dev_params_mod;
+ struct rte_kvargs *kvargs;
+
+ kvargs = rte_kvargs_parse(vdev_args, NULL);
+ if (kvargs == NULL) {
+ NFB_LOG(ERR, "Failed to parse device arguments %s", vdev_args);
+ ret = -EINVAL;
+ goto err_parse_args;
+ }
+
+ dev_params = strdup(vdev_args);
+ if (dev_params == NULL) {
+ ret = -ENOMEM;
+ goto err_strdup_params;
+ }
+
+ params.device = &dev->device;
+ params.specific_init = NULL;
+ params.specific_device = NULL;
+ params.path = nfb_default_dev_path();
+ params.args = dev_params;
+ params.nfb_id = 0;
+ params.ep_index = -1;
+
+ len = strlen(dev_params) + 1;
+ pos = 0;
+ dev_params[pos] = '\0';
+
+ /* Parse parameters for virtual device */
+ for (count = 0; count != kvargs->count; ++count) {
+ const struct rte_kvargs_pair *pair = &kvargs->pairs[count];
+
+ if (!strcmp(pair->key, VDEV_NFB_ARG_DEV)) {
+ params.path = pair->value;
+ } else {
+ /* Clone non-vdev arguments, result is shorter or equal length */
+ dev_params_mod = dev_params + pos;
+ ret = snprintf(dev_params_mod, len, "%s%s=%s",
+ pos == 0 ? "" : ",", pair->key, pair->value);
+ if (ret < 0 || ret >= (signed int)len)
+ goto err_clone_args;
+ pos += ret;
+ len -= ret;
+ }
+ }
+
+ strlcpy(params.name, vdev_name, sizeof(params.name));
+
+ ret = nfb_eth_common_probe(¶ms);
+ if (ret)
+ goto err_nfb_common_probe;
+
+ free(dev_params);
+ rte_kvargs_free(kvargs);
+
+ return ret;
+
+err_nfb_common_probe:
+err_clone_args:
+ free(dev_params);
+err_strdup_params:
+ rte_kvargs_free(kvargs);
+err_parse_args:
+ return ret;
+}
+
+static int
+vdev_nfb_vdev_remove(struct rte_vdev_device *dev)
+{
+ return nfb_eth_common_remove(&dev->device);
+}
+
+/** Virtual device descriptor. */
+static struct rte_vdev_driver vdev_nfb_vdev = {
+ .probe = vdev_nfb_vdev_probe,
+ .remove = vdev_nfb_vdev_remove,
+};
+
+RTE_PMD_REGISTER_VDEV(VDEV_NFB_DRIVER, vdev_nfb_vdev);
+RTE_PMD_REGISTER_ALIAS(VDEV_NFB_DRIVER, eth_vdev_nfb);
+RTE_PMD_REGISTER_PARAM_STRING(net_vdev_nfb,
+ VDEV_NFB_ARG_DEV "=<string>"
+ );
--
2.53.0
^ permalink raw reply related [flat|nested] 131+ messages in thread
* [PATCH v9 4/8] net/nfb: add device argument "port" to limit used ports
2026-02-16 16:24 ` [PATCH v9 " spinler
` (2 preceding siblings ...)
2026-02-16 16:25 ` [PATCH v9 3/8] net/nfb: add vdev as alternative device probe method spinler
@ 2026-02-16 16:25 ` spinler
2026-02-16 16:25 ` [PATCH v9 5/8] net/nfb: init only MACs associated with device spinler
` (4 subsequent siblings)
8 siblings, 0 replies; 131+ messages in thread
From: spinler @ 2026-02-16 16:25 UTC (permalink / raw)
To: dev; +Cc: Martin Spinler
From: Martin Spinler <spinler@cesnet.cz>
NFB devices do not use separate PCI device for each port; one PCI device
contains handles for all ports instead. For some application this approach
can be limiting.
The "port" argument can be used to select only desired ports.
It can be used multiple times. When is not used at all, the driver
selects all ports.
Signed-off-by: Martin Spinler <spinler@cesnet.cz>
---
drivers/net/nfb/nfb.h | 7 ++++
drivers/net/nfb/nfb_ethdev.c | 67 +++++++++++++++++++++++++++++++++---
drivers/net/nfb/nfb_vdev.c | 3 +-
3 files changed, 72 insertions(+), 5 deletions(-)
diff --git a/drivers/net/nfb/nfb.h b/drivers/net/nfb/nfb.h
index a854d16426..22fcb68cd7 100644
--- a/drivers/net/nfb/nfb.h
+++ b/drivers/net/nfb/nfb.h
@@ -42,6 +42,13 @@ extern int nfb_logtype;
#define RTE_NFB_DRIVER_NAME net_nfb
+/* Device arguments */
+#define NFB_ARG_PORT "port"
+
+#define NFB_COMMON_ARGS \
+ NFB_ARG_PORT "=<number>" \
+ ""
+
/*
* Handles obtained from the libnfb: each process must use own instance.
* Stored inside dev->process_private.
diff --git a/drivers/net/nfb/nfb_ethdev.c b/drivers/net/nfb/nfb_ethdev.c
index 233eb91e18..83601b3c2e 100644
--- a/drivers/net/nfb/nfb_ethdev.c
+++ b/drivers/net/nfb/nfb_ethdev.c
@@ -4,6 +4,7 @@
* All rights reserved.
*/
+#include <ctype.h>
#include <sys/queue.h>
#include <eal_export.h>
#include <rte_tailq.h>
@@ -22,12 +23,19 @@
#include "nfb_rxmode.h"
#include "nfb.h"
+static const char * const VALID_KEYS[] = {
+ NFB_ARG_PORT,
+ NULL
+};
+
struct nfb_ifc_create_params {
struct nfb_probe_params *probe_params;
struct nc_ifc_map_info map_info;
struct nc_ifc_info *ifc_info;
int basename_len; /* Cached real length of original probe_params->name */
+ /* Return value of failed nfb_eth_dev_create_for_ifc when rte_kvargs_process is used */
+ int ret;
};
/* The TAILQ entries are used for cleanup of allocated resources
@@ -738,6 +746,29 @@ nfb_eth_dev_create_for_ifc(struct nfb_ifc_create_params *cp)
return ret;
}
+static int nfb_eth_dev_create_for_ifc_by_port(const char *key __rte_unused,
+ const char *value, void *opaque)
+{
+ char *end;
+ unsigned long port;
+ struct nfb_ifc_create_params *ifc_params = opaque;
+
+ if (value == NULL || strlen(value) == 0 || !isdigit(*value))
+ return -EINVAL;
+
+ errno = 0;
+ port = strtoul(value, &end, 10);
+ if (errno != 0 || *end != '\0')
+ return -EINVAL;
+
+ if (port >= LONG_MAX || port >= (unsigned long)ifc_params->map_info.ifc_cnt)
+ return -EINVAL;
+
+ ifc_params->ifc_info = &ifc_params->map_info.ifc[port];
+ ifc_params->ret = nfb_eth_dev_create_for_ifc(ifc_params);
+ return ifc_params->ret;
+}
+
int
nfb_eth_common_probe(struct nfb_probe_params *params)
{
@@ -746,6 +777,7 @@ nfb_eth_common_probe(struct nfb_probe_params *params)
struct nfb_device *nfb_dev;
struct nfb_ifc_create_params ifc_params;
+ struct rte_kvargs *kvlist = NULL;
/* Open the device handle just for parsing ifc_params.
* A separate handle is used later for each netdev.
@@ -760,16 +792,38 @@ nfb_eth_common_probe(struct nfb_probe_params *params)
if (ret)
goto err_map_info_create;
+ if (params->args != NULL && strlen(params->args) > 0) {
+ kvlist = rte_kvargs_parse(params->args, VALID_KEYS);
+ if (kvlist == NULL) {
+ NFB_LOG(ERR, "Failed to parse device arguments %s", params->args);
+ ret = -EINVAL;
+ goto err_parse_args;
+ }
+ }
+
+ ifc_params.ret = 0;
ifc_params.probe_params = params;
ifc_params.basename_len = strlen(params->name);
- for (i = 0; i < ifc_params.map_info.ifc_cnt; i++) {
- ifc_params.ifc_info = &ifc_params.map_info.ifc[i];
- ret = nfb_eth_dev_create_for_ifc(&ifc_params);
- if (ret)
+ /* When at least one port argument is specified, create only selected ports */
+ if (kvlist && rte_kvargs_count(kvlist, NFB_ARG_PORT)) {
+ ret = rte_kvargs_process(kvlist, NFB_ARG_PORT,
+ nfb_eth_dev_create_for_ifc_by_port, (void *)&ifc_params);
+ if (ret) {
+ ret = ifc_params.ret;
goto err_dev_create;
+ }
+ } else {
+ for (i = 0; i < ifc_params.map_info.ifc_cnt; i++) {
+ ifc_params.ifc_info = &ifc_params.map_info.ifc[i];
+ ret = nfb_eth_dev_create_for_ifc(&ifc_params);
+ if (ret)
+ goto err_dev_create;
+ }
}
+ rte_kvargs_free(kvlist);
+
nc_map_info_destroy(&ifc_params.map_info);
nfb_close(nfb_dev);
@@ -777,6 +831,8 @@ nfb_eth_common_probe(struct nfb_probe_params *params)
err_dev_create:
nfb_eth_common_remove(params->device);
+ rte_kvargs_free(kvlist);
+err_parse_args:
nc_map_info_destroy(&ifc_params.map_info);
err_map_info_create:
nfb_close(nfb_dev);
@@ -882,3 +938,6 @@ RTE_PMD_REGISTER_PCI(RTE_NFB_DRIVER_NAME, nfb_eth_driver);
RTE_PMD_REGISTER_PCI_TABLE(RTE_NFB_DRIVER_NAME, nfb_pci_id_table);
RTE_PMD_REGISTER_KMOD_DEP(RTE_NFB_DRIVER_NAME, "* nfb");
RTE_LOG_REGISTER_DEFAULT(nfb_logtype, NOTICE);
+RTE_PMD_REGISTER_PARAM_STRING(RTE_NFB_DRIVER_NAME,
+ NFB_COMMON_ARGS
+ );
diff --git a/drivers/net/nfb/nfb_vdev.c b/drivers/net/nfb/nfb_vdev.c
index b79f7ac416..57f10d2068 100644
--- a/drivers/net/nfb/nfb_vdev.c
+++ b/drivers/net/nfb/nfb_vdev.c
@@ -102,5 +102,6 @@ static struct rte_vdev_driver vdev_nfb_vdev = {
RTE_PMD_REGISTER_VDEV(VDEV_NFB_DRIVER, vdev_nfb_vdev);
RTE_PMD_REGISTER_ALIAS(VDEV_NFB_DRIVER, eth_vdev_nfb);
RTE_PMD_REGISTER_PARAM_STRING(net_vdev_nfb,
- VDEV_NFB_ARG_DEV "=<string>"
+ VDEV_NFB_ARG_DEV "=<string> "
+ NFB_COMMON_ARGS
);
--
2.53.0
^ permalink raw reply related [flat|nested] 131+ messages in thread
* [PATCH v9 5/8] net/nfb: init only MACs associated with device
2026-02-16 16:24 ` [PATCH v9 " spinler
` (3 preceding siblings ...)
2026-02-16 16:25 ` [PATCH v9 4/8] net/nfb: add device argument "port" to limit used ports spinler
@ 2026-02-16 16:25 ` spinler
2026-02-16 16:25 ` [PATCH v9 6/8] net/nfb: add compatible cards to driver PCI ID table spinler
` (3 subsequent siblings)
8 siblings, 0 replies; 131+ messages in thread
From: spinler @ 2026-02-16 16:25 UTC (permalink / raw)
To: dev; +Cc: Martin Spinler
From: Martin Spinler <spinler@cesnet.cz>
Use information obtained from the new manner of creating NFB ethdevs
and initialize only RxMACs / TxMACs associated with actual ethdev.
This enables separated management and configuration of interface ports.
Signed-off-by: Martin Spinler <spinler@cesnet.cz>
---
drivers/net/nfb/nfb.h | 12 +--
drivers/net/nfb/nfb_ethdev.c | 149 ++++++++++++++++++-----------------
2 files changed, 79 insertions(+), 82 deletions(-)
diff --git a/drivers/net/nfb/nfb.h b/drivers/net/nfb/nfb.h
index 22fcb68cd7..ea8e4fd255 100644
--- a/drivers/net/nfb/nfb.h
+++ b/drivers/net/nfb/nfb.h
@@ -36,10 +36,6 @@ extern int nfb_logtype;
#define RTE_ETH_NDP_MAX_RX_QUEUES 32
#define RTE_ETH_NDP_MAX_TX_QUEUES 32
-/* Max index of rx/tx dmas */
-#define RTE_MAX_NC_RXMAC 256
-#define RTE_MAX_NC_TXMAC 256
-
#define RTE_NFB_DRIVER_NAME net_nfb
/* Device arguments */
@@ -54,10 +50,10 @@ extern int nfb_logtype;
* Stored inside dev->process_private.
*/
struct pmd_internals {
- uint16_t max_rxmac;
- uint16_t max_txmac;
- struct nc_rxmac *rxmac[RTE_MAX_NC_RXMAC];
- struct nc_txmac *txmac[RTE_MAX_NC_TXMAC];
+ uint16_t max_rxmac; /**< Count of valid rxmac items */
+ uint16_t max_txmac; /**< Count of valid txmac items */
+ struct nc_rxmac **rxmac; /**< Array of Rx MAC handles */
+ struct nc_txmac **txmac; /**< Array of Tx MAC handles */
struct nfb_device *nfb;
TAILQ_ENTRY(pmd_internals) eth_dev_list; /**< Item in list of all devices */
diff --git a/drivers/net/nfb/nfb_ethdev.c b/drivers/net/nfb/nfb_ethdev.c
index 83601b3c2e..82b39a0bfe 100644
--- a/drivers/net/nfb/nfb_ethdev.c
+++ b/drivers/net/nfb/nfb_ethdev.c
@@ -55,81 +55,87 @@ static const struct rte_ether_addr eth_addr = {
};
/**
- * Open all RX DMA queues
+ * Open Rx and Tx MAC components associated with current ifc
*
- * @param dev
- * Pointer to nfb device.
- * @param[out] rxmac
- * Pointer to output array of nc_rxmac
- * @param[out] max_rxmac
- * Pointer to output max index of rxmac
+ * @param intl
+ * Pointer to driver internal structure
+ * @param params
+ * Pointer to init parameters structure
*/
-static void
-nfb_nc_rxmac_init(struct nfb_device *nfb,
- struct nc_rxmac *rxmac[RTE_MAX_NC_RXMAC],
- uint16_t *max_rxmac)
+static int
+nfb_nc_eth_init(struct pmd_internals *intl, struct nfb_ifc_create_params *params)
{
- *max_rxmac = 0;
- while ((rxmac[*max_rxmac] = nc_rxmac_open_index(nfb, *max_rxmac)))
- ++(*max_rxmac);
-}
+ int ret;
+ int i, rxm, txm;
+ struct nc_ifc_info *ifc = params->ifc_info;
+ struct nc_ifc_map_info *mi = ¶ms->map_info;
-/**
- * Open all TX DMA queues
- *
- * @param dev
- * Pointer to nfb device.
- * @param[out] txmac
- * Pointer to output array of nc_txmac
- * @param[out] max_rxmac
- * Pointer to output max index of txmac
- */
-static void
-nfb_nc_txmac_init(struct nfb_device *nfb,
- struct nc_txmac *txmac[RTE_MAX_NC_TXMAC],
- uint16_t *max_txmac)
-{
- *max_txmac = 0;
- while ((txmac[*max_txmac] = nc_txmac_open_index(nfb, *max_txmac)))
- ++(*max_txmac);
-}
+ ret = -ENOMEM;
+ if (ifc->eth_cnt == 0)
+ return 0;
-/**
- * Close all RX DMA queues
- *
- * @param rxmac
- * Pointer to array of nc_rxmac
- * @param max_rxmac
- * Maximum index of rxmac
- */
-static void
-nfb_nc_rxmac_deinit(struct nc_rxmac *rxmac[RTE_MAX_NC_RXMAC],
- uint16_t max_rxmac)
-{
- uint16_t i;
- for (i = 0; i < max_rxmac; i++) {
- nc_rxmac_close(rxmac[i]);
- rxmac[i] = NULL;
+ intl->rxmac = calloc(ifc->eth_cnt, sizeof(*intl->rxmac));
+ if (intl->rxmac == NULL)
+ goto err_alloc_rxmac;
+
+ intl->txmac = calloc(ifc->eth_cnt, sizeof(*intl->txmac));
+ if (intl->txmac == NULL)
+ goto err_alloc_txmac;
+
+ /* Some eths may not have assigned MAC nodes, hence use separate var for indexing */
+ rxm = 0;
+ txm = 0;
+ for (i = 0; i < mi->eth_cnt; i++) {
+ if (mi->eth[i].ifc != ifc->id)
+ continue;
+
+ if (rxm >= ifc->eth_cnt || txm >= ifc->eth_cnt) {
+ NFB_LOG(ERR, "MAC mapping inconsistent");
+ ret = -EINVAL;
+ goto err_map_inconsistent;
+ }
+
+ intl->rxmac[rxm] = nc_rxmac_open(intl->nfb, mi->eth[i].node_rxmac);
+ if (intl->rxmac[rxm])
+ rxm++;
+
+ intl->txmac[txm] = nc_txmac_open(intl->nfb, mi->eth[i].node_txmac);
+ if (intl->txmac[txm])
+ txm++;
}
+
+ intl->max_rxmac = rxm;
+ intl->max_txmac = txm;
+ return 0;
+
+err_map_inconsistent:
+ for (i = 0; i < txm; i++)
+ nc_txmac_close(intl->txmac[i]);
+ for (i = 0; i < rxm; i++)
+ nc_rxmac_close(intl->rxmac[i]);
+ free(intl->txmac);
+err_alloc_txmac:
+ free(intl->rxmac);
+err_alloc_rxmac:
+ return ret;
}
/**
- * Close all TX DMA queues
- *
- * @param txmac
- * Pointer to array of nc_txmac
- * @param max_txmac
- * Maximum index of txmac
+ * Close all Rx+Tx MAC components
+ * @param intl
+ * Pointer to internal driver structure
*/
static void
-nfb_nc_txmac_deinit(struct nc_txmac *txmac[RTE_MAX_NC_TXMAC],
- uint16_t max_txmac)
+nfb_nc_eth_deinit(struct pmd_internals *intl)
{
uint16_t i;
- for (i = 0; i < max_txmac; i++) {
- nc_txmac_close(txmac[i]);
- txmac[i] = NULL;
- }
+ for (i = 0; i < intl->max_txmac; i++)
+ nc_txmac_close(intl->txmac[i]);
+ for (i = 0; i < intl->max_rxmac; i++)
+ nc_rxmac_close(intl->rxmac[i]);
+
+ free(intl->txmac);
+ free(intl->rxmac);
}
/**
@@ -346,7 +352,7 @@ nfb_eth_link_update(struct rte_eth_dev *dev,
link.link_duplex = RTE_ETH_LINK_FULL_DUPLEX;
link.link_autoneg = RTE_ETH_LINK_SPEED_FIXED;
- if (internals->rxmac[0] != NULL) {
+ if (internals->max_rxmac) {
nc_rxmac_read_status(internals->rxmac[0], &status);
switch (status.speed) {
@@ -571,12 +577,9 @@ nfb_eth_dev_init(struct rte_eth_dev *dev, void *init_data)
max_rx_queues = ifc->rxq_cnt;
max_tx_queues = ifc->txq_cnt;
- nfb_nc_rxmac_init(internals->nfb,
- internals->rxmac,
- &internals->max_rxmac);
- nfb_nc_txmac_init(internals->nfb,
- internals->txmac,
- &internals->max_txmac);
+ ret = nfb_nc_eth_init(internals, params);
+ if (ret)
+ goto err_nc_eth_init;
/* Set rx, tx burst functions */
dev->rx_pkt_burst = nfb_eth_ndp_rx;
@@ -662,10 +665,9 @@ nfb_eth_dev_init(struct rte_eth_dev *dev, void *init_data)
rte_free(priv->queue_map_rx);
err_alloc_queue_map:
err_secondary_not_ready:
- nfb_nc_rxmac_deinit(internals->rxmac, internals->max_rxmac);
- nfb_nc_txmac_deinit(internals->txmac, internals->max_txmac);
+ nfb_nc_eth_deinit(internals);
+err_nc_eth_init:
nfb_close(internals->nfb);
-
err_nfb_open:
rte_free(internals);
err_alloc_internals:
@@ -692,8 +694,7 @@ nfb_eth_dev_uninit(struct rte_eth_dev *dev)
TAILQ_REMOVE(&nfb_eth_dev_list, internals, eth_dev_list);
- nfb_nc_rxmac_deinit(internals->rxmac, internals->max_rxmac);
- nfb_nc_txmac_deinit(internals->txmac, internals->max_txmac);
+ nfb_nc_eth_deinit(internals);
nfb_close(internals->nfb);
rte_free(internals);
--
2.53.0
^ permalink raw reply related [flat|nested] 131+ messages in thread
* [PATCH v9 6/8] net/nfb: add compatible cards to driver PCI ID table
2026-02-16 16:24 ` [PATCH v9 " spinler
` (4 preceding siblings ...)
2026-02-16 16:25 ` [PATCH v9 5/8] net/nfb: init only MACs associated with device spinler
@ 2026-02-16 16:25 ` spinler
2026-02-16 16:25 ` [PATCH v9 7/8] net/nfb: report firmware version spinler
` (2 subsequent siblings)
8 siblings, 0 replies; 131+ messages in thread
From: spinler @ 2026-02-16 16:25 UTC (permalink / raw)
To: dev; +Cc: Martin Spinler
From: Martin Spinler <spinler@cesnet.cz>
The nfb driver is compatible with:
- NFB-200G2QL card secondary PCI device
- FB2CGHH card
- COMBO-400G1 card
- Common CESNET-NDK cards
Signed-off-by: Martin Spinler <spinler@cesnet.cz>
---
drivers/net/nfb/nfb.h | 5 +++++
drivers/net/nfb/nfb_ethdev.c | 4 ++++
2 files changed, 9 insertions(+)
diff --git a/drivers/net/nfb/nfb.h b/drivers/net/nfb/nfb.h
index ea8e4fd255..1d863d5ad7 100644
--- a/drivers/net/nfb/nfb.h
+++ b/drivers/net/nfb/nfb.h
@@ -24,13 +24,18 @@ extern int nfb_logtype;
/* PCI Vendor ID */
#define PCI_VENDOR_ID_NETCOPE 0x1b26
#define PCI_VENDOR_ID_SILICOM 0x1c2c
+#define PCI_VENDOR_ID_CESNET 0x18ec
/* PCI Device IDs */
#define PCI_DEVICE_ID_NFB_40G2 0xcb80
#define PCI_DEVICE_ID_NFB_100G2 0xc2c1
#define PCI_DEVICE_ID_NFB_200G2QL 0xc250
+#define PCI_DEVICE_ID_NFB_200G2QL_E1 0xc251
#define PCI_DEVICE_ID_FB2CGG3 0x00d0
+#define PCI_DEVICE_ID_FB2CGHH 0x00d2
#define PCI_DEVICE_ID_FB2CGG3D 0xc240
+#define PCI_DEVICE_ID_COMBO400G1 0xc400
+#define PCI_DEVICE_ID_CESNET_NDK_COMMON 0xc000
/* Max index of ndp rx/tx queues */
#define RTE_ETH_NDP_MAX_RX_QUEUES 32
diff --git a/drivers/net/nfb/nfb_ethdev.c b/drivers/net/nfb/nfb_ethdev.c
index 82b39a0bfe..3bdfc89499 100644
--- a/drivers/net/nfb/nfb_ethdev.c
+++ b/drivers/net/nfb/nfb_ethdev.c
@@ -844,8 +844,12 @@ static const struct rte_pci_id nfb_pci_id_table[] = {
{ RTE_PCI_DEVICE(PCI_VENDOR_ID_NETCOPE, PCI_DEVICE_ID_NFB_40G2) },
{ RTE_PCI_DEVICE(PCI_VENDOR_ID_NETCOPE, PCI_DEVICE_ID_NFB_100G2) },
{ RTE_PCI_DEVICE(PCI_VENDOR_ID_NETCOPE, PCI_DEVICE_ID_NFB_200G2QL) },
+ { RTE_PCI_DEVICE(PCI_VENDOR_ID_NETCOPE, PCI_DEVICE_ID_NFB_200G2QL_E1) },
{ RTE_PCI_DEVICE(PCI_VENDOR_ID_SILICOM, PCI_DEVICE_ID_FB2CGG3) },
+ { RTE_PCI_DEVICE(PCI_VENDOR_ID_SILICOM, PCI_DEVICE_ID_FB2CGHH) },
{ RTE_PCI_DEVICE(PCI_VENDOR_ID_SILICOM, PCI_DEVICE_ID_FB2CGG3D) },
+ { RTE_PCI_DEVICE(PCI_VENDOR_ID_CESNET, PCI_DEVICE_ID_COMBO400G1) },
+ { RTE_PCI_DEVICE(PCI_VENDOR_ID_CESNET, PCI_DEVICE_ID_CESNET_NDK_COMMON) },
{ .vendor_id = 0, }
};
--
2.53.0
^ permalink raw reply related [flat|nested] 131+ messages in thread
* [PATCH v9 7/8] net/nfb: report firmware version
2026-02-16 16:24 ` [PATCH v9 " spinler
` (5 preceding siblings ...)
2026-02-16 16:25 ` [PATCH v9 6/8] net/nfb: add compatible cards to driver PCI ID table spinler
@ 2026-02-16 16:25 ` spinler
2026-02-16 16:25 ` [PATCH v9 8/8] doc/nfb: cleanup and update guide spinler
2026-02-16 22:11 ` [PATCH v9 0/8] net/nfb: rework to real multiport Stephen Hemminger
8 siblings, 0 replies; 131+ messages in thread
From: spinler @ 2026-02-16 16:25 UTC (permalink / raw)
To: dev; +Cc: Martin Spinler
From: Martin Spinler <spinler@cesnet.cz>
Report the firmware version, which is described
by project name and project version.
Signed-off-by: Martin Spinler <spinler@cesnet.cz>
---
drivers/net/nfb/nfb_ethdev.c | 27 +++++++++++++++++++++++++++
1 file changed, 27 insertions(+)
diff --git a/drivers/net/nfb/nfb_ethdev.c b/drivers/net/nfb/nfb_ethdev.c
index 3bdfc89499..111f75e293 100644
--- a/drivers/net/nfb/nfb_ethdev.c
+++ b/drivers/net/nfb/nfb_ethdev.c
@@ -502,6 +502,32 @@ nfb_eth_mac_addr_remove(struct rte_eth_dev *dev, uint32_t index)
nc_rxmac_set_mac(internals->rxmac[i], index, 0, 0);
}
+static int
+nfb_eth_fw_version_get(struct rte_eth_dev *dev, char *fw_version,
+ size_t fw_size)
+{
+ int ret;
+ const char *proj_name, *proj_vers;
+ struct pmd_internals *intl = dev->process_private;
+
+ proj_name = nc_info_get_fw_project_name(intl->nfb, NULL);
+ proj_vers = nc_info_get_fw_project_version(intl->nfb, NULL);
+
+ if (proj_name == NULL)
+ proj_name = "";
+ if (proj_vers == NULL)
+ proj_vers = "";
+
+ ret = snprintf(fw_version, fw_size, "%s;%s", proj_name, proj_vers);
+ if (ret < 0)
+ return ret;
+
+ if (ret >= (signed int)fw_size)
+ return strlen(proj_name) + 1 + strlen(proj_vers) + 1;
+
+ return 0;
+}
+
static const struct eth_dev_ops ops = {
.dev_start = nfb_eth_dev_start,
.dev_stop = nfb_eth_dev_stop,
@@ -528,6 +554,7 @@ static const struct eth_dev_ops ops = {
.mac_addr_set = nfb_eth_mac_addr_set,
.mac_addr_add = nfb_eth_mac_addr_add,
.mac_addr_remove = nfb_eth_mac_addr_remove,
+ .fw_version_get = nfb_eth_fw_version_get,
};
/**
--
2.53.0
^ permalink raw reply related [flat|nested] 131+ messages in thread
* [PATCH v9 8/8] doc/nfb: cleanup and update guide
2026-02-16 16:24 ` [PATCH v9 " spinler
` (6 preceding siblings ...)
2026-02-16 16:25 ` [PATCH v9 7/8] net/nfb: report firmware version spinler
@ 2026-02-16 16:25 ` spinler
2026-02-16 22:11 ` [PATCH v9 0/8] net/nfb: rework to real multiport Stephen Hemminger
8 siblings, 0 replies; 131+ messages in thread
From: spinler @ 2026-02-16 16:25 UTC (permalink / raw)
To: dev; +Cc: Martin Spinler
From: Martin Spinler <spinler@cesnet.cz>
Clean up documentation and update the guide with:
- Current card list with firmware download links
- Updated port argument description
Signed-off-by: Martin Spinler <spinler@cesnet.cz>
---
doc/guides/nics/features/nfb.ini | 4 +
doc/guides/nics/nfb.rst | 187 +++++++++++--------------
doc/guides/rel_notes/release_26_03.rst | 6 +
3 files changed, 95 insertions(+), 102 deletions(-)
diff --git a/doc/guides/nics/features/nfb.ini b/doc/guides/nics/features/nfb.ini
index c77b592789..7ac89fc15f 100644
--- a/doc/guides/nics/features/nfb.ini
+++ b/doc/guides/nics/features/nfb.ini
@@ -9,9 +9,13 @@ Link status = Y
Queue start/stop = Y
Promiscuous mode = Y
Allmulticast mode = Y
+Unicast MAC filter = Y
+Timestamp offload = P
Basic stats = Y
Extended stats = Y
Stats per queue = Y
+FW version = Y
+Multiprocess aware = Y
Linux = Y
x86-64 = Y
Usage doc = Y
diff --git a/doc/guides/nics/nfb.rst b/doc/guides/nics/nfb.rst
index a9b4049654..55a635d8d4 100644
--- a/doc/guides/nics/nfb.rst
+++ b/doc/guides/nics/nfb.rst
@@ -2,64 +2,101 @@
Copyright 2019 Cesnet
Copyright 2019 Netcope Technologies
-NFB poll mode driver library
-=================================
+NFB Poll Mode Driver
+====================
+
+The NFB PMD implements support for the FPGA-based
+programmable NICs running `CESNET-NDK <https://www.liberouter.org/ndk/>`_
+based firmware (formerly known as the NetCOPE platform). The CESNET Network Development Kit offers
+wide spectrum of supported cards, for example:
+N6010, FB2CGG3 (Silicom Denmark),
+IA-420F, IA-440i (BittWare),
+AGI-FH400G (ReflexCES),
+and `many more <https://github.com/CESNET/ndk-fpga/tree/devel/cards>`_.
+
+The CESNET-NDK framework is open source and
+can be found on `CESNET-NDK <https://github.com/CESNET/ndk-fpga>`_.
+Ready-to-use demo firmwares can be found on the `DYNANIC page <https://dyna-nic.com/try-now/>`_.
+
+Software compatibility and firmware for
+`historical cards <https://www.liberouter.org/technologies/cards/>`_
+are left unmaintained.
+
+Software prerequisites
+----------------------
-The NFB poll mode driver library implements support for the Netcope
-FPGA Boards (**NFB-40G2, NFB-100G2, NFB-200G2QL**) and Silicom **FB2CGG3** card,
-FPGA-based programmable NICs. The NFB PMD uses interface provided by the libnfb
-library to communicate with these cards over the nfb layer.
+This PMD requires a Linux kernel module, which is responsible for initialization and
+allocation of resources needed for the nfb layer function.
+Communication between PMD and kernel modules is mediated by the libnfb library.
+The kernel module and library are not part of DPDK and must be installed
+separately. Dependencies can be found on GitHub:
+`nfb-framework <https://github.com/CESNET/ndk-sw>`_ as source code,
+or for RPM-based distributions, the prebuilt `nfb-framework` package on
+`Fedora Copr <https://copr.fedorainfracloud.org/coprs/g/CESNET/nfb-framework/>`_.
-More information about the
-`NFB cards <https://www.liberouter.org/technologies/cards/>`_
-and used technology
-(`Network Development Kit <https://www.liberouter.org/ndk/>`_)
-can be found on the `Liberouter website <http://www.liberouter.org/>`_.
+Before starting the DPDK, make sure that the kernel module is loaded (`sudo modprobe nfb`)
+and the card is running the CESNET-NDK based firmware (`nfb-info -l`).
.. note::
- Currently the driver is supported only on x86_64 architectures.
- Only x86_64 versions of the external libraries are provided.
+ Currently, the driver is supported only on x86_64 architectures.
+
+NFB card architecture
+---------------------
+
+Ethernet Ports
+~~~~~~~~~~~~~~
+
+The NFB cards are multi-port multi-queue cards, where (generally) data from any
+Ethernet port may be sent by the firmware to any queue.
-Prerequisites
--------------
+The cards were historically represented in DPDK as a single port.
+Currently each Ethernet channel is represented as one DPDK port.
-This PMD requires kernel modules which are responsible for initialization and
-allocation of resources needed for nfb layer function.
-Communication between PMD and kernel modules is mediated by libnfb library.
-These kernel modules and library are not part of DPDK and must be installed
-separately:
+.. note::
-* **libnfb library**
+ Normally, one port corresponds to one channel, but ports can often be configured in a separate
+ manner. For example one 100G port can be used as 4x25G or 4x10G independent Ethernet channels.
- The library provides API for initialization of nfb transfers, receiving and
- transmitting data segments.
+By default, all ports are initialized and used for the allowed PCI device.
+When this behaviour is limiting (e.g., for multiple instances of DPDK app on different ports
+of the same PCI device), ports can be specified by the `port` item in the `allow` argument:
-* **Kernel modules**
+.. code-block:: console
- * nfb
+ -a 0000:01:00.0,port=0,port=3
- Kernel modules manage initialization of hardware, allocation and
- sharing of resources for user space applications.
+PCIe slots
+~~~~~~~~~~
-Dependencies can be found here:
-`Netcope common <https://github.com/CESNET/ndk-sw>`_.
+Some cards employ more than one PCIe device for better data throughput. This can be achieved by
+slot bifurcation (only a minor improvement) or by an add-on cable connected to another PCIe slot.
+Both improvements can work together, as is, for example, in the case of the AGI-FH400G card.
-Versions of the packages
-~~~~~~~~~~~~~~~~~~~~~~~~
+Because primary and secondary slot(s) can be attached to different NUMA nodes
+(also applies for bifurcation on some HW), the data structures need to be correctly allocated.
+(Device-aware allocation matters also on IOMMU-enabled systems.)
+The firmware already provides DMA queue to PCI device mapping. The DPDK application just needs to
+use all PCI devices, otherwise some queues will not be available; provide all PCI endpoints
+listed in the `nfb-info -v` in the `allow` argument.
-The minimum version of the provided packages:
+.. note::
-* for DPDK from 19.05
+ For cards where the number of Ethernet ports is less than the number of PCI devices
+ (e.g., AGI-FH400G: 1 port, up to 4 PCI devices), the virtual DPDK ports are
+ created to achieve the best NUMA-aware throughput
+ (virtual ports lack a lot of configuration features).
-Configuration
--------------
+Features
+--------
Timestamps
+~~~~~~~~~~
-The PMD supports hardware timestamps of frame receipt on physical network interface. In order to use
-the timestamps, the hardware timestamping unit must be enabled (follow the documentation of the NFB
-products). The standard `RTE_ETH_RX_OFFLOAD_TIMESTAMP` flag can be used for this feature.
+The PMD supports hardware timestamps of frame receipt on physical network interface.
+In order to use the timestamps, the hardware timestamping unit must be enabled
+(follow the documentation of the NFB products).
+The standard `RTE_ETH_RX_OFFLOAD_TIMESTAMP` flag can be used for this feature.
When the timestamps are enabled, a timestamp validity flag is set in the MBUFs
containing received frames and timestamp is inserted into the `rte_mbuf` struct.
@@ -67,72 +104,18 @@ containing received frames and timestamp is inserted into the `rte_mbuf` struct.
The timestamp is an `uint64_t` field and holds the number of nanoseconds
elapsed since 1.1.1970 00:00:00 UTC.
+Simulation
+~~~~~~~~~~
-Using the NFB PMD
-----------------------
-
-Kernel modules have to be loaded before running the DPDK application.
-
-NFB card architecture
----------------------
-
-The NFB cards are multi-port multi-queue cards, where (generally) data from any
-Ethernet port may be sent to any queue.
-They are represented in DPDK as a single port.
-
-NFB-200G2QL card employs an add-on cable which allows to connect it to two
-physical PCI-E slots at the same time (see the diagram below).
-This is done to allow 200 Gbps of traffic to be transferred through the PCI-E
-bus (note that a single PCI-E 3.0 x16 slot provides only 125 Gbps theoretical
-throughput).
-
-Although each slot may be connected to a different CPU and therefore to a different
-NUMA node, the card is represented as a single port in DPDK. To work with data
-from the individual queues on the right NUMA node, connection of NUMA nodes on
-first and last queue (each NUMA node has half of the queues) need to be checked.
-
-Limitations
------------
-
-Driver is usable only on Linux architecture, namely on CentOS.
-
-Since a card is always represented as a single port, but can be connected to two
-NUMA nodes, there is need for manual check where master/slave is connected.
-
-Example of usage
-----------------
-
-Read packets from 0. and 1. receive queue and write them to 0. and 1.
-transmit queue:
+The CESNET-NDK framework offers the possibility of simulating the firmware together with DPDK.
+This allows for easy debugging of a packet flow behaviour with a specific firmware configuration.
+The DPDK NFB driver can be connected to the simulator (Questa/ModelSim/nvc) via a virtual device:
.. code-block:: console
- ./<build_dir>/app/dpdk-testpmd -l 0-3 -n 2 \
- -- --port-topology=chained --rxq=2 --txq=2 --nb-cores=2 -i -a
-
-Example output:
-
-.. code-block:: console
+ dpdk-testpmd
+ --vdev=eth_vdev_nfb,dev=libnfb-ext-grpc.so:grpc+dma_vas:localhost:50051,queue_driver=native
+ --iova-mode=va -- -i
- [...]
- EAL: PCI device 0000:06:00.0 on NUMA socket -1
- EAL: probe driver: 1b26:c1c1 net_nfb
- PMD: Initializing NFB device (0000:06:00.0)
- PMD: Available DMA queues RX: 8 TX: 8
- PMD: NFB device (0000:06:00.0) successfully initialized
- Interactive-mode selected
- Auto-start selected
- Configuring Port 0 (socket 0)
- Port 0: 00:11:17:00:00:00
- Checking link statuses...
- Port 0 Link Up - speed 10000 Mbps - full-duplex
- Done
- Start automatic packet forwarding
- io packet forwarding - CRC stripping disabled - packets/burst=32
- nb forwarding cores=2 - nb forwarding ports=1
- RX queues=2 - RX desc=128 - RX free threshold=0
- RX threshold registers: pthresh=0 hthresh=0 wthresh=0
- TX queues=2 - TX desc=512 - TX free threshold=0
- TX threshold registers: pthresh=0 hthresh=0 wthresh=0
- TX RS bit threshold=0 - TXQ flags=0x0
- testpmd>
+More info about the simulation can be found int the CESNET-NDK `documentation
+<https://cesnet.github.io/ndk-fpga/devel/ndk_apps/minimal/tests/cocotb/readme.html>`_.
diff --git a/doc/guides/rel_notes/release_26_03.rst b/doc/guides/rel_notes/release_26_03.rst
index ff02c294d0..7678cf1a49 100644
--- a/doc/guides/rel_notes/release_26_03.rst
+++ b/doc/guides/rel_notes/release_26_03.rst
@@ -62,6 +62,12 @@ New Features
* **Updated CESNET NFB ethernet driver.**
* The timestamp value has been updated to make it usable.
+ * The DPDK port now represents just one Ethernet port instead of all Ethernet ports on the NIC.
+ * All ports are used by default, but a subset can be selected using the ``port`` argument.
+ * Report firmware version.
+ * Common CESNET-NDK-based adapters have been added,
+ including the FB2CGHH (Silicom Denmark) and XpressSX AGI-FH400G (Reflex CES).
+
Removed Items
-------------
--
2.53.0
^ permalink raw reply related [flat|nested] 131+ messages in thread
* Re: [PATCH v9 0/8] net/nfb: rework to real multiport
2026-02-16 16:24 ` [PATCH v9 " spinler
` (7 preceding siblings ...)
2026-02-16 16:25 ` [PATCH v9 8/8] doc/nfb: cleanup and update guide spinler
@ 2026-02-16 22:11 ` Stephen Hemminger
2026-02-17 7:09 ` Martin Spinler
8 siblings, 1 reply; 131+ messages in thread
From: Stephen Hemminger @ 2026-02-16 22:11 UTC (permalink / raw)
To: spinler; +Cc: dev
On Mon, 16 Feb 2026 17:24:58 +0100
spinler@cesnet.cz wrote:
> From: Martin Spinler <spinler@cesnet.cz>
>
> This series implements real multiport for better user experience.
>
> The existing driver creates one ethdev/port for one PCI device.
> As the CESNET-NDK based cards aren't capable to represent each
> Ethernet port by own PCI device, new driver implementation
> processes real port configuration from firmware/card and switches
> from rte_eth_dev_pci_generic_probe to multiple rte_eth_dev_create calls.
>
> ---
> Depends-on: patch-37245 ("doc/nfb: update release notes for nfb driver")
After some feedback to the AI review. It still had minor feedback, do you want more detail?
I can take this as is or you can send new version.
NFB v9 Review Summary
8 patches, v9 — Martin Spinler's multi-port NFB driver rework. Overall a clean, well-structured series.
Warnings:
Patch 2: If rte_eth_dev_create() succeeds but rte_eth_dev_get_by_name()
returns NULL, the created device is never added to the cleanup TAILQ
and leaks.
Patch 3: nfb_default_dev_path() return value used without NULL check before passing to nfb_open().
Patch 4: The kvargs callback returns errors from input validation (e.g., bad port string) but ifc_params.ret is only set after nfb_eth_dev_create_for_ifc, so the caller can overwrite the real error with 0.
Patch 8: Release notes don't mention the new vdev/simulation support from patch 3.
^ permalink raw reply [flat|nested] 131+ messages in thread
* Re: [PATCH v9 0/8] net/nfb: rework to real multiport
2026-02-16 22:11 ` [PATCH v9 0/8] net/nfb: rework to real multiport Stephen Hemminger
@ 2026-02-17 7:09 ` Martin Spinler
0 siblings, 0 replies; 131+ messages in thread
From: Martin Spinler @ 2026-02-17 7:09 UTC (permalink / raw)
To: Stephen Hemminger; +Cc: dev
> After some feedback to the AI review. It still had minor feedback, do you want more detail?
> I can take this as is or you can send new version.
I think I can address only one warning from the AI review:
the propagated return value in Patch 4.
> Patch 2: If rte_eth_dev_create() succeeds but rte_eth_dev_get_by_name()
> returns NULL, the created device is never added to the cleanup TAILQ
> and leaks.
The rte_eth_dev_create() does not return rte_eth_dev*, so I don't have
a handle to cleanup something with rte_eth_dev_destroy().
The only solution that comes to my mind, is to add the rte_eth_dev
temporary handle to let say a second 'temporary' TAILQ list inside
nfb_eth_dev_init() (called in rte_eth_dev_create()), which will be
checked after the rte_eth_dev_create() returns. However, it's still
messy and doesn't solve the problem comprehensively.
> Patch 3: nfb_default_dev_path() return value used without NULL check before passing to nfb_open().
Both is fine (nfb_default_dev_path doesn't returns NULL, nfb_open can
handle NULL: calls nfb_default_dev_path() internally in that case).
> Patch 4: The kvargs callback returns errors from input validation (e.g., bad port string) but ifc_params.ret is only set after nfb_eth_dev_create_for_ifc, so the caller can overwrite the real error with 0.
Fixed in v10.
> Patch 8: Release notes don't mention the new vdev/simulation support from patch 3.
Quite a minor feature.
^ permalink raw reply [flat|nested] 131+ messages in thread
* [PATCH v10 0/8] net/nfb: rework to real multiport
2026-01-15 15:16 [PATCH 0/8] net/nfb: rework to real multiport spinler
` (16 preceding siblings ...)
2026-02-16 16:24 ` [PATCH v9 " spinler
@ 2026-02-17 7:10 ` spinler
2026-02-17 7:10 ` [PATCH v10 1/8] net/nfb: prepare for indirect queue mapping scheme spinler
` (9 more replies)
17 siblings, 10 replies; 131+ messages in thread
From: spinler @ 2026-02-17 7:10 UTC (permalink / raw)
To: dev; +Cc: Martin Spinler
From: Martin Spinler <spinler@cesnet.cz>
This series implements real multiport for better user experience.
The existing driver creates one ethdev/port for one PCI device.
As the CESNET-NDK based cards aren't capable to represent each
Ethernet port by own PCI device, new driver implementation
processes real port configuration from firmware/card and switches
from rte_eth_dev_pci_generic_probe to multiple rte_eth_dev_create calls.
---
Depends-on: patch-37245 ("doc/nfb: update release notes for nfb driver")
v10:
* fixed propagated return value in nfb_eth_dev_create_for_ifc_by_port()
Martin Spinler (8):
net/nfb: prepare for indirect queue mapping scheme
net/nfb: create one ethdev per ethernet port
net/nfb: add vdev as alternative device probe method
net/nfb: add device argument "port" to limit used ports
net/nfb: init only MACs associated with device
net/nfb: add compatible cards to driver PCI ID table
net/nfb: report firmware version
doc/nfb: cleanup and update guide
doc/guides/nics/features/nfb.ini | 4 +
doc/guides/nics/nfb.rst | 187 +++++-----
doc/guides/rel_notes/release_26_03.rst | 6 +
drivers/net/nfb/meson.build | 1 +
drivers/net/nfb/nfb.h | 55 ++-
drivers/net/nfb/nfb_ethdev.c | 493 +++++++++++++++++++------
drivers/net/nfb/nfb_rx.c | 33 +-
drivers/net/nfb/nfb_rx.h | 9 +-
drivers/net/nfb/nfb_tx.c | 30 +-
drivers/net/nfb/nfb_tx.h | 7 +-
drivers/net/nfb/nfb_vdev.c | 107 ++++++
11 files changed, 676 insertions(+), 256 deletions(-)
create mode 100644 drivers/net/nfb/nfb_vdev.c
--
2.53.0
^ permalink raw reply [flat|nested] 131+ messages in thread
* [PATCH v10 1/8] net/nfb: prepare for indirect queue mapping scheme
2026-02-17 7:10 ` [PATCH v10 " spinler
@ 2026-02-17 7:10 ` spinler
2026-02-17 7:10 ` [PATCH v10 2/8] net/nfb: create one ethdev per ethernet port spinler
` (8 subsequent siblings)
9 siblings, 0 replies; 131+ messages in thread
From: spinler @ 2026-02-17 7:10 UTC (permalink / raw)
To: dev; +Cc: Martin Spinler
From: Martin Spinler <spinler@cesnet.cz>
The NFB driver is being enhanced to create one ethdev per physical
Ethernet port instead of one ethdev per PCI device. This requires
an indirect mapping layer because DPDK queue indices no longer
directly correspond to firmware queue indices.
This change adds queue_map_rx and queue_map_tx arrays to track the
mapping between DPDK queue indices and firmware queue IDs, preparing
for the multi-port implementation in subsequent patches.
Signed-off-by: Martin Spinler <spinler@cesnet.cz>
---
drivers/net/nfb/nfb.h | 6 ++++++
drivers/net/nfb/nfb_ethdev.c | 28 ++++++++++++++++++++++++++++
drivers/net/nfb/nfb_rx.c | 33 ++++++++++++++++++---------------
drivers/net/nfb/nfb_rx.h | 9 ++++-----
drivers/net/nfb/nfb_tx.c | 30 ++++++++++++++++++------------
drivers/net/nfb/nfb_tx.h | 7 +++----
6 files changed, 77 insertions(+), 36 deletions(-)
diff --git a/drivers/net/nfb/nfb.h b/drivers/net/nfb/nfb.h
index 90b04c6151..fa51afc7de 100644
--- a/drivers/net/nfb/nfb.h
+++ b/drivers/net/nfb/nfb.h
@@ -60,6 +60,12 @@ struct pmd_internals {
struct pmd_priv {
uint16_t max_rx_queues;
uint16_t max_tx_queues;
+
+ /** Mapping from DPDK RX queue index to firmware queue ID */
+ int *queue_map_rx;
+ /** Mapping from DPDK TX queue index to firmware queue ID */
+ int *queue_map_tx;
+ bool ready; /**< This structure is initialized for usage in secondary process */
};
#endif /* _NFB_H_ */
diff --git a/drivers/net/nfb/nfb_ethdev.c b/drivers/net/nfb/nfb_ethdev.c
index 967f127f40..c0a2905249 100644
--- a/drivers/net/nfb/nfb_ethdev.c
+++ b/drivers/net/nfb/nfb_ethdev.c
@@ -509,6 +509,7 @@ static const struct eth_dev_ops ops = {
static int
nfb_eth_dev_init(struct rte_eth_dev *dev)
{
+ int i;
int ret;
uint32_t mac_count;
struct rte_eth_dev_data *data = dev->data;
@@ -577,6 +578,20 @@ nfb_eth_dev_init(struct rte_eth_dev *dev)
priv->max_rx_queues = max_rx_queues;
priv->max_tx_queues = max_tx_queues;
+ priv->queue_map_rx = rte_calloc("NFB queue map", max_rx_queues + max_tx_queues,
+ sizeof(*priv->queue_map_rx), 0);
+ if (priv->queue_map_rx == NULL) {
+ ret = -ENOMEM;
+ goto err_alloc_queue_map;
+ }
+ priv->queue_map_tx = priv->queue_map_rx + max_rx_queues;
+
+ /* default queue mapping is 1:1 */
+ for (i = 0; i < max_rx_queues; i++)
+ priv->queue_map_rx[i] = i;
+ for (i = 0; i < max_tx_queues; i++)
+ priv->queue_map_tx[i] = i;
+
/* Allocate space for MAC addresses */
mac_count = nfb_eth_get_max_mac_address_count(dev);
data->mac_addrs = rte_zmalloc(data->name,
@@ -599,6 +614,12 @@ nfb_eth_dev_init(struct rte_eth_dev *dev)
data->all_multicast = nfb_eth_allmulticast_get(dev);
data->dev_flags |= RTE_ETH_DEV_AUTOFILL_QUEUE_XSTATS;
+ priv->ready = true;
+ } else {
+ if (!priv->ready) {
+ ret = -EBADFD;
+ goto err_secondary_not_ready;
+ }
}
NFB_LOG(INFO, "NFB device (" PCI_PRI_FMT ") successfully initialized",
@@ -608,6 +629,9 @@ nfb_eth_dev_init(struct rte_eth_dev *dev)
return 0;
err_malloc_mac_addrs:
+ rte_free(priv->queue_map_rx);
+err_alloc_queue_map:
+err_secondary_not_ready:
nfb_nc_rxmac_deinit(internals->rxmac, internals->max_rxmac);
nfb_nc_txmac_deinit(internals->txmac, internals->max_txmac);
nfb_close(internals->nfb);
@@ -633,6 +657,10 @@ nfb_eth_dev_uninit(struct rte_eth_dev *dev)
struct rte_pci_device *pci_dev = RTE_ETH_DEV_TO_PCI(dev);
struct rte_pci_addr *pci_addr = &pci_dev->addr;
struct pmd_internals *internals = dev->process_private;
+ struct pmd_priv *priv = dev->data->dev_private;
+
+ if (rte_eal_process_type() == RTE_PROC_PRIMARY)
+ rte_free(priv->queue_map_rx);
nfb_nc_rxmac_deinit(internals->rxmac, internals->max_rxmac);
nfb_nc_txmac_deinit(internals->txmac, internals->max_txmac);
diff --git a/drivers/net/nfb/nfb_rx.c b/drivers/net/nfb/nfb_rx.c
index 413d275853..5b8579e280 100644
--- a/drivers/net/nfb/nfb_rx.c
+++ b/drivers/net/nfb/nfb_rx.c
@@ -61,12 +61,16 @@ nfb_eth_rx_queue_setup(struct rte_eth_dev *dev,
struct rte_mempool *mb_pool)
{
struct pmd_internals *internals = dev->process_private;
+ struct pmd_priv *priv = dev->data->dev_private;
- struct ndp_rx_queue *rxq;
int ret;
+ int qid;
+ struct ndp_rx_queue *rxq;
+
+ if (rx_queue_id >= priv->max_rx_queues)
+ return -EINVAL;
- rxq = rte_zmalloc_socket("ndp rx queue",
- sizeof(struct ndp_rx_queue),
+ rxq = rte_zmalloc_socket("ndp rx queue", sizeof(struct ndp_rx_queue),
RTE_CACHE_LINE_SIZE, socket_id);
if (rxq == NULL) {
@@ -77,23 +81,23 @@ nfb_eth_rx_queue_setup(struct rte_eth_dev *dev,
rxq->flags = 0;
- ret = nfb_eth_rx_queue_init(internals->nfb,
- rx_queue_id,
- dev->data->port_id,
- mb_pool,
- rxq);
+ qid = priv->queue_map_rx[rx_queue_id];
- if (ret == 0)
- dev->data->rx_queues[rx_queue_id] = rxq;
- else
- rte_free(rxq);
+ ret = nfb_eth_rx_queue_init(internals->nfb, qid, dev->data->port_id, mb_pool, rxq);
+ if (ret)
+ goto err_queue_init;
+
+ dev->data->rx_queues[rx_queue_id] = rxq;
+ return 0;
+err_queue_init:
+ rte_free(rxq);
return ret;
}
int
nfb_eth_rx_queue_init(struct nfb_device *nfb,
- uint16_t rx_queue_id,
+ int qid,
uint16_t port_id,
struct rte_mempool *mb_pool,
struct ndp_rx_queue *rxq)
@@ -104,12 +108,11 @@ nfb_eth_rx_queue_init(struct nfb_device *nfb,
if (nfb == NULL)
return -EINVAL;
- rxq->queue = ndp_open_rx_queue(nfb, rx_queue_id);
+ rxq->queue = ndp_open_rx_queue(nfb, qid);
if (rxq->queue == NULL)
return -EINVAL;
rxq->nfb = nfb;
- rxq->rx_queue_id = rx_queue_id;
rxq->in_port = port_id;
rxq->mb_pool = mb_pool;
rxq->buf_size = (uint16_t)(mbp_priv->mbuf_data_room_size -
diff --git a/drivers/net/nfb/nfb_rx.h b/drivers/net/nfb/nfb_rx.h
index 67b3b00e2a..831945c577 100644
--- a/drivers/net/nfb/nfb_rx.h
+++ b/drivers/net/nfb/nfb_rx.h
@@ -30,7 +30,6 @@ nfb_timestamp_dynfield(struct rte_mbuf *mbuf)
struct ndp_rx_queue {
struct nfb_device *nfb; /* nfb dev structure */
struct ndp_queue *queue; /* rx queue */
- uint16_t rx_queue_id; /* index */
uint8_t in_port; /* port */
uint8_t flags; /* setup flags */
@@ -47,8 +46,8 @@ struct ndp_rx_queue {
*
* @param nfb
* Pointer to nfb device structure.
- * @param rx_queue_id
- * RX queue index.
+ * @param qid
+ * RX queue ID.
* @param port_id
* Device [external] port identifier.
* @param mb_pool
@@ -60,7 +59,7 @@ struct ndp_rx_queue {
*/
int
nfb_eth_rx_queue_init(struct nfb_device *nfb,
- uint16_t rx_queue_id,
+ int qid,
uint16_t port_id,
struct rte_mempool *mb_pool,
struct ndp_rx_queue *rxq);
@@ -70,7 +69,7 @@ nfb_eth_rx_queue_init(struct nfb_device *nfb,
*
* @param dev
* Pointer to Ethernet device structure.
- * @param idx
+ * @param rx_queue_id
* RX queue index.
* @param desc
* Number of descriptors to configure in queue.
diff --git a/drivers/net/nfb/nfb_tx.c b/drivers/net/nfb/nfb_tx.c
index 1f997ce22f..1a7dcc3d30 100644
--- a/drivers/net/nfb/nfb_tx.c
+++ b/drivers/net/nfb/nfb_tx.c
@@ -54,11 +54,16 @@ nfb_eth_tx_queue_setup(struct rte_eth_dev *dev,
const struct rte_eth_txconf *tx_conf __rte_unused)
{
struct pmd_internals *internals = dev->process_private;
+ struct pmd_priv *priv = dev->data->dev_private;
+
int ret;
+ int qid;
struct ndp_tx_queue *txq;
- txq = rte_zmalloc_socket("ndp tx queue",
- sizeof(struct ndp_tx_queue),
+ if (tx_queue_id >= priv->max_tx_queues)
+ return -EINVAL;
+
+ txq = rte_zmalloc_socket("ndp tx queue", sizeof(struct ndp_tx_queue),
RTE_CACHE_LINE_SIZE, socket_id);
if (txq == NULL) {
@@ -67,32 +72,33 @@ nfb_eth_tx_queue_setup(struct rte_eth_dev *dev,
return -ENOMEM;
}
- ret = nfb_eth_tx_queue_init(internals->nfb,
- tx_queue_id,
- txq);
+ qid = priv->queue_map_tx[tx_queue_id];
- if (ret == 0)
- dev->data->tx_queues[tx_queue_id] = txq;
- else
- rte_free(txq);
+ ret = nfb_eth_tx_queue_init(internals->nfb, qid, txq);
+ if (ret)
+ goto err_queue_init;
+ dev->data->tx_queues[tx_queue_id] = txq;
+ return 0;
+
+err_queue_init:
+ rte_free(txq);
return ret;
}
int
nfb_eth_tx_queue_init(struct nfb_device *nfb,
- uint16_t tx_queue_id,
+ int qid,
struct ndp_tx_queue *txq)
{
if (nfb == NULL)
return -EINVAL;
- txq->queue = ndp_open_tx_queue(nfb, tx_queue_id);
+ txq->queue = ndp_open_tx_queue(nfb, qid);
if (txq->queue == NULL)
return -EINVAL;
txq->nfb = nfb;
- txq->tx_queue_id = tx_queue_id;
txq->tx_pkts = 0;
txq->tx_bytes = 0;
diff --git a/drivers/net/nfb/nfb_tx.h b/drivers/net/nfb/nfb_tx.h
index f107cf914b..c253af1a86 100644
--- a/drivers/net/nfb/nfb_tx.h
+++ b/drivers/net/nfb/nfb_tx.h
@@ -17,7 +17,6 @@
struct ndp_tx_queue {
struct nfb_device *nfb; /* nfb dev structure */
struct ndp_queue *queue; /* tx queue */
- uint16_t tx_queue_id; /* index */
volatile uint64_t tx_pkts; /* packets transmitted */
volatile uint64_t tx_bytes; /* bytes transmitted */
volatile uint64_t err_pkts; /* erroneous packets */
@@ -54,8 +53,8 @@ nfb_eth_tx_queue_setup(struct rte_eth_dev *dev,
*
* @param nfb
* Pointer to nfb device structure.
- * @param tx_queue_id
- * TX queue index.
+ * @param qid
+ * TX queue ID.
* @param[out] txq
* Pointer to ndp_tx_queue output structure
*
@@ -64,7 +63,7 @@ nfb_eth_tx_queue_setup(struct rte_eth_dev *dev,
*/
int
nfb_eth_tx_queue_init(struct nfb_device *nfb,
- uint16_t tx_queue_id,
+ int qid,
struct ndp_tx_queue *txq);
/**
--
2.53.0
^ permalink raw reply related [flat|nested] 131+ messages in thread
* [PATCH v10 2/8] net/nfb: create one ethdev per ethernet port
2026-02-17 7:10 ` [PATCH v10 " spinler
2026-02-17 7:10 ` [PATCH v10 1/8] net/nfb: prepare for indirect queue mapping scheme spinler
@ 2026-02-17 7:10 ` spinler
2026-02-17 7:10 ` [PATCH v10 3/8] net/nfb: add vdev as alternative device probe method spinler
` (7 subsequent siblings)
9 siblings, 0 replies; 131+ messages in thread
From: spinler @ 2026-02-17 7:10 UTC (permalink / raw)
To: dev; +Cc: Martin Spinler
From: Martin Spinler <spinler@cesnet.cz>
The existing driver creates one ethdev/port for one PCI device.
As the CESNET-NDK-based cards are mostly unable to represent
each physical port with one PCI device, this atypical behaviour
often limits the user.
The nfb-framework comes with generic port representation API,
which provides information about ports and their mapping to firmware
resource (physical ports, DMA queues, processing pipelines).
This driver design switches from one rte_eth_dev_pci_generic_probe call
to multiple rte_eth_dev_create calls, while parses the firmware mapping
information.
Signed-off-by: Martin Spinler <spinler@cesnet.cz>
---
drivers/net/nfb/nfb.h | 25 ++++
drivers/net/nfb/nfb_ethdev.c | 232 ++++++++++++++++++++++++++++-------
2 files changed, 216 insertions(+), 41 deletions(-)
diff --git a/drivers/net/nfb/nfb.h b/drivers/net/nfb/nfb.h
index fa51afc7de..a854d16426 100644
--- a/drivers/net/nfb/nfb.h
+++ b/drivers/net/nfb/nfb.h
@@ -11,6 +11,7 @@
#include <nfb/ndp.h>
#include <netcope/rxmac.h>
#include <netcope/txmac.h>
+#include <netcope/info.h>
extern int nfb_logtype;
#define RTE_LOGTYPE_NFB nfb_logtype
@@ -51,6 +52,9 @@ struct pmd_internals {
struct nc_rxmac *rxmac[RTE_MAX_NC_RXMAC];
struct nc_txmac *txmac[RTE_MAX_NC_TXMAC];
struct nfb_device *nfb;
+
+ TAILQ_ENTRY(pmd_internals) eth_dev_list; /**< Item in list of all devices */
+ struct rte_eth_dev *eth_dev; /**< Handle for matching the device being removed */
};
/*
@@ -68,4 +72,25 @@ struct pmd_priv {
bool ready; /**< This structure is initialized for usage in secondary process */
};
+/* Data for common device probing */
+struct nfb_probe_params {
+ /** Generic device information */
+ struct rte_device *device;
+ /** Port bus specific initialisation callback function */
+ ethdev_bus_specific_init specific_init;
+ /** Port bus specific initialisation parameters */
+ void *specific_device;
+
+ const char *path; /**< libnfb device string */
+ const char *args; /**< Device arguments */
+ int nfb_id; /**< ID of NFB device in system */
+ int ep_index; /**< (PCIe) endpoint index for matching assigned interfaces */
+
+ char name[RTE_DEV_NAME_MAX_LEN]; /**< Probed name (e.g. PCI device name) */
+};
+
+
+int nfb_eth_common_probe(struct nfb_probe_params *params);
+int nfb_eth_common_remove(struct rte_device *dev);
+
#endif /* _NFB_H_ */
diff --git a/drivers/net/nfb/nfb_ethdev.c b/drivers/net/nfb/nfb_ethdev.c
index c0a2905249..233eb91e18 100644
--- a/drivers/net/nfb/nfb_ethdev.c
+++ b/drivers/net/nfb/nfb_ethdev.c
@@ -4,6 +4,10 @@
* All rights reserved.
*/
+#include <sys/queue.h>
+#include <eal_export.h>
+#include <rte_tailq.h>
+
#include <nfb/nfb.h>
#include <nfb/ndp.h>
#include <netcope/rxmac.h>
@@ -18,6 +22,21 @@
#include "nfb_rxmode.h"
#include "nfb.h"
+struct nfb_ifc_create_params {
+ struct nfb_probe_params *probe_params;
+ struct nc_ifc_map_info map_info;
+ struct nc_ifc_info *ifc_info;
+
+ int basename_len; /* Cached real length of original probe_params->name */
+};
+
+/* The TAILQ entries are used for cleanup of allocated resources
+ * in situations, where dev_close is not called.
+ */
+TAILQ_HEAD(nfb_pmd_internals_head, pmd_internals);
+static struct nfb_pmd_internals_head nfb_eth_dev_list =
+ TAILQ_HEAD_INITIALIZER(nfb_eth_dev_list);
+
static int nfb_eth_dev_uninit(struct rte_eth_dev *dev);
/**
@@ -507,23 +526,20 @@ static const struct eth_dev_ops ops = {
* 0 on success, a negative errno value otherwise.
*/
static int
-nfb_eth_dev_init(struct rte_eth_dev *dev)
+nfb_eth_dev_init(struct rte_eth_dev *dev, void *init_data)
{
int i;
+ int cnt;
int ret;
uint32_t mac_count;
struct rte_eth_dev_data *data = dev->data;
struct pmd_internals *internals;
+ struct nfb_ifc_create_params *params = init_data;
+ struct nc_ifc_info *ifc = params->ifc_info;
+ struct nc_ifc_map_info *mi = ¶ms->map_info;
struct pmd_priv *priv = data->dev_private;
- struct rte_pci_device *pci_dev = RTE_ETH_DEV_TO_PCI(dev);
- struct rte_pci_addr *pci_addr = &pci_dev->addr;
struct rte_ether_addr eth_addr_init;
uint16_t max_rx_queues, max_tx_queues;
- char nfb_dev[PATH_MAX];
-
- NFB_LOG(INFO, "Initializing NFB device (" PCI_PRI_FMT ")",
- pci_addr->domain, pci_addr->bus, pci_addr->devid,
- pci_addr->function);
internals = rte_zmalloc_socket("nfb_internals",
sizeof(struct pmd_internals), RTE_CACHE_LINE_SIZE,
@@ -535,27 +551,17 @@ nfb_eth_dev_init(struct rte_eth_dev *dev)
dev->process_private = internals;
- snprintf(nfb_dev, sizeof(nfb_dev),
- "/dev/nfb/by-pci-slot/" PCI_PRI_FMT,
- pci_addr->domain, pci_addr->bus, pci_addr->devid,
- pci_addr->function);
-
- /*
- * Get number of available DMA RX and TX queues, which is maximum
- * number of queues that can be created and store it in private device
- * data structure.
- */
- internals->nfb = nfb_open(nfb_dev);
+ /* Open device handle */
+ internals->nfb = nfb_open(params->probe_params->path);
if (internals->nfb == NULL) {
- NFB_LOG(ERR, "nfb_open(): failed to open %s", nfb_dev);
+ NFB_LOG(ERR, "nfb_open(): failed to open %s", params->probe_params->path);
ret = -EINVAL;
goto err_nfb_open;
}
- max_rx_queues = ndp_get_rx_queue_available_count(internals->nfb);
- max_tx_queues = ndp_get_tx_queue_available_count(internals->nfb);
- NFB_LOG(INFO, "Available NDP queues RX: %u TX: %u",
- max_rx_queues, max_tx_queues);
+ /* Get number of available DMA RX and TX queues */
+ max_rx_queues = ifc->rxq_cnt;
+ max_tx_queues = ifc->txq_cnt;
nfb_nc_rxmac_init(internals->nfb,
internals->rxmac,
@@ -586,11 +592,30 @@ nfb_eth_dev_init(struct rte_eth_dev *dev)
}
priv->queue_map_tx = priv->queue_map_rx + max_rx_queues;
- /* default queue mapping is 1:1 */
- for (i = 0; i < max_rx_queues; i++)
- priv->queue_map_rx[i] = i;
- for (i = 0; i < max_tx_queues; i++)
- priv->queue_map_tx[i] = i;
+ /* Use queue mapping provided by libnfb */
+ cnt = 0;
+ for (i = 0; i < mi->rxq_cnt; i++) {
+ if (mi->rxq[i].ifc == ifc->id) {
+ if (cnt >= max_rx_queues) {
+ NFB_LOG(ERR, "RX queue mapping inconsistent");
+ ret = -EINVAL;
+ goto err_queue_map;
+ }
+ priv->queue_map_rx[cnt++] = mi->rxq[i].id;
+ }
+ }
+
+ cnt = 0;
+ for (i = 0; i < mi->txq_cnt; i++) {
+ if (mi->txq[i].ifc == ifc->id) {
+ if (cnt >= max_tx_queues) {
+ NFB_LOG(ERR, "TX queue mapping inconsistent");
+ ret = -EINVAL;
+ goto err_queue_map;
+ }
+ priv->queue_map_tx[cnt++] = mi->txq[i].id;
+ }
+ }
/* Allocate space for MAC addresses */
mac_count = nfb_eth_get_max_mac_address_count(dev);
@@ -622,13 +647,10 @@ nfb_eth_dev_init(struct rte_eth_dev *dev)
}
}
- NFB_LOG(INFO, "NFB device (" PCI_PRI_FMT ") successfully initialized",
- pci_addr->domain, pci_addr->bus, pci_addr->devid,
- pci_addr->function);
-
return 0;
err_malloc_mac_addrs:
+err_queue_map:
rte_free(priv->queue_map_rx);
err_alloc_queue_map:
err_secondary_not_ready:
@@ -654,25 +676,111 @@ nfb_eth_dev_init(struct rte_eth_dev *dev)
static int
nfb_eth_dev_uninit(struct rte_eth_dev *dev)
{
- struct rte_pci_device *pci_dev = RTE_ETH_DEV_TO_PCI(dev);
- struct rte_pci_addr *pci_addr = &pci_dev->addr;
struct pmd_internals *internals = dev->process_private;
struct pmd_priv *priv = dev->data->dev_private;
if (rte_eal_process_type() == RTE_PROC_PRIMARY)
rte_free(priv->queue_map_rx);
+ TAILQ_REMOVE(&nfb_eth_dev_list, internals, eth_dev_list);
+
nfb_nc_rxmac_deinit(internals->rxmac, internals->max_rxmac);
nfb_nc_txmac_deinit(internals->txmac, internals->max_txmac);
nfb_close(internals->nfb);
rte_free(internals);
- NFB_LOG(INFO, "NFB device (" PCI_PRI_FMT ") successfully uninitialized",
- pci_addr->domain, pci_addr->bus, pci_addr->devid,
- pci_addr->function);
+ return 0;
+}
+
+static int
+nfb_eth_dev_create_for_ifc(struct nfb_ifc_create_params *cp)
+{
+ int ret;
+ struct nc_ifc_info *ifc;
+ struct nfb_probe_params *pp;
+ struct rte_eth_dev *eth_dev;
+ struct pmd_internals *p;
+
+ ifc = cp->ifc_info;
+ pp = cp->probe_params;
+
+ /* Skip interfaces which doesn't belong to the probed PCI device */
+ if ((pp->ep_index != -1 && ifc->ep != pp->ep_index) ||
+ (ifc->flags & NC_IFC_INFO_FLAG_ACTIVE) == 0)
+ return 0;
+
+ ret = snprintf(pp->name + cp->basename_len, sizeof(pp->name) - cp->basename_len,
+ "_eth%d", ifc->id);
+ if (ret < 0 || ret >= (signed int)sizeof(pp->name) - cp->basename_len)
+ return -EINVAL;
+
+ ret = rte_eth_dev_create(pp->device, pp->name,
+ sizeof(struct pmd_priv),
+ pp->specific_init, pp->specific_device,
+ nfb_eth_dev_init, cp);
+ if (ret)
+ goto out;
+
+ eth_dev = rte_eth_dev_get_by_name(pp->name);
+ if (eth_dev == NULL) {
+ NFB_LOG(ERR, "Failed to get created device %s", pp->name);
+ ret = -ENODEV;
+ goto out;
+ }
+ p = eth_dev->process_private;
+ p->eth_dev = eth_dev;
+ TAILQ_INSERT_TAIL(&nfb_eth_dev_list, p, eth_dev_list);
+
+out:
+ /* return to original name (just for clarity) */
+ pp->name[cp->basename_len] = '\0';
+ return ret;
+}
+
+int
+nfb_eth_common_probe(struct nfb_probe_params *params)
+{
+ int i;
+ int ret;
+
+ struct nfb_device *nfb_dev;
+ struct nfb_ifc_create_params ifc_params;
+
+ /* Open the device handle just for parsing ifc_params.
+ * A separate handle is used later for each netdev.
+ */
+ nfb_dev = nfb_open(params->path);
+ if (nfb_dev == NULL) {
+ NFB_LOG(ERR, "nfb_open(): failed to open %s", params->path);
+ return -EINVAL;
+ }
+
+ ret = nc_ifc_map_info_create_ordinary(nfb_dev, &ifc_params.map_info);
+ if (ret)
+ goto err_map_info_create;
+
+ ifc_params.probe_params = params;
+ ifc_params.basename_len = strlen(params->name);
+
+ for (i = 0; i < ifc_params.map_info.ifc_cnt; i++) {
+ ifc_params.ifc_info = &ifc_params.map_info.ifc[i];
+ ret = nfb_eth_dev_create_for_ifc(&ifc_params);
+ if (ret)
+ goto err_dev_create;
+ }
+
+ nc_map_info_destroy(&ifc_params.map_info);
+ nfb_close(nfb_dev);
return 0;
+
+err_dev_create:
+ nfb_eth_common_remove(params->device);
+ nc_map_info_destroy(&ifc_params.map_info);
+err_map_info_create:
+ nfb_close(nfb_dev);
+ return ret;
}
static const struct rte_pci_id nfb_pci_id_table[] = {
@@ -701,8 +809,50 @@ static int
nfb_eth_pci_probe(struct rte_pci_driver *pci_drv __rte_unused,
struct rte_pci_device *pci_dev)
{
- return rte_eth_dev_pci_generic_probe(pci_dev,
- sizeof(struct pmd_priv), nfb_eth_dev_init);
+ int ret;
+ char path[PATH_MAX];
+
+ struct nc_composed_device_info comp_dev_info;
+ struct nfb_probe_params params = {0};
+
+ rte_pci_device_name(&pci_dev->addr, params.name, sizeof(params.name));
+
+ /*
+ * NFB device can be composed from multiple PCI devices,
+ * find the base char device ID for the current PCI device
+ */
+ ret = nc_get_composed_device_info_by_pci(NULL, params.name, &comp_dev_info);
+ if (ret) {
+ NFB_LOG(ERR, "Could not find NFB device for %s", params.name);
+ return -ENODEV;
+ }
+
+ ret = snprintf(path, sizeof(path), NFB_BASE_DEV_PATH "%d", comp_dev_info.nfb_id);
+ if (ret < 0 || ret >= (signed int)sizeof(path))
+ return -EINVAL;
+
+ params.args = pci_dev->device.devargs ? pci_dev->device.devargs->args : NULL;
+ params.path = path;
+ params.nfb_id = comp_dev_info.nfb_id;
+ params.ep_index = comp_dev_info.ep_index;
+
+ params.device = &pci_dev->device;
+ params.specific_init = eth_dev_pci_specific_init;
+ params.specific_device = pci_dev;
+
+ return nfb_eth_common_probe(¶ms);
+}
+
+int
+nfb_eth_common_remove(struct rte_device *dev)
+{
+ struct pmd_internals *entry, *temp;
+
+ RTE_TAILQ_FOREACH_SAFE(entry, &nfb_eth_dev_list, eth_dev_list, temp) {
+ if (dev == entry->eth_dev->device)
+ rte_eth_dev_destroy(entry->eth_dev, nfb_eth_dev_uninit);
+ }
+ return 0;
}
/**
@@ -719,7 +869,7 @@ nfb_eth_pci_probe(struct rte_pci_driver *pci_drv __rte_unused,
static int
nfb_eth_pci_remove(struct rte_pci_device *pci_dev)
{
- return rte_eth_dev_pci_generic_remove(pci_dev, nfb_eth_dev_uninit);
+ return nfb_eth_common_remove(&pci_dev->device);
}
static struct rte_pci_driver nfb_eth_driver = {
--
2.53.0
^ permalink raw reply related [flat|nested] 131+ messages in thread
* [PATCH v10 3/8] net/nfb: add vdev as alternative device probe method
2026-02-17 7:10 ` [PATCH v10 " spinler
2026-02-17 7:10 ` [PATCH v10 1/8] net/nfb: prepare for indirect queue mapping scheme spinler
2026-02-17 7:10 ` [PATCH v10 2/8] net/nfb: create one ethdev per ethernet port spinler
@ 2026-02-17 7:10 ` spinler
2026-02-17 7:10 ` [PATCH v10 4/8] net/nfb: add device argument "port" to limit used ports spinler
` (6 subsequent siblings)
9 siblings, 0 replies; 131+ messages in thread
From: spinler @ 2026-02-17 7:10 UTC (permalink / raw)
To: dev; +Cc: Martin Spinler
From: Martin Spinler <spinler@cesnet.cz>
VDEV are useful for NFB devices not represented by PCI device,
e.g. virtual or simulated devices.
Signed-off-by: Martin Spinler <spinler@cesnet.cz>
---
drivers/net/nfb/meson.build | 1 +
drivers/net/nfb/nfb_vdev.c | 106 ++++++++++++++++++++++++++++++++++++
2 files changed, 107 insertions(+)
create mode 100644 drivers/net/nfb/nfb_vdev.c
diff --git a/drivers/net/nfb/meson.build b/drivers/net/nfb/meson.build
index d7a255c928..9e458dfb4a 100644
--- a/drivers/net/nfb/meson.build
+++ b/drivers/net/nfb/meson.build
@@ -16,6 +16,7 @@ ext_deps += dep
sources = files(
'nfb_ethdev.c',
+ 'nfb_vdev.c',
'nfb_rx.c',
'nfb_rxmode.c',
'nfb_stats.c',
diff --git a/drivers/net/nfb/nfb_vdev.c b/drivers/net/nfb/nfb_vdev.c
new file mode 100644
index 0000000000..b79f7ac416
--- /dev/null
+++ b/drivers/net/nfb/nfb_vdev.c
@@ -0,0 +1,106 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2026 Cesnet
+ */
+
+#include <rte_kvargs.h>
+#include <ethdev_vdev.h>
+
+#include "nfb.h"
+
+#define VDEV_NFB_DRIVER net_vdev_nfb
+#define VDEV_NFB_ARG_DEV "dev"
+
+static int
+vdev_nfb_vdev_probe(struct rte_vdev_device *dev)
+{
+ unsigned int count;
+ int ret = 0;
+ size_t len, pos;
+
+ struct nfb_probe_params params;
+
+ const char *vdev_name = rte_vdev_device_name(dev);
+ const char *vdev_args = rte_vdev_device_args(dev);
+ char *dev_params, *dev_params_mod;
+ struct rte_kvargs *kvargs;
+
+ kvargs = rte_kvargs_parse(vdev_args, NULL);
+ if (kvargs == NULL) {
+ NFB_LOG(ERR, "Failed to parse device arguments %s", vdev_args);
+ ret = -EINVAL;
+ goto err_parse_args;
+ }
+
+ dev_params = strdup(vdev_args);
+ if (dev_params == NULL) {
+ ret = -ENOMEM;
+ goto err_strdup_params;
+ }
+
+ params.device = &dev->device;
+ params.specific_init = NULL;
+ params.specific_device = NULL;
+ params.path = nfb_default_dev_path();
+ params.args = dev_params;
+ params.nfb_id = 0;
+ params.ep_index = -1;
+
+ len = strlen(dev_params) + 1;
+ pos = 0;
+ dev_params[pos] = '\0';
+
+ /* Parse parameters for virtual device */
+ for (count = 0; count != kvargs->count; ++count) {
+ const struct rte_kvargs_pair *pair = &kvargs->pairs[count];
+
+ if (!strcmp(pair->key, VDEV_NFB_ARG_DEV)) {
+ params.path = pair->value;
+ } else {
+ /* Clone non-vdev arguments, result is shorter or equal length */
+ dev_params_mod = dev_params + pos;
+ ret = snprintf(dev_params_mod, len, "%s%s=%s",
+ pos == 0 ? "" : ",", pair->key, pair->value);
+ if (ret < 0 || ret >= (signed int)len)
+ goto err_clone_args;
+ pos += ret;
+ len -= ret;
+ }
+ }
+
+ strlcpy(params.name, vdev_name, sizeof(params.name));
+
+ ret = nfb_eth_common_probe(¶ms);
+ if (ret)
+ goto err_nfb_common_probe;
+
+ free(dev_params);
+ rte_kvargs_free(kvargs);
+
+ return ret;
+
+err_nfb_common_probe:
+err_clone_args:
+ free(dev_params);
+err_strdup_params:
+ rte_kvargs_free(kvargs);
+err_parse_args:
+ return ret;
+}
+
+static int
+vdev_nfb_vdev_remove(struct rte_vdev_device *dev)
+{
+ return nfb_eth_common_remove(&dev->device);
+}
+
+/** Virtual device descriptor. */
+static struct rte_vdev_driver vdev_nfb_vdev = {
+ .probe = vdev_nfb_vdev_probe,
+ .remove = vdev_nfb_vdev_remove,
+};
+
+RTE_PMD_REGISTER_VDEV(VDEV_NFB_DRIVER, vdev_nfb_vdev);
+RTE_PMD_REGISTER_ALIAS(VDEV_NFB_DRIVER, eth_vdev_nfb);
+RTE_PMD_REGISTER_PARAM_STRING(net_vdev_nfb,
+ VDEV_NFB_ARG_DEV "=<string>"
+ );
--
2.53.0
^ permalink raw reply related [flat|nested] 131+ messages in thread
* [PATCH v10 4/8] net/nfb: add device argument "port" to limit used ports
2026-02-17 7:10 ` [PATCH v10 " spinler
` (2 preceding siblings ...)
2026-02-17 7:10 ` [PATCH v10 3/8] net/nfb: add vdev as alternative device probe method spinler
@ 2026-02-17 7:10 ` spinler
2026-02-17 7:10 ` [PATCH v10 5/8] net/nfb: init only MACs associated with device spinler
` (5 subsequent siblings)
9 siblings, 0 replies; 131+ messages in thread
From: spinler @ 2026-02-17 7:10 UTC (permalink / raw)
To: dev; +Cc: Martin Spinler
From: Martin Spinler <spinler@cesnet.cz>
NFB devices do not use separate PCI device for each port; one PCI device
contains handles for all ports instead. For some application this approach
can be limiting.
The "port" argument can be used to select only desired ports.
It can be used multiple times. When is not used at all, the driver
selects all ports.
Signed-off-by: Martin Spinler <spinler@cesnet.cz>
---
drivers/net/nfb/nfb.h | 7 ++++
drivers/net/nfb/nfb_ethdev.c | 71 ++++++++++++++++++++++++++++++++++--
drivers/net/nfb/nfb_vdev.c | 3 +-
3 files changed, 76 insertions(+), 5 deletions(-)
diff --git a/drivers/net/nfb/nfb.h b/drivers/net/nfb/nfb.h
index a854d16426..22fcb68cd7 100644
--- a/drivers/net/nfb/nfb.h
+++ b/drivers/net/nfb/nfb.h
@@ -42,6 +42,13 @@ extern int nfb_logtype;
#define RTE_NFB_DRIVER_NAME net_nfb
+/* Device arguments */
+#define NFB_ARG_PORT "port"
+
+#define NFB_COMMON_ARGS \
+ NFB_ARG_PORT "=<number>" \
+ ""
+
/*
* Handles obtained from the libnfb: each process must use own instance.
* Stored inside dev->process_private.
diff --git a/drivers/net/nfb/nfb_ethdev.c b/drivers/net/nfb/nfb_ethdev.c
index 233eb91e18..706b90bae5 100644
--- a/drivers/net/nfb/nfb_ethdev.c
+++ b/drivers/net/nfb/nfb_ethdev.c
@@ -4,6 +4,7 @@
* All rights reserved.
*/
+#include <ctype.h>
#include <sys/queue.h>
#include <eal_export.h>
#include <rte_tailq.h>
@@ -22,12 +23,19 @@
#include "nfb_rxmode.h"
#include "nfb.h"
+static const char * const VALID_KEYS[] = {
+ NFB_ARG_PORT,
+ NULL
+};
+
struct nfb_ifc_create_params {
struct nfb_probe_params *probe_params;
struct nc_ifc_map_info map_info;
struct nc_ifc_info *ifc_info;
int basename_len; /* Cached real length of original probe_params->name */
+ /* Return value of failed nfb_eth_dev_create_for_ifc when rte_kvargs_process is used */
+ int ret;
};
/* The TAILQ entries are used for cleanup of allocated resources
@@ -738,6 +746,33 @@ nfb_eth_dev_create_for_ifc(struct nfb_ifc_create_params *cp)
return ret;
}
+static int nfb_eth_dev_create_for_ifc_by_port(const char *key __rte_unused,
+ const char *value, void *opaque)
+{
+ int ret = -EINVAL;
+ char *end;
+ unsigned long port;
+ struct nfb_ifc_create_params *ifc_params = opaque;
+
+ if (value == NULL || strlen(value) == 0 || !isdigit(*value))
+ goto out;
+
+ errno = 0;
+ port = strtoul(value, &end, 10);
+ if (errno != 0 || *end != '\0')
+ goto out;
+
+ if (port >= LONG_MAX || port >= (unsigned long)ifc_params->map_info.ifc_cnt)
+ goto out;
+
+ ifc_params->ifc_info = &ifc_params->map_info.ifc[port];
+ ret = nfb_eth_dev_create_for_ifc(ifc_params);
+
+out:
+ ifc_params->ret = ret;
+ return ret;
+}
+
int
nfb_eth_common_probe(struct nfb_probe_params *params)
{
@@ -746,6 +781,7 @@ nfb_eth_common_probe(struct nfb_probe_params *params)
struct nfb_device *nfb_dev;
struct nfb_ifc_create_params ifc_params;
+ struct rte_kvargs *kvlist = NULL;
/* Open the device handle just for parsing ifc_params.
* A separate handle is used later for each netdev.
@@ -760,16 +796,38 @@ nfb_eth_common_probe(struct nfb_probe_params *params)
if (ret)
goto err_map_info_create;
+ if (params->args != NULL && strlen(params->args) > 0) {
+ kvlist = rte_kvargs_parse(params->args, VALID_KEYS);
+ if (kvlist == NULL) {
+ NFB_LOG(ERR, "Failed to parse device arguments %s", params->args);
+ ret = -EINVAL;
+ goto err_parse_args;
+ }
+ }
+
+ ifc_params.ret = 0;
ifc_params.probe_params = params;
ifc_params.basename_len = strlen(params->name);
- for (i = 0; i < ifc_params.map_info.ifc_cnt; i++) {
- ifc_params.ifc_info = &ifc_params.map_info.ifc[i];
- ret = nfb_eth_dev_create_for_ifc(&ifc_params);
- if (ret)
+ /* When at least one port argument is specified, create only selected ports */
+ if (kvlist && rte_kvargs_count(kvlist, NFB_ARG_PORT)) {
+ ret = rte_kvargs_process(kvlist, NFB_ARG_PORT,
+ nfb_eth_dev_create_for_ifc_by_port, (void *)&ifc_params);
+ if (ret) {
+ ret = ifc_params.ret;
goto err_dev_create;
+ }
+ } else {
+ for (i = 0; i < ifc_params.map_info.ifc_cnt; i++) {
+ ifc_params.ifc_info = &ifc_params.map_info.ifc[i];
+ ret = nfb_eth_dev_create_for_ifc(&ifc_params);
+ if (ret)
+ goto err_dev_create;
+ }
}
+ rte_kvargs_free(kvlist);
+
nc_map_info_destroy(&ifc_params.map_info);
nfb_close(nfb_dev);
@@ -777,6 +835,8 @@ nfb_eth_common_probe(struct nfb_probe_params *params)
err_dev_create:
nfb_eth_common_remove(params->device);
+ rte_kvargs_free(kvlist);
+err_parse_args:
nc_map_info_destroy(&ifc_params.map_info);
err_map_info_create:
nfb_close(nfb_dev);
@@ -882,3 +942,6 @@ RTE_PMD_REGISTER_PCI(RTE_NFB_DRIVER_NAME, nfb_eth_driver);
RTE_PMD_REGISTER_PCI_TABLE(RTE_NFB_DRIVER_NAME, nfb_pci_id_table);
RTE_PMD_REGISTER_KMOD_DEP(RTE_NFB_DRIVER_NAME, "* nfb");
RTE_LOG_REGISTER_DEFAULT(nfb_logtype, NOTICE);
+RTE_PMD_REGISTER_PARAM_STRING(RTE_NFB_DRIVER_NAME,
+ NFB_COMMON_ARGS
+ );
diff --git a/drivers/net/nfb/nfb_vdev.c b/drivers/net/nfb/nfb_vdev.c
index b79f7ac416..57f10d2068 100644
--- a/drivers/net/nfb/nfb_vdev.c
+++ b/drivers/net/nfb/nfb_vdev.c
@@ -102,5 +102,6 @@ static struct rte_vdev_driver vdev_nfb_vdev = {
RTE_PMD_REGISTER_VDEV(VDEV_NFB_DRIVER, vdev_nfb_vdev);
RTE_PMD_REGISTER_ALIAS(VDEV_NFB_DRIVER, eth_vdev_nfb);
RTE_PMD_REGISTER_PARAM_STRING(net_vdev_nfb,
- VDEV_NFB_ARG_DEV "=<string>"
+ VDEV_NFB_ARG_DEV "=<string> "
+ NFB_COMMON_ARGS
);
--
2.53.0
^ permalink raw reply related [flat|nested] 131+ messages in thread
* [PATCH v10 5/8] net/nfb: init only MACs associated with device
2026-02-17 7:10 ` [PATCH v10 " spinler
` (3 preceding siblings ...)
2026-02-17 7:10 ` [PATCH v10 4/8] net/nfb: add device argument "port" to limit used ports spinler
@ 2026-02-17 7:10 ` spinler
2026-02-17 7:10 ` [PATCH v10 6/8] net/nfb: add compatible cards to driver PCI ID table spinler
` (4 subsequent siblings)
9 siblings, 0 replies; 131+ messages in thread
From: spinler @ 2026-02-17 7:10 UTC (permalink / raw)
To: dev; +Cc: Martin Spinler
From: Martin Spinler <spinler@cesnet.cz>
Use information obtained from the new manner of creating NFB ethdevs
and initialize only RxMACs / TxMACs associated with actual ethdev.
This enables separated management and configuration of interface ports.
Signed-off-by: Martin Spinler <spinler@cesnet.cz>
---
drivers/net/nfb/nfb.h | 12 +--
drivers/net/nfb/nfb_ethdev.c | 149 ++++++++++++++++++-----------------
2 files changed, 79 insertions(+), 82 deletions(-)
diff --git a/drivers/net/nfb/nfb.h b/drivers/net/nfb/nfb.h
index 22fcb68cd7..ea8e4fd255 100644
--- a/drivers/net/nfb/nfb.h
+++ b/drivers/net/nfb/nfb.h
@@ -36,10 +36,6 @@ extern int nfb_logtype;
#define RTE_ETH_NDP_MAX_RX_QUEUES 32
#define RTE_ETH_NDP_MAX_TX_QUEUES 32
-/* Max index of rx/tx dmas */
-#define RTE_MAX_NC_RXMAC 256
-#define RTE_MAX_NC_TXMAC 256
-
#define RTE_NFB_DRIVER_NAME net_nfb
/* Device arguments */
@@ -54,10 +50,10 @@ extern int nfb_logtype;
* Stored inside dev->process_private.
*/
struct pmd_internals {
- uint16_t max_rxmac;
- uint16_t max_txmac;
- struct nc_rxmac *rxmac[RTE_MAX_NC_RXMAC];
- struct nc_txmac *txmac[RTE_MAX_NC_TXMAC];
+ uint16_t max_rxmac; /**< Count of valid rxmac items */
+ uint16_t max_txmac; /**< Count of valid txmac items */
+ struct nc_rxmac **rxmac; /**< Array of Rx MAC handles */
+ struct nc_txmac **txmac; /**< Array of Tx MAC handles */
struct nfb_device *nfb;
TAILQ_ENTRY(pmd_internals) eth_dev_list; /**< Item in list of all devices */
diff --git a/drivers/net/nfb/nfb_ethdev.c b/drivers/net/nfb/nfb_ethdev.c
index 706b90bae5..5fadc863c5 100644
--- a/drivers/net/nfb/nfb_ethdev.c
+++ b/drivers/net/nfb/nfb_ethdev.c
@@ -55,81 +55,87 @@ static const struct rte_ether_addr eth_addr = {
};
/**
- * Open all RX DMA queues
+ * Open Rx and Tx MAC components associated with current ifc
*
- * @param dev
- * Pointer to nfb device.
- * @param[out] rxmac
- * Pointer to output array of nc_rxmac
- * @param[out] max_rxmac
- * Pointer to output max index of rxmac
+ * @param intl
+ * Pointer to driver internal structure
+ * @param params
+ * Pointer to init parameters structure
*/
-static void
-nfb_nc_rxmac_init(struct nfb_device *nfb,
- struct nc_rxmac *rxmac[RTE_MAX_NC_RXMAC],
- uint16_t *max_rxmac)
+static int
+nfb_nc_eth_init(struct pmd_internals *intl, struct nfb_ifc_create_params *params)
{
- *max_rxmac = 0;
- while ((rxmac[*max_rxmac] = nc_rxmac_open_index(nfb, *max_rxmac)))
- ++(*max_rxmac);
-}
+ int ret;
+ int i, rxm, txm;
+ struct nc_ifc_info *ifc = params->ifc_info;
+ struct nc_ifc_map_info *mi = ¶ms->map_info;
-/**
- * Open all TX DMA queues
- *
- * @param dev
- * Pointer to nfb device.
- * @param[out] txmac
- * Pointer to output array of nc_txmac
- * @param[out] max_rxmac
- * Pointer to output max index of txmac
- */
-static void
-nfb_nc_txmac_init(struct nfb_device *nfb,
- struct nc_txmac *txmac[RTE_MAX_NC_TXMAC],
- uint16_t *max_txmac)
-{
- *max_txmac = 0;
- while ((txmac[*max_txmac] = nc_txmac_open_index(nfb, *max_txmac)))
- ++(*max_txmac);
-}
+ ret = -ENOMEM;
+ if (ifc->eth_cnt == 0)
+ return 0;
-/**
- * Close all RX DMA queues
- *
- * @param rxmac
- * Pointer to array of nc_rxmac
- * @param max_rxmac
- * Maximum index of rxmac
- */
-static void
-nfb_nc_rxmac_deinit(struct nc_rxmac *rxmac[RTE_MAX_NC_RXMAC],
- uint16_t max_rxmac)
-{
- uint16_t i;
- for (i = 0; i < max_rxmac; i++) {
- nc_rxmac_close(rxmac[i]);
- rxmac[i] = NULL;
+ intl->rxmac = calloc(ifc->eth_cnt, sizeof(*intl->rxmac));
+ if (intl->rxmac == NULL)
+ goto err_alloc_rxmac;
+
+ intl->txmac = calloc(ifc->eth_cnt, sizeof(*intl->txmac));
+ if (intl->txmac == NULL)
+ goto err_alloc_txmac;
+
+ /* Some eths may not have assigned MAC nodes, hence use separate var for indexing */
+ rxm = 0;
+ txm = 0;
+ for (i = 0; i < mi->eth_cnt; i++) {
+ if (mi->eth[i].ifc != ifc->id)
+ continue;
+
+ if (rxm >= ifc->eth_cnt || txm >= ifc->eth_cnt) {
+ NFB_LOG(ERR, "MAC mapping inconsistent");
+ ret = -EINVAL;
+ goto err_map_inconsistent;
+ }
+
+ intl->rxmac[rxm] = nc_rxmac_open(intl->nfb, mi->eth[i].node_rxmac);
+ if (intl->rxmac[rxm])
+ rxm++;
+
+ intl->txmac[txm] = nc_txmac_open(intl->nfb, mi->eth[i].node_txmac);
+ if (intl->txmac[txm])
+ txm++;
}
+
+ intl->max_rxmac = rxm;
+ intl->max_txmac = txm;
+ return 0;
+
+err_map_inconsistent:
+ for (i = 0; i < txm; i++)
+ nc_txmac_close(intl->txmac[i]);
+ for (i = 0; i < rxm; i++)
+ nc_rxmac_close(intl->rxmac[i]);
+ free(intl->txmac);
+err_alloc_txmac:
+ free(intl->rxmac);
+err_alloc_rxmac:
+ return ret;
}
/**
- * Close all TX DMA queues
- *
- * @param txmac
- * Pointer to array of nc_txmac
- * @param max_txmac
- * Maximum index of txmac
+ * Close all Rx+Tx MAC components
+ * @param intl
+ * Pointer to internal driver structure
*/
static void
-nfb_nc_txmac_deinit(struct nc_txmac *txmac[RTE_MAX_NC_TXMAC],
- uint16_t max_txmac)
+nfb_nc_eth_deinit(struct pmd_internals *intl)
{
uint16_t i;
- for (i = 0; i < max_txmac; i++) {
- nc_txmac_close(txmac[i]);
- txmac[i] = NULL;
- }
+ for (i = 0; i < intl->max_txmac; i++)
+ nc_txmac_close(intl->txmac[i]);
+ for (i = 0; i < intl->max_rxmac; i++)
+ nc_rxmac_close(intl->rxmac[i]);
+
+ free(intl->txmac);
+ free(intl->rxmac);
}
/**
@@ -346,7 +352,7 @@ nfb_eth_link_update(struct rte_eth_dev *dev,
link.link_duplex = RTE_ETH_LINK_FULL_DUPLEX;
link.link_autoneg = RTE_ETH_LINK_SPEED_FIXED;
- if (internals->rxmac[0] != NULL) {
+ if (internals->max_rxmac) {
nc_rxmac_read_status(internals->rxmac[0], &status);
switch (status.speed) {
@@ -571,12 +577,9 @@ nfb_eth_dev_init(struct rte_eth_dev *dev, void *init_data)
max_rx_queues = ifc->rxq_cnt;
max_tx_queues = ifc->txq_cnt;
- nfb_nc_rxmac_init(internals->nfb,
- internals->rxmac,
- &internals->max_rxmac);
- nfb_nc_txmac_init(internals->nfb,
- internals->txmac,
- &internals->max_txmac);
+ ret = nfb_nc_eth_init(internals, params);
+ if (ret)
+ goto err_nc_eth_init;
/* Set rx, tx burst functions */
dev->rx_pkt_burst = nfb_eth_ndp_rx;
@@ -662,10 +665,9 @@ nfb_eth_dev_init(struct rte_eth_dev *dev, void *init_data)
rte_free(priv->queue_map_rx);
err_alloc_queue_map:
err_secondary_not_ready:
- nfb_nc_rxmac_deinit(internals->rxmac, internals->max_rxmac);
- nfb_nc_txmac_deinit(internals->txmac, internals->max_txmac);
+ nfb_nc_eth_deinit(internals);
+err_nc_eth_init:
nfb_close(internals->nfb);
-
err_nfb_open:
rte_free(internals);
err_alloc_internals:
@@ -692,8 +694,7 @@ nfb_eth_dev_uninit(struct rte_eth_dev *dev)
TAILQ_REMOVE(&nfb_eth_dev_list, internals, eth_dev_list);
- nfb_nc_rxmac_deinit(internals->rxmac, internals->max_rxmac);
- nfb_nc_txmac_deinit(internals->txmac, internals->max_txmac);
+ nfb_nc_eth_deinit(internals);
nfb_close(internals->nfb);
rte_free(internals);
--
2.53.0
^ permalink raw reply related [flat|nested] 131+ messages in thread
* [PATCH v10 6/8] net/nfb: add compatible cards to driver PCI ID table
2026-02-17 7:10 ` [PATCH v10 " spinler
` (4 preceding siblings ...)
2026-02-17 7:10 ` [PATCH v10 5/8] net/nfb: init only MACs associated with device spinler
@ 2026-02-17 7:10 ` spinler
2026-02-17 7:10 ` [PATCH v10 7/8] net/nfb: report firmware version spinler
` (3 subsequent siblings)
9 siblings, 0 replies; 131+ messages in thread
From: spinler @ 2026-02-17 7:10 UTC (permalink / raw)
To: dev; +Cc: Martin Spinler
From: Martin Spinler <spinler@cesnet.cz>
The nfb driver is compatible with:
- NFB-200G2QL card secondary PCI device
- FB2CGHH card
- COMBO-400G1 card
- Common CESNET-NDK cards
Signed-off-by: Martin Spinler <spinler@cesnet.cz>
---
drivers/net/nfb/nfb.h | 5 +++++
drivers/net/nfb/nfb_ethdev.c | 4 ++++
2 files changed, 9 insertions(+)
diff --git a/drivers/net/nfb/nfb.h b/drivers/net/nfb/nfb.h
index ea8e4fd255..1d863d5ad7 100644
--- a/drivers/net/nfb/nfb.h
+++ b/drivers/net/nfb/nfb.h
@@ -24,13 +24,18 @@ extern int nfb_logtype;
/* PCI Vendor ID */
#define PCI_VENDOR_ID_NETCOPE 0x1b26
#define PCI_VENDOR_ID_SILICOM 0x1c2c
+#define PCI_VENDOR_ID_CESNET 0x18ec
/* PCI Device IDs */
#define PCI_DEVICE_ID_NFB_40G2 0xcb80
#define PCI_DEVICE_ID_NFB_100G2 0xc2c1
#define PCI_DEVICE_ID_NFB_200G2QL 0xc250
+#define PCI_DEVICE_ID_NFB_200G2QL_E1 0xc251
#define PCI_DEVICE_ID_FB2CGG3 0x00d0
+#define PCI_DEVICE_ID_FB2CGHH 0x00d2
#define PCI_DEVICE_ID_FB2CGG3D 0xc240
+#define PCI_DEVICE_ID_COMBO400G1 0xc400
+#define PCI_DEVICE_ID_CESNET_NDK_COMMON 0xc000
/* Max index of ndp rx/tx queues */
#define RTE_ETH_NDP_MAX_RX_QUEUES 32
diff --git a/drivers/net/nfb/nfb_ethdev.c b/drivers/net/nfb/nfb_ethdev.c
index 5fadc863c5..9065620922 100644
--- a/drivers/net/nfb/nfb_ethdev.c
+++ b/drivers/net/nfb/nfb_ethdev.c
@@ -848,8 +848,12 @@ static const struct rte_pci_id nfb_pci_id_table[] = {
{ RTE_PCI_DEVICE(PCI_VENDOR_ID_NETCOPE, PCI_DEVICE_ID_NFB_40G2) },
{ RTE_PCI_DEVICE(PCI_VENDOR_ID_NETCOPE, PCI_DEVICE_ID_NFB_100G2) },
{ RTE_PCI_DEVICE(PCI_VENDOR_ID_NETCOPE, PCI_DEVICE_ID_NFB_200G2QL) },
+ { RTE_PCI_DEVICE(PCI_VENDOR_ID_NETCOPE, PCI_DEVICE_ID_NFB_200G2QL_E1) },
{ RTE_PCI_DEVICE(PCI_VENDOR_ID_SILICOM, PCI_DEVICE_ID_FB2CGG3) },
+ { RTE_PCI_DEVICE(PCI_VENDOR_ID_SILICOM, PCI_DEVICE_ID_FB2CGHH) },
{ RTE_PCI_DEVICE(PCI_VENDOR_ID_SILICOM, PCI_DEVICE_ID_FB2CGG3D) },
+ { RTE_PCI_DEVICE(PCI_VENDOR_ID_CESNET, PCI_DEVICE_ID_COMBO400G1) },
+ { RTE_PCI_DEVICE(PCI_VENDOR_ID_CESNET, PCI_DEVICE_ID_CESNET_NDK_COMMON) },
{ .vendor_id = 0, }
};
--
2.53.0
^ permalink raw reply related [flat|nested] 131+ messages in thread
* [PATCH v10 7/8] net/nfb: report firmware version
2026-02-17 7:10 ` [PATCH v10 " spinler
` (5 preceding siblings ...)
2026-02-17 7:10 ` [PATCH v10 6/8] net/nfb: add compatible cards to driver PCI ID table spinler
@ 2026-02-17 7:10 ` spinler
2026-02-17 7:10 ` [PATCH v10 8/8] doc/nfb: cleanup and update guide spinler
` (2 subsequent siblings)
9 siblings, 0 replies; 131+ messages in thread
From: spinler @ 2026-02-17 7:10 UTC (permalink / raw)
To: dev; +Cc: Martin Spinler
From: Martin Spinler <spinler@cesnet.cz>
Report the firmware version, which is described
by project name and project version.
Signed-off-by: Martin Spinler <spinler@cesnet.cz>
---
drivers/net/nfb/nfb_ethdev.c | 27 +++++++++++++++++++++++++++
1 file changed, 27 insertions(+)
diff --git a/drivers/net/nfb/nfb_ethdev.c b/drivers/net/nfb/nfb_ethdev.c
index 9065620922..9737ccc65b 100644
--- a/drivers/net/nfb/nfb_ethdev.c
+++ b/drivers/net/nfb/nfb_ethdev.c
@@ -502,6 +502,32 @@ nfb_eth_mac_addr_remove(struct rte_eth_dev *dev, uint32_t index)
nc_rxmac_set_mac(internals->rxmac[i], index, 0, 0);
}
+static int
+nfb_eth_fw_version_get(struct rte_eth_dev *dev, char *fw_version,
+ size_t fw_size)
+{
+ int ret;
+ const char *proj_name, *proj_vers;
+ struct pmd_internals *intl = dev->process_private;
+
+ proj_name = nc_info_get_fw_project_name(intl->nfb, NULL);
+ proj_vers = nc_info_get_fw_project_version(intl->nfb, NULL);
+
+ if (proj_name == NULL)
+ proj_name = "";
+ if (proj_vers == NULL)
+ proj_vers = "";
+
+ ret = snprintf(fw_version, fw_size, "%s;%s", proj_name, proj_vers);
+ if (ret < 0)
+ return ret;
+
+ if (ret >= (signed int)fw_size)
+ return strlen(proj_name) + 1 + strlen(proj_vers) + 1;
+
+ return 0;
+}
+
static const struct eth_dev_ops ops = {
.dev_start = nfb_eth_dev_start,
.dev_stop = nfb_eth_dev_stop,
@@ -528,6 +554,7 @@ static const struct eth_dev_ops ops = {
.mac_addr_set = nfb_eth_mac_addr_set,
.mac_addr_add = nfb_eth_mac_addr_add,
.mac_addr_remove = nfb_eth_mac_addr_remove,
+ .fw_version_get = nfb_eth_fw_version_get,
};
/**
--
2.53.0
^ permalink raw reply related [flat|nested] 131+ messages in thread
* [PATCH v10 8/8] doc/nfb: cleanup and update guide
2026-02-17 7:10 ` [PATCH v10 " spinler
` (6 preceding siblings ...)
2026-02-17 7:10 ` [PATCH v10 7/8] net/nfb: report firmware version spinler
@ 2026-02-17 7:10 ` spinler
2026-03-13 16:48 ` Thomas Monjalon
2026-02-17 14:58 ` [PATCH v10 0/8] net/nfb: rework to real multiport Stephen Hemminger
2026-02-19 0:12 ` Stephen Hemminger
9 siblings, 1 reply; 131+ messages in thread
From: spinler @ 2026-02-17 7:10 UTC (permalink / raw)
To: dev; +Cc: Martin Spinler
From: Martin Spinler <spinler@cesnet.cz>
Clean up documentation and update the guide with:
- Current card list with firmware download links
- Updated port argument description
Signed-off-by: Martin Spinler <spinler@cesnet.cz>
---
doc/guides/nics/features/nfb.ini | 4 +
doc/guides/nics/nfb.rst | 187 +++++++++++--------------
doc/guides/rel_notes/release_26_03.rst | 6 +
3 files changed, 95 insertions(+), 102 deletions(-)
diff --git a/doc/guides/nics/features/nfb.ini b/doc/guides/nics/features/nfb.ini
index c77b592789..7ac89fc15f 100644
--- a/doc/guides/nics/features/nfb.ini
+++ b/doc/guides/nics/features/nfb.ini
@@ -9,9 +9,13 @@ Link status = Y
Queue start/stop = Y
Promiscuous mode = Y
Allmulticast mode = Y
+Unicast MAC filter = Y
+Timestamp offload = P
Basic stats = Y
Extended stats = Y
Stats per queue = Y
+FW version = Y
+Multiprocess aware = Y
Linux = Y
x86-64 = Y
Usage doc = Y
diff --git a/doc/guides/nics/nfb.rst b/doc/guides/nics/nfb.rst
index a9b4049654..55a635d8d4 100644
--- a/doc/guides/nics/nfb.rst
+++ b/doc/guides/nics/nfb.rst
@@ -2,64 +2,101 @@
Copyright 2019 Cesnet
Copyright 2019 Netcope Technologies
-NFB poll mode driver library
-=================================
+NFB Poll Mode Driver
+====================
+
+The NFB PMD implements support for the FPGA-based
+programmable NICs running `CESNET-NDK <https://www.liberouter.org/ndk/>`_
+based firmware (formerly known as the NetCOPE platform). The CESNET Network Development Kit offers
+wide spectrum of supported cards, for example:
+N6010, FB2CGG3 (Silicom Denmark),
+IA-420F, IA-440i (BittWare),
+AGI-FH400G (ReflexCES),
+and `many more <https://github.com/CESNET/ndk-fpga/tree/devel/cards>`_.
+
+The CESNET-NDK framework is open source and
+can be found on `CESNET-NDK <https://github.com/CESNET/ndk-fpga>`_.
+Ready-to-use demo firmwares can be found on the `DYNANIC page <https://dyna-nic.com/try-now/>`_.
+
+Software compatibility and firmware for
+`historical cards <https://www.liberouter.org/technologies/cards/>`_
+are left unmaintained.
+
+Software prerequisites
+----------------------
-The NFB poll mode driver library implements support for the Netcope
-FPGA Boards (**NFB-40G2, NFB-100G2, NFB-200G2QL**) and Silicom **FB2CGG3** card,
-FPGA-based programmable NICs. The NFB PMD uses interface provided by the libnfb
-library to communicate with these cards over the nfb layer.
+This PMD requires a Linux kernel module, which is responsible for initialization and
+allocation of resources needed for the nfb layer function.
+Communication between PMD and kernel modules is mediated by the libnfb library.
+The kernel module and library are not part of DPDK and must be installed
+separately. Dependencies can be found on GitHub:
+`nfb-framework <https://github.com/CESNET/ndk-sw>`_ as source code,
+or for RPM-based distributions, the prebuilt `nfb-framework` package on
+`Fedora Copr <https://copr.fedorainfracloud.org/coprs/g/CESNET/nfb-framework/>`_.
-More information about the
-`NFB cards <https://www.liberouter.org/technologies/cards/>`_
-and used technology
-(`Network Development Kit <https://www.liberouter.org/ndk/>`_)
-can be found on the `Liberouter website <http://www.liberouter.org/>`_.
+Before starting the DPDK, make sure that the kernel module is loaded (`sudo modprobe nfb`)
+and the card is running the CESNET-NDK based firmware (`nfb-info -l`).
.. note::
- Currently the driver is supported only on x86_64 architectures.
- Only x86_64 versions of the external libraries are provided.
+ Currently, the driver is supported only on x86_64 architectures.
+
+NFB card architecture
+---------------------
+
+Ethernet Ports
+~~~~~~~~~~~~~~
+
+The NFB cards are multi-port multi-queue cards, where (generally) data from any
+Ethernet port may be sent by the firmware to any queue.
-Prerequisites
--------------
+The cards were historically represented in DPDK as a single port.
+Currently each Ethernet channel is represented as one DPDK port.
-This PMD requires kernel modules which are responsible for initialization and
-allocation of resources needed for nfb layer function.
-Communication between PMD and kernel modules is mediated by libnfb library.
-These kernel modules and library are not part of DPDK and must be installed
-separately:
+.. note::
-* **libnfb library**
+ Normally, one port corresponds to one channel, but ports can often be configured in a separate
+ manner. For example one 100G port can be used as 4x25G or 4x10G independent Ethernet channels.
- The library provides API for initialization of nfb transfers, receiving and
- transmitting data segments.
+By default, all ports are initialized and used for the allowed PCI device.
+When this behaviour is limiting (e.g., for multiple instances of DPDK app on different ports
+of the same PCI device), ports can be specified by the `port` item in the `allow` argument:
-* **Kernel modules**
+.. code-block:: console
- * nfb
+ -a 0000:01:00.0,port=0,port=3
- Kernel modules manage initialization of hardware, allocation and
- sharing of resources for user space applications.
+PCIe slots
+~~~~~~~~~~
-Dependencies can be found here:
-`Netcope common <https://github.com/CESNET/ndk-sw>`_.
+Some cards employ more than one PCIe device for better data throughput. This can be achieved by
+slot bifurcation (only a minor improvement) or by an add-on cable connected to another PCIe slot.
+Both improvements can work together, as is, for example, in the case of the AGI-FH400G card.
-Versions of the packages
-~~~~~~~~~~~~~~~~~~~~~~~~
+Because primary and secondary slot(s) can be attached to different NUMA nodes
+(also applies for bifurcation on some HW), the data structures need to be correctly allocated.
+(Device-aware allocation matters also on IOMMU-enabled systems.)
+The firmware already provides DMA queue to PCI device mapping. The DPDK application just needs to
+use all PCI devices, otherwise some queues will not be available; provide all PCI endpoints
+listed in the `nfb-info -v` in the `allow` argument.
-The minimum version of the provided packages:
+.. note::
-* for DPDK from 19.05
+ For cards where the number of Ethernet ports is less than the number of PCI devices
+ (e.g., AGI-FH400G: 1 port, up to 4 PCI devices), the virtual DPDK ports are
+ created to achieve the best NUMA-aware throughput
+ (virtual ports lack a lot of configuration features).
-Configuration
--------------
+Features
+--------
Timestamps
+~~~~~~~~~~
-The PMD supports hardware timestamps of frame receipt on physical network interface. In order to use
-the timestamps, the hardware timestamping unit must be enabled (follow the documentation of the NFB
-products). The standard `RTE_ETH_RX_OFFLOAD_TIMESTAMP` flag can be used for this feature.
+The PMD supports hardware timestamps of frame receipt on physical network interface.
+In order to use the timestamps, the hardware timestamping unit must be enabled
+(follow the documentation of the NFB products).
+The standard `RTE_ETH_RX_OFFLOAD_TIMESTAMP` flag can be used for this feature.
When the timestamps are enabled, a timestamp validity flag is set in the MBUFs
containing received frames and timestamp is inserted into the `rte_mbuf` struct.
@@ -67,72 +104,18 @@ containing received frames and timestamp is inserted into the `rte_mbuf` struct.
The timestamp is an `uint64_t` field and holds the number of nanoseconds
elapsed since 1.1.1970 00:00:00 UTC.
+Simulation
+~~~~~~~~~~
-Using the NFB PMD
-----------------------
-
-Kernel modules have to be loaded before running the DPDK application.
-
-NFB card architecture
----------------------
-
-The NFB cards are multi-port multi-queue cards, where (generally) data from any
-Ethernet port may be sent to any queue.
-They are represented in DPDK as a single port.
-
-NFB-200G2QL card employs an add-on cable which allows to connect it to two
-physical PCI-E slots at the same time (see the diagram below).
-This is done to allow 200 Gbps of traffic to be transferred through the PCI-E
-bus (note that a single PCI-E 3.0 x16 slot provides only 125 Gbps theoretical
-throughput).
-
-Although each slot may be connected to a different CPU and therefore to a different
-NUMA node, the card is represented as a single port in DPDK. To work with data
-from the individual queues on the right NUMA node, connection of NUMA nodes on
-first and last queue (each NUMA node has half of the queues) need to be checked.
-
-Limitations
------------
-
-Driver is usable only on Linux architecture, namely on CentOS.
-
-Since a card is always represented as a single port, but can be connected to two
-NUMA nodes, there is need for manual check where master/slave is connected.
-
-Example of usage
-----------------
-
-Read packets from 0. and 1. receive queue and write them to 0. and 1.
-transmit queue:
+The CESNET-NDK framework offers the possibility of simulating the firmware together with DPDK.
+This allows for easy debugging of a packet flow behaviour with a specific firmware configuration.
+The DPDK NFB driver can be connected to the simulator (Questa/ModelSim/nvc) via a virtual device:
.. code-block:: console
- ./<build_dir>/app/dpdk-testpmd -l 0-3 -n 2 \
- -- --port-topology=chained --rxq=2 --txq=2 --nb-cores=2 -i -a
-
-Example output:
-
-.. code-block:: console
+ dpdk-testpmd
+ --vdev=eth_vdev_nfb,dev=libnfb-ext-grpc.so:grpc+dma_vas:localhost:50051,queue_driver=native
+ --iova-mode=va -- -i
- [...]
- EAL: PCI device 0000:06:00.0 on NUMA socket -1
- EAL: probe driver: 1b26:c1c1 net_nfb
- PMD: Initializing NFB device (0000:06:00.0)
- PMD: Available DMA queues RX: 8 TX: 8
- PMD: NFB device (0000:06:00.0) successfully initialized
- Interactive-mode selected
- Auto-start selected
- Configuring Port 0 (socket 0)
- Port 0: 00:11:17:00:00:00
- Checking link statuses...
- Port 0 Link Up - speed 10000 Mbps - full-duplex
- Done
- Start automatic packet forwarding
- io packet forwarding - CRC stripping disabled - packets/burst=32
- nb forwarding cores=2 - nb forwarding ports=1
- RX queues=2 - RX desc=128 - RX free threshold=0
- RX threshold registers: pthresh=0 hthresh=0 wthresh=0
- TX queues=2 - TX desc=512 - TX free threshold=0
- TX threshold registers: pthresh=0 hthresh=0 wthresh=0
- TX RS bit threshold=0 - TXQ flags=0x0
- testpmd>
+More info about the simulation can be found int the CESNET-NDK `documentation
+<https://cesnet.github.io/ndk-fpga/devel/ndk_apps/minimal/tests/cocotb/readme.html>`_.
diff --git a/doc/guides/rel_notes/release_26_03.rst b/doc/guides/rel_notes/release_26_03.rst
index ff02c294d0..7678cf1a49 100644
--- a/doc/guides/rel_notes/release_26_03.rst
+++ b/doc/guides/rel_notes/release_26_03.rst
@@ -62,6 +62,12 @@ New Features
* **Updated CESNET NFB ethernet driver.**
* The timestamp value has been updated to make it usable.
+ * The DPDK port now represents just one Ethernet port instead of all Ethernet ports on the NIC.
+ * All ports are used by default, but a subset can be selected using the ``port`` argument.
+ * Report firmware version.
+ * Common CESNET-NDK-based adapters have been added,
+ including the FB2CGHH (Silicom Denmark) and XpressSX AGI-FH400G (Reflex CES).
+
Removed Items
-------------
--
2.53.0
^ permalink raw reply related [flat|nested] 131+ messages in thread
* Re: [PATCH v10 0/8] net/nfb: rework to real multiport
2026-02-17 7:10 ` [PATCH v10 " spinler
` (7 preceding siblings ...)
2026-02-17 7:10 ` [PATCH v10 8/8] doc/nfb: cleanup and update guide spinler
@ 2026-02-17 14:58 ` Stephen Hemminger
2026-02-17 15:05 ` Martin Spinler
2026-02-19 0:12 ` Stephen Hemminger
9 siblings, 1 reply; 131+ messages in thread
From: Stephen Hemminger @ 2026-02-17 14:58 UTC (permalink / raw)
To: spinler; +Cc: dev
On Tue, 17 Feb 2026 08:10:32 +0100
spinler@cesnet.cz wrote:
> From: Martin Spinler <spinler@cesnet.cz>
>
> This series implements real multiport for better user experience.
>
> The existing driver creates one ethdev/port for one PCI device.
> As the CESNET-NDK based cards aren't capable to represent each
> Ethernet port by own PCI device, new driver implementation
> processes real port configuration from firmware/card and switches
> from rte_eth_dev_pci_generic_probe to multiple rte_eth_dev_create calls.
>
> ---
> Depends-on: patch-37245 ("doc/nfb: update release notes for nfb driver")
Does it still depend on this patch? It looks like the release note change
is in this set.
^ permalink raw reply [flat|nested] 131+ messages in thread
* Re: [PATCH v10 0/8] net/nfb: rework to real multiport
2026-02-17 14:58 ` [PATCH v10 0/8] net/nfb: rework to real multiport Stephen Hemminger
@ 2026-02-17 15:05 ` Martin Spinler
2026-02-17 15:22 ` Martin Spinler
0 siblings, 1 reply; 131+ messages in thread
From: Martin Spinler @ 2026-02-17 15:05 UTC (permalink / raw)
To: Stephen Hemminger; +Cc: dev
> Does it still depend on this patch? It looks like the release note change
> is in this set.
Yes, it made sense to me because there would be no git conflicts.
I didn't see a problem as the patch-37245 is currently Accepted.
Am I missing something?
^ permalink raw reply [flat|nested] 131+ messages in thread
* Re: [PATCH v10 0/8] net/nfb: rework to real multiport
2026-02-17 15:05 ` Martin Spinler
@ 2026-02-17 15:22 ` Martin Spinler
0 siblings, 0 replies; 131+ messages in thread
From: Martin Spinler @ 2026-02-17 15:22 UTC (permalink / raw)
To: Stephen Hemminger; +Cc: dev
On Tue, 2026-02-17 at 16:05 +0100, Martin Spinler wrote:
> > Does it still depend on this patch? It looks like the release note change
> > is in this set.
Sorry, just to clarity: these are additional release note updates for this set.
(And these are the last standalone ones; the new "net/nfb: ethernet
enhancements" series has release note updates for each patch.)
>
> Yes, it made sense to me because there would be no git conflicts.
> I didn't see a problem as the patch-37245 is currently Accepted.
> Am I missing something?
^ permalink raw reply [flat|nested] 131+ messages in thread
* Re: [PATCH v10 0/8] net/nfb: rework to real multiport
2026-02-17 7:10 ` [PATCH v10 " spinler
` (8 preceding siblings ...)
2026-02-17 14:58 ` [PATCH v10 0/8] net/nfb: rework to real multiport Stephen Hemminger
@ 2026-02-19 0:12 ` Stephen Hemminger
9 siblings, 0 replies; 131+ messages in thread
From: Stephen Hemminger @ 2026-02-19 0:12 UTC (permalink / raw)
To: spinler; +Cc: dev
On Tue, 17 Feb 2026 08:10:32 +0100
spinler@cesnet.cz wrote:
> From: Martin Spinler <spinler@cesnet.cz>
>
> This series implements real multiport for better user experience.
>
> The existing driver creates one ethdev/port for one PCI device.
> As the CESNET-NDK based cards aren't capable to represent each
> Ethernet port by own PCI device, new driver implementation
> processes real port configuration from firmware/card and switches
> from rte_eth_dev_pci_generic_probe to multiple rte_eth_dev_create calls.
>
> ---
Queued to next-net.
^ permalink raw reply [flat|nested] 131+ messages in thread
* Re: [PATCH v10 8/8] doc/nfb: cleanup and update guide
2026-02-17 7:10 ` [PATCH v10 8/8] doc/nfb: cleanup and update guide spinler
@ 2026-03-13 16:48 ` Thomas Monjalon
0 siblings, 0 replies; 131+ messages in thread
From: Thomas Monjalon @ 2026-03-13 16:48 UTC (permalink / raw)
To: Stephen Hemminger; +Cc: dev, Martin Spinler
17/02/2026 08:10, spinler@cesnet.cz:
> From: Martin Spinler <spinler@cesnet.cz>
>
> Clean up documentation and update the guide with:
> - Current card list with firmware download links
> - Updated port argument description
>
> Signed-off-by: Martin Spinler <spinler@cesnet.cz>
> ---
> doc/guides/nics/features/nfb.ini | 4 +
> doc/guides/nics/nfb.rst | 187 +++++++++++--------------
> doc/guides/rel_notes/release_26_03.rst | 6 +
> 3 files changed, 95 insertions(+), 102 deletions(-)
I don't know why only release notes were merged in next-net.
I've caught it thanks to the description not matching the content.
I fix it when pulling in main.
Hope there is no more issue with this series.
^ permalink raw reply [flat|nested] 131+ messages in thread
end of thread, other threads:[~2026-03-13 16:48 UTC | newest]
Thread overview: 131+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-01-15 15:16 [PATCH 0/8] net/nfb: rework to real multiport spinler
2026-01-15 15:16 ` [PATCH 1/8] net/nfb: prepare for indirect queue mapping scheme spinler
2026-01-16 17:34 ` Stephen Hemminger
2026-01-20 15:16 ` Martin Spinler
2026-01-15 15:16 ` [PATCH 2/8] net/nfb: create ethdev for every eth port/channel spinler
2026-01-15 15:16 ` [PATCH 3/8] net/nfb: add vdev as alternative device probe method spinler
2026-01-15 15:16 ` [PATCH 4/8] net/nfb: add device argument "port" to limit used ports spinler
2026-01-16 17:36 ` Stephen Hemminger
2026-01-20 15:16 ` Martin Spinler
2026-01-16 17:36 ` Stephen Hemminger
2026-01-16 17:37 ` Stephen Hemminger
2026-01-15 15:16 ` [PATCH 5/8] net/nfb: init only MACs associated with device spinler
2026-01-15 15:16 ` [PATCH 6/8] net/nfb: add compatible cards to driver PCI ID table spinler
2026-01-15 15:16 ` [PATCH 7/8] net/nfb: report firmware version spinler
2026-01-15 15:16 ` [PATCH 8/8] doc/nfb: cleanup and update guide spinler
2026-01-16 5:50 ` [PATCH 0/8] net/nfb: rework to real multiport Stephen Hemminger
2026-01-16 16:44 ` [PATCH v2 " spinler
2026-01-16 16:44 ` [PATCH v2 1/8] net/nfb: prepare for indirect queue mapping scheme spinler
2026-01-16 16:44 ` [PATCH v2 2/8] net/nfb: create ethdev for every eth port/channel spinler
2026-01-16 16:44 ` [PATCH v2 3/8] net/nfb: add vdev as alternative device probe method spinler
2026-01-16 16:44 ` [PATCH v2 4/8] net/nfb: add device argument "port" to limit used ports spinler
2026-01-16 16:44 ` [PATCH v2 5/8] net/nfb: init only MACs associated with device spinler
2026-01-16 16:44 ` [PATCH v2 6/8] net/nfb: add compatible cards to driver PCI ID table spinler
2026-01-16 16:44 ` [PATCH v2 7/8] net/nfb: report firmware version spinler
2026-01-16 16:44 ` [PATCH v2 8/8] doc/nfb: cleanup and update guide spinler
2026-01-20 2:25 ` [PATCH v2 0/8] net/nfb: rework to real multiport Stephen Hemminger
2026-01-20 15:16 ` Martin Spinler
2026-01-21 17:03 ` [PATCH v3 " spinler
2026-01-21 17:03 ` [PATCH v3 1/8] net/nfb: prepare for indirect queue mapping scheme spinler
2026-01-21 17:03 ` [PATCH v3 2/8] net/nfb: create ethdev for every eth port/channel spinler
2026-01-21 17:03 ` [PATCH v3 3/8] net/nfb: add vdev as alternative device probe method spinler
2026-01-21 17:40 ` Stephen Hemminger
2026-01-21 17:03 ` [PATCH v3 4/8] net/nfb: add device argument "port" to limit used ports spinler
2026-01-21 17:03 ` [PATCH v3 5/8] net/nfb: init only MACs associated with device spinler
2026-01-21 17:03 ` [PATCH v3 6/8] net/nfb: add compatible cards to driver PCI ID table spinler
2026-01-21 17:03 ` [PATCH v3 7/8] net/nfb: report firmware version spinler
2026-01-21 17:03 ` [PATCH v3 8/8] doc/nfb: cleanup and update guide spinler
2026-01-21 17:41 ` Stephen Hemminger
2026-01-21 17:42 ` [PATCH v3 0/8] net/nfb: rework to real multiport Stephen Hemminger
2026-01-22 7:27 ` [PATCH v4 " spinler
2026-01-22 7:27 ` [PATCH v4 1/8] net/nfb: prepare for indirect queue mapping scheme spinler
2026-01-22 7:27 ` [PATCH v4 2/8] net/nfb: create ethdev for every eth port/channel spinler
2026-01-22 7:27 ` [PATCH v4 3/8] net/nfb: add vdev as alternative device probe method spinler
2026-01-22 7:27 ` [PATCH v4 4/8] net/nfb: add device argument "port" to limit used ports spinler
2026-01-22 7:27 ` [PATCH v4 5/8] net/nfb: init only MACs associated with device spinler
2026-01-22 7:27 ` [PATCH v4 6/8] net/nfb: add compatible cards to driver PCI ID table spinler
2026-01-22 7:27 ` [PATCH v4 7/8] net/nfb: report firmware version spinler
2026-01-22 7:27 ` [PATCH v4 8/8] doc/nfb: cleanup and update guide spinler
2026-01-23 1:00 ` [PATCH v4 0/8] net/nfb: rework to real multiport Stephen Hemminger
2026-01-23 1:01 ` Stephen Hemminger
2026-01-23 1:14 ` Stephen Hemminger
2026-01-23 17:34 ` Martin Spinler
2026-01-23 17:22 ` [PATCH v5 " spinler
2026-01-23 17:22 ` [PATCH v5 1/8] net/nfb: prepare for indirect queue mapping scheme spinler
2026-01-23 17:22 ` [PATCH v5 2/8] net/nfb: create one ethdev per ethernet port spinler
2026-01-27 0:37 ` Stephen Hemminger
2026-01-27 8:12 ` Martin Spinler
2026-01-27 14:09 ` Stephen Hemminger
2026-01-23 17:22 ` [PATCH v5 3/8] net/nfb: add vdev as alternative device probe method spinler
2026-01-23 17:22 ` [PATCH v5 4/8] net/nfb: add device argument "port" to limit used ports spinler
2026-01-23 17:22 ` [PATCH v5 5/8] net/nfb: init only MACs associated with device spinler
2026-01-23 17:22 ` [PATCH v5 6/8] net/nfb: add compatible cards to driver PCI ID table spinler
2026-01-23 17:22 ` [PATCH v5 7/8] net/nfb: report firmware version spinler
2026-01-23 17:22 ` [PATCH v5 8/8] doc/nfb: cleanup and update guide spinler
2026-01-24 19:10 ` [REVIEW] " Stephen Hemminger
2026-01-24 19:19 ` [PATCH v5 0/8] net/nfb: rework to real multiport Stephen Hemminger
2026-02-02 15:34 ` [PATCH v6 " spinler
2026-02-02 15:34 ` [PATCH v6 1/8] net/nfb: prepare for indirect queue mapping scheme spinler
2026-02-02 17:50 ` Stephen Hemminger
2026-02-02 15:34 ` [PATCH v6 2/8] net/nfb: create one ethdev per ethernet port spinler
2026-02-02 15:34 ` [PATCH v6 3/8] net/nfb: add vdev as alternative device probe method spinler
2026-02-02 15:34 ` [PATCH v6 4/8] net/nfb: add device argument "port" to limit used ports spinler
2026-02-02 15:34 ` [PATCH v6 5/8] net/nfb: init only MACs associated with device spinler
2026-02-02 15:34 ` [PATCH v6 6/8] net/nfb: add compatible cards to driver PCI ID table spinler
2026-02-02 15:34 ` [PATCH v6 7/8] net/nfb: report firmware version spinler
2026-02-02 15:34 ` [PATCH v6 8/8] doc/nfb: cleanup and update guide spinler
2026-02-02 17:42 ` [REVIEW] " Stephen Hemminger
2026-02-02 17:51 ` Stephen Hemminger
2026-02-02 17:52 ` Stephen Hemminger
2026-02-02 17:54 ` Stephen Hemminger
2026-02-02 17:54 ` Stephen Hemminger
2026-02-04 12:31 ` [PATCH v7 0/8] net/nfb: rework to real multiport spinler
2026-02-04 12:31 ` [PATCH v7 1/8] net/nfb: prepare for indirect queue mapping scheme spinler
2026-02-04 12:31 ` [PATCH v7 2/8] net/nfb: create one ethdev per ethernet port spinler
2026-02-04 12:31 ` [PATCH v7 3/8] net/nfb: add vdev as alternative device probe method spinler
2026-02-04 12:31 ` [PATCH v7 4/8] net/nfb: add device argument "port" to limit used ports spinler
2026-02-04 12:31 ` [PATCH v7 5/8] net/nfb: init only MACs associated with device spinler
2026-02-04 12:31 ` [PATCH v7 6/8] net/nfb: add compatible cards to driver PCI ID table spinler
2026-02-04 12:31 ` [PATCH v7 7/8] net/nfb: report firmware version spinler
2026-02-04 12:31 ` [PATCH v7 8/8] doc/nfb: cleanup and update guide spinler
2026-02-10 0:35 ` [PATCH v7 0/8] net/nfb: rework to real multiport Stephen Hemminger
2026-02-13 18:53 ` Martin Špinler
2026-02-12 18:35 ` Stephen Hemminger
2026-02-13 18:53 ` Martin Špinler
2026-02-13 18:53 ` [PATCH v8 " spinler
2026-02-13 18:53 ` [PATCH v8 1/8] net/nfb: prepare for indirect queue mapping scheme spinler
2026-02-13 18:53 ` [PATCH v8 2/8] net/nfb: create one ethdev per ethernet port spinler
2026-02-13 19:33 ` Stephen Hemminger
2026-02-13 18:53 ` [PATCH v8 3/8] net/nfb: add vdev as alternative device probe method spinler
2026-02-13 18:53 ` [PATCH v8 4/8] net/nfb: add device argument "port" to limit used ports spinler
2026-02-13 18:53 ` [PATCH v8 5/8] net/nfb: init only MACs associated with device spinler
2026-02-13 18:53 ` [PATCH v8 6/8] net/nfb: add compatible cards to driver PCI ID table spinler
2026-02-13 18:53 ` [PATCH v8 7/8] net/nfb: report firmware version spinler
2026-02-13 18:53 ` [PATCH v8 8/8] doc/nfb: cleanup and update guide spinler
2026-02-13 19:39 ` [PATCH v8 0/8] net/nfb: rework to real multiport Stephen Hemminger
2026-02-13 20:13 ` Martin Špinler
2026-02-16 16:24 ` [PATCH v9 " spinler
2026-02-16 16:24 ` [PATCH v9 1/8] net/nfb: prepare for indirect queue mapping scheme spinler
2026-02-16 16:25 ` [PATCH v9 2/8] net/nfb: create one ethdev per ethernet port spinler
2026-02-16 16:25 ` [PATCH v9 3/8] net/nfb: add vdev as alternative device probe method spinler
2026-02-16 16:25 ` [PATCH v9 4/8] net/nfb: add device argument "port" to limit used ports spinler
2026-02-16 16:25 ` [PATCH v9 5/8] net/nfb: init only MACs associated with device spinler
2026-02-16 16:25 ` [PATCH v9 6/8] net/nfb: add compatible cards to driver PCI ID table spinler
2026-02-16 16:25 ` [PATCH v9 7/8] net/nfb: report firmware version spinler
2026-02-16 16:25 ` [PATCH v9 8/8] doc/nfb: cleanup and update guide spinler
2026-02-16 22:11 ` [PATCH v9 0/8] net/nfb: rework to real multiport Stephen Hemminger
2026-02-17 7:09 ` Martin Spinler
2026-02-17 7:10 ` [PATCH v10 " spinler
2026-02-17 7:10 ` [PATCH v10 1/8] net/nfb: prepare for indirect queue mapping scheme spinler
2026-02-17 7:10 ` [PATCH v10 2/8] net/nfb: create one ethdev per ethernet port spinler
2026-02-17 7:10 ` [PATCH v10 3/8] net/nfb: add vdev as alternative device probe method spinler
2026-02-17 7:10 ` [PATCH v10 4/8] net/nfb: add device argument "port" to limit used ports spinler
2026-02-17 7:10 ` [PATCH v10 5/8] net/nfb: init only MACs associated with device spinler
2026-02-17 7:10 ` [PATCH v10 6/8] net/nfb: add compatible cards to driver PCI ID table spinler
2026-02-17 7:10 ` [PATCH v10 7/8] net/nfb: report firmware version spinler
2026-02-17 7:10 ` [PATCH v10 8/8] doc/nfb: cleanup and update guide spinler
2026-03-13 16:48 ` Thomas Monjalon
2026-02-17 14:58 ` [PATCH v10 0/8] net/nfb: rework to real multiport Stephen Hemminger
2026-02-17 15:05 ` Martin Spinler
2026-02-17 15:22 ` Martin Spinler
2026-02-19 0:12 ` Stephen Hemminger
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox