From f9e2511d80c2eaeb940c5f8280d7eea9d7946ece Mon Sep 17 00:00:00 2001 From: Breno Leitao Date: Wed, 18 Jun 2025 01:32:42 -0700 Subject: netdevsim: migrate to dstats stats collection Replace custom statistics tracking with the kernel's dstats infrastructure to simplify code and improve consistency with other network drivers. This change: - Sets dev->pcpu_stat_type = NETDEV_PCPU_STAT_DSTATS for automatic automatic allocation and deallocation. - Removes manual stats fields and their update - Replaces custom nsim_get_stats64() with dev_get_stats() - Uses dev_dstats_tx_add() and dev_dstats_tx_dropped() helpers - Eliminates the need for manual synchronization primitives The dstats framework provides the same functionality with less code. Suggested-by: Jakub Kicinski Reviewed-by: Joe Damato Signed-off-by: Breno Leitao Link: https://patch.msgid.link/20250618-netdevsim_stat-v4-1-19fe0d35e28e@debian.org Signed-off-by: Jakub Kicinski --- drivers/net/netdevsim/netdev.c | 33 ++++++--------------------------- 1 file changed, 6 insertions(+), 27 deletions(-) (limited to 'drivers/net/netdevsim/netdev.c') diff --git a/drivers/net/netdevsim/netdev.c b/drivers/net/netdevsim/netdev.c index fa5fbd97ad69..5010d8eefc85 100644 --- a/drivers/net/netdevsim/netdev.c +++ b/drivers/net/netdevsim/netdev.c @@ -93,19 +93,14 @@ static netdev_tx_t nsim_start_xmit(struct sk_buff *skb, struct net_device *dev) hrtimer_start(&rq->napi_timer, us_to_ktime(5), HRTIMER_MODE_REL); rcu_read_unlock(); - u64_stats_update_begin(&ns->syncp); - ns->tx_packets++; - ns->tx_bytes += len; - u64_stats_update_end(&ns->syncp); + dev_dstats_tx_add(dev, skb->len); return NETDEV_TX_OK; out_drop_free: dev_kfree_skb(skb); out_drop_cnt: rcu_read_unlock(); - u64_stats_update_begin(&ns->syncp); - ns->tx_dropped++; - u64_stats_update_end(&ns->syncp); + dev_dstats_tx_dropped(dev); return NETDEV_TX_OK; } @@ -126,20 +121,6 @@ static int nsim_change_mtu(struct net_device *dev, int new_mtu) return 0; } -static void -nsim_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *stats) -{ - struct netdevsim *ns = netdev_priv(dev); - unsigned int start; - - do { - start = u64_stats_fetch_begin(&ns->syncp); - stats->tx_bytes = ns->tx_bytes; - stats->tx_packets = ns->tx_packets; - stats->tx_dropped = ns->tx_dropped; - } while (u64_stats_fetch_retry(&ns->syncp, start)); -} - static int nsim_setup_tc_block_cb(enum tc_setup_type type, void *type_data, void *cb_priv) { @@ -556,7 +537,6 @@ static const struct net_device_ops nsim_netdev_ops = { .ndo_set_mac_address = eth_mac_addr, .ndo_validate_addr = eth_validate_addr, .ndo_change_mtu = nsim_change_mtu, - .ndo_get_stats64 = nsim_get_stats64, .ndo_set_vf_mac = nsim_set_vf_mac, .ndo_set_vf_vlan = nsim_set_vf_vlan, .ndo_set_vf_rate = nsim_set_vf_rate, @@ -580,7 +560,6 @@ static const struct net_device_ops nsim_vf_netdev_ops = { .ndo_set_mac_address = eth_mac_addr, .ndo_validate_addr = eth_validate_addr, .ndo_change_mtu = nsim_change_mtu, - .ndo_get_stats64 = nsim_get_stats64, .ndo_setup_tc = nsim_setup_tc, .ndo_set_features = nsim_set_features, }; @@ -594,7 +573,7 @@ static void nsim_get_queue_stats_rx(struct net_device *dev, int idx, struct rtnl_link_stats64 rtstats = {}; if (!idx) - nsim_get_stats64(dev, &rtstats); + dev_get_stats(dev, &rtstats); stats->packets = rtstats.rx_packets - !!rtstats.rx_packets; stats->bytes = rtstats.rx_bytes; @@ -606,7 +585,7 @@ static void nsim_get_queue_stats_tx(struct net_device *dev, int idx, struct rtnl_link_stats64 rtstats = {}; if (!idx) - nsim_get_stats64(dev, &rtstats); + dev_get_stats(dev, &rtstats); stats->packets = rtstats.tx_packets - !!rtstats.tx_packets; stats->bytes = rtstats.tx_bytes; @@ -618,7 +597,7 @@ static void nsim_get_base_stats(struct net_device *dev, { struct rtnl_link_stats64 rtstats = {}; - nsim_get_stats64(dev, &rtstats); + dev_get_stats(dev, &rtstats); rx->packets = !!rtstats.rx_packets; rx->bytes = 0; @@ -890,6 +869,7 @@ static void nsim_setup(struct net_device *dev) NETIF_F_HW_CSUM | NETIF_F_LRO | NETIF_F_TSO; + dev->pcpu_stat_type = NETDEV_PCPU_STAT_DSTATS; dev->max_mtu = ETH_MAX_MTU; dev->xdp_features = NETDEV_XDP_ACT_HW_OFFLOAD; } @@ -1022,7 +1002,6 @@ nsim_create(struct nsim_dev *nsim_dev, struct nsim_dev_port *nsim_dev_port) dev_net_set(dev, nsim_dev_net(nsim_dev)); ns = netdev_priv(dev); ns->netdev = dev; - u64_stats_init(&ns->syncp); ns->nsim_dev = nsim_dev; ns->nsim_dev_port = nsim_dev_port; ns->nsim_bus_dev = nsim_dev->nsim_bus_dev; -- cgit v1.2.3 From 788eb4de608bbebad237674e1057305340d653ba Mon Sep 17 00:00:00 2001 From: Breno Leitao Date: Wed, 18 Jun 2025 01:32:43 -0700 Subject: netdevsim: collect statistics at RX side When the RX side of netdevsim was added, the RX statistics were missing, making the driver unusable for GenerateTraffic() test framework. This patch adds proper statistics tracking on RX side, complementing the TX path. Reviewed-by: Joe Damato Signed-off-by: Breno Leitao Link: https://patch.msgid.link/20250618-netdevsim_stat-v4-2-19fe0d35e28e@debian.org Signed-off-by: Jakub Kicinski --- drivers/net/netdevsim/netdev.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) (limited to 'drivers/net/netdevsim/netdev.c') diff --git a/drivers/net/netdevsim/netdev.c b/drivers/net/netdevsim/netdev.c index 5010d8eefc85..3ea14f5c5e0c 100644 --- a/drivers/net/netdevsim/netdev.c +++ b/drivers/net/netdevsim/netdev.c @@ -331,16 +331,24 @@ static int nsim_get_iflink(const struct net_device *dev) static int nsim_rcv(struct nsim_rq *rq, int budget) { + struct net_device *dev = rq->napi.dev; struct sk_buff *skb; - int i; + unsigned int skblen; + int i, ret; for (i = 0; i < budget; i++) { if (skb_queue_empty(&rq->skb_queue)) break; skb = skb_dequeue(&rq->skb_queue); + /* skb might be discard at netif_receive_skb, save the len */ + skblen = skb->len; skb_mark_napi_id(skb, &rq->napi); - netif_receive_skb(skb); + ret = netif_receive_skb(skb); + if (ret == NET_RX_SUCCESS) + dev_dstats_rx_add(dev, skblen); + else + dev_dstats_rx_dropped(dev); } return i; -- cgit v1.2.3 From 2a68a22304f90e5ee960cccd86895a50cf154723 Mon Sep 17 00:00:00 2001 From: Breno Leitao Date: Wed, 18 Jun 2025 01:32:45 -0700 Subject: netdevsim: account dropped packet length in stats on queue free Add a call to dev_dstats_rx_dropped_add() in nsim_queue_free() to account for the number of packets dropped when purging the skb queue. This improves the accuracy of RX drop statistics reported by netdevsim. local_bh_{disable, enable}() protection is used to disable preemption, which is necessary given that dev_dstats_rx_dropped_add() access this_cpu_ptr(). See discussion in [1]. Link: https://lore.kernel.org/all/20250617055934.3fd9d322@kernel.org/ [1] Suggested-by: Jakub Kicinski Signed-off-by: Breno Leitao Link: https://patch.msgid.link/20250618-netdevsim_stat-v4-4-19fe0d35e28e@debian.org Signed-off-by: Jakub Kicinski --- drivers/net/netdevsim/netdev.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) (limited to 'drivers/net/netdevsim/netdev.c') diff --git a/drivers/net/netdevsim/netdev.c b/drivers/net/netdevsim/netdev.c index 3ea14f5c5e0c..7f2809be5d48 100644 --- a/drivers/net/netdevsim/netdev.c +++ b/drivers/net/netdevsim/netdev.c @@ -632,9 +632,12 @@ static struct nsim_rq *nsim_queue_alloc(void) return rq; } -static void nsim_queue_free(struct nsim_rq *rq) +static void nsim_queue_free(struct net_device *dev, struct nsim_rq *rq) { hrtimer_cancel(&rq->napi_timer); + local_bh_disable(); + dev_dstats_rx_dropped_add(dev, rq->skb_queue.qlen); + local_bh_enable(); skb_queue_purge_reason(&rq->skb_queue, SKB_DROP_REASON_QUEUE_PURGE); kfree(rq); } @@ -681,7 +684,7 @@ nsim_queue_mem_alloc(struct net_device *dev, void *per_queue_mem, int idx) return 0; err_free: - nsim_queue_free(qmem->rq); + nsim_queue_free(dev, qmem->rq); return err; } @@ -695,7 +698,7 @@ static void nsim_queue_mem_free(struct net_device *dev, void *per_queue_mem) if (!ns->rq_reset_mode) netif_napi_del_locked(&qmem->rq->napi); page_pool_destroy(qmem->rq->page_pool); - nsim_queue_free(qmem->rq); + nsim_queue_free(dev, qmem->rq); } } @@ -913,7 +916,7 @@ static void nsim_queue_uninit(struct netdevsim *ns) int i; for (i = 0; i < dev->num_rx_queues; i++) - nsim_queue_free(ns->rq[i]); + nsim_queue_free(dev, ns->rq[i]); kfree(ns->rq); ns->rq = NULL; -- cgit v1.2.3 From 5e95c0a3a55aea490420bd6994805edb050cc86b Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Fri, 20 Jun 2025 10:40:07 -0700 Subject: netdevsim: fix UaF when counting Tx stats skb may be freed as soon as we put it on the rx queue. Use the len variable like the code did prior to the conversion. Fixes: f9e2511d80c2 ("netdevsim: migrate to dstats stats collection") Signed-off-by: Jakub Kicinski Acked-by: David S. Miller Reviewed-by: Breno Leitao Signed-off-by: David S. Miller --- drivers/net/netdevsim/netdev.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net/netdevsim/netdev.c') diff --git a/drivers/net/netdevsim/netdev.c b/drivers/net/netdevsim/netdev.c index 7f2809be5d48..e36d3e846c2d 100644 --- a/drivers/net/netdevsim/netdev.c +++ b/drivers/net/netdevsim/netdev.c @@ -93,7 +93,7 @@ static netdev_tx_t nsim_start_xmit(struct sk_buff *skb, struct net_device *dev) hrtimer_start(&rq->napi_timer, us_to_ktime(5), HRTIMER_MODE_REL); rcu_read_unlock(); - dev_dstats_tx_add(dev, skb->len); + dev_dstats_tx_add(dev, len); return NETDEV_TX_OK; out_drop_free: -- cgit v1.2.3 From 54cb59cf81b02de847178c054eed7957170e9386 Mon Sep 17 00:00:00 2001 From: Toke Høiland-Jørgensen Date: Thu, 10 Jul 2025 13:18:33 +0200 Subject: net: netdevsim: Support setting dev->perm_addr on port creation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Network management daemons that match on the device permanent address currently have no virtual interface types to test against. NetworkManager, in particular, has carried an out of tree patch to set the permanent address on netdevsim devices to use in its CI for this purpose. To support this use case, support setting netdev->perm_addr when creating a netdevsim port. Reviewed-by: Simon Horman Signed-off-by: Toke Høiland-Jørgensen Link: https://patch.msgid.link/20250710-netdevsim-perm_addr-v4-1-c9db2fecf3bf@redhat.com Signed-off-by: Jakub Kicinski --- drivers/net/netdevsim/bus.c | 26 ++++++++++++++++++++++---- drivers/net/netdevsim/dev.c | 14 +++++++------- drivers/net/netdevsim/netdev.c | 9 ++++++--- drivers/net/netdevsim/netdevsim.h | 9 +++++---- 4 files changed, 40 insertions(+), 18 deletions(-) (limited to 'drivers/net/netdevsim/netdev.c') diff --git a/drivers/net/netdevsim/bus.c b/drivers/net/netdevsim/bus.c index 64c0cdd31bf8..830abcb98476 100644 --- a/drivers/net/netdevsim/bus.c +++ b/drivers/net/netdevsim/bus.c @@ -66,17 +66,35 @@ new_port_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct nsim_bus_dev *nsim_bus_dev = to_nsim_bus_dev(dev); + u8 eth_addr[ETH_ALEN] = {}; unsigned int port_index; + bool addr_set = false; int ret; /* Prevent to use nsim_bus_dev before initialization. */ if (!smp_load_acquire(&nsim_bus_dev->init)) return -EBUSY; - ret = kstrtouint(buf, 0, &port_index); - if (ret) - return ret; - ret = nsim_drv_port_add(nsim_bus_dev, NSIM_DEV_PORT_TYPE_PF, port_index); + ret = sscanf(buf, "%u %hhx:%hhx:%hhx:%hhx:%hhx:%hhx", &port_index, + ð_addr[0], ð_addr[1], ð_addr[2], ð_addr[3], + ð_addr[4], ð_addr[5]); + switch (ret) { + case 7: + if (!is_valid_ether_addr(eth_addr)) { + pr_err("The supplied perm_addr is not a valid MAC address\n"); + return -EINVAL; + } + addr_set = true; + fallthrough; + case 1: + break; + default: + pr_err("Format for adding new port is \"id [perm_addr]\" (uint MAC).\n"); + return -EINVAL; + } + + ret = nsim_drv_port_add(nsim_bus_dev, NSIM_DEV_PORT_TYPE_PF, port_index, + addr_set ? eth_addr : NULL); return ret ? ret : count; } diff --git a/drivers/net/netdevsim/dev.c b/drivers/net/netdevsim/dev.c index b3647691060c..01c7edb28d96 100644 --- a/drivers/net/netdevsim/dev.c +++ b/drivers/net/netdevsim/dev.c @@ -589,7 +589,7 @@ static void nsim_dev_dummy_region_exit(struct nsim_dev *nsim_dev) static int __nsim_dev_port_add(struct nsim_dev *nsim_dev, enum nsim_dev_port_type type, - unsigned int port_index); + unsigned int port_index, u8 perm_addr[ETH_ALEN]); static void __nsim_dev_port_del(struct nsim_dev_port *nsim_dev_port); static int nsim_esw_legacy_enable(struct nsim_dev *nsim_dev, @@ -613,7 +613,7 @@ static int nsim_esw_switchdev_enable(struct nsim_dev *nsim_dev, int i, err; for (i = 0; i < nsim_dev_get_vfs(nsim_dev); i++) { - err = __nsim_dev_port_add(nsim_dev, NSIM_DEV_PORT_TYPE_VF, i); + err = __nsim_dev_port_add(nsim_dev, NSIM_DEV_PORT_TYPE_VF, i, NULL); if (err) { NL_SET_ERR_MSG_MOD(extack, "Failed to initialize VFs' netdevsim ports"); pr_err("Failed to initialize VF id=%d. %d.\n", i, err); @@ -1396,7 +1396,7 @@ static const struct devlink_ops nsim_dev_devlink_ops = { #define NSIM_DEV_TEST1_DEFAULT true static int __nsim_dev_port_add(struct nsim_dev *nsim_dev, enum nsim_dev_port_type type, - unsigned int port_index) + unsigned int port_index, u8 perm_addr[ETH_ALEN]) { struct devlink_port_attrs attrs = {}; struct nsim_dev_port *nsim_dev_port; @@ -1433,7 +1433,7 @@ static int __nsim_dev_port_add(struct nsim_dev *nsim_dev, enum nsim_dev_port_typ if (err) goto err_dl_port_unregister; - nsim_dev_port->ns = nsim_create(nsim_dev, nsim_dev_port); + nsim_dev_port->ns = nsim_create(nsim_dev, nsim_dev_port, perm_addr); if (IS_ERR(nsim_dev_port->ns)) { err = PTR_ERR(nsim_dev_port->ns); goto err_port_debugfs_exit; @@ -1489,7 +1489,7 @@ static int nsim_dev_port_add_all(struct nsim_dev *nsim_dev, int i, err; for (i = 0; i < port_count; i++) { - err = __nsim_dev_port_add(nsim_dev, NSIM_DEV_PORT_TYPE_PF, i); + err = __nsim_dev_port_add(nsim_dev, NSIM_DEV_PORT_TYPE_PF, i, NULL); if (err) goto err_port_del_all; } @@ -1745,7 +1745,7 @@ __nsim_dev_port_lookup(struct nsim_dev *nsim_dev, enum nsim_dev_port_type type, } int nsim_drv_port_add(struct nsim_bus_dev *nsim_bus_dev, enum nsim_dev_port_type type, - unsigned int port_index) + unsigned int port_index, u8 perm_addr[ETH_ALEN]) { struct nsim_dev *nsim_dev = dev_get_drvdata(&nsim_bus_dev->dev); int err; @@ -1754,7 +1754,7 @@ int nsim_drv_port_add(struct nsim_bus_dev *nsim_bus_dev, enum nsim_dev_port_type if (__nsim_dev_port_lookup(nsim_dev, type, port_index)) err = -EEXIST; else - err = __nsim_dev_port_add(nsim_dev, type, port_index); + err = __nsim_dev_port_add(nsim_dev, type, port_index, perm_addr); devl_unlock(priv_to_devlink(nsim_dev)); return err; } diff --git a/drivers/net/netdevsim/netdev.c b/drivers/net/netdevsim/netdev.c index e36d3e846c2d..f316e44130f7 100644 --- a/drivers/net/netdevsim/netdev.c +++ b/drivers/net/netdevsim/netdev.c @@ -998,8 +998,9 @@ static void nsim_exit_netdevsim(struct netdevsim *ns) mock_phc_destroy(ns->phc); } -struct netdevsim * -nsim_create(struct nsim_dev *nsim_dev, struct nsim_dev_port *nsim_dev_port) +struct netdevsim *nsim_create(struct nsim_dev *nsim_dev, + struct nsim_dev_port *nsim_dev_port, + u8 perm_addr[ETH_ALEN]) { struct net_device *dev; struct netdevsim *ns; @@ -1010,6 +1011,9 @@ nsim_create(struct nsim_dev *nsim_dev, struct nsim_dev_port *nsim_dev_port) if (!dev) return ERR_PTR(-ENOMEM); + if (perm_addr) + memcpy(dev->perm_addr, perm_addr, ETH_ALEN); + dev_net_set(dev, nsim_dev_net(nsim_dev)); ns = netdev_priv(dev); ns->netdev = dev; @@ -1031,7 +1035,6 @@ nsim_create(struct nsim_dev *nsim_dev, struct nsim_dev_port *nsim_dev_port) ns->qr_dfs = debugfs_create_file("queue_reset", 0200, nsim_dev_port->ddir, ns, &nsim_qreset_fops); - return ns; err_free_netdev: diff --git a/drivers/net/netdevsim/netdevsim.h b/drivers/net/netdevsim/netdevsim.h index 809dd29fc5fe..8eeeb9256077 100644 --- a/drivers/net/netdevsim/netdevsim.h +++ b/drivers/net/netdevsim/netdevsim.h @@ -143,8 +143,9 @@ struct netdevsim { struct netdev_net_notifier nn; }; -struct netdevsim * -nsim_create(struct nsim_dev *nsim_dev, struct nsim_dev_port *nsim_dev_port); +struct netdevsim *nsim_create(struct nsim_dev *nsim_dev, + struct nsim_dev_port *nsim_dev_port, + u8 perm_addr[ETH_ALEN]); void nsim_destroy(struct netdevsim *ns); bool netdev_is_nsim(struct net_device *dev); @@ -362,8 +363,8 @@ void nsim_dev_exit(void); int nsim_drv_probe(struct nsim_bus_dev *nsim_bus_dev); void nsim_drv_remove(struct nsim_bus_dev *nsim_bus_dev); int nsim_drv_port_add(struct nsim_bus_dev *nsim_bus_dev, - enum nsim_dev_port_type type, - unsigned int port_index); + enum nsim_dev_port_type type, unsigned int port_index, + u8 perm_addr[ETH_ALEN]); int nsim_drv_port_del(struct nsim_bus_dev *nsim_bus_dev, enum nsim_dev_port_type type, unsigned int port_index); -- cgit v1.2.3 From ff2ac4df58adb6d7721bb0ce6069e1bd0e613126 Mon Sep 17 00:00:00 2001 From: Breno Leitao Date: Fri, 11 Jul 2025 10:06:59 -0700 Subject: netdevsim: implement peer queue flow control Add flow control mechanism between paired netdevsim devices to stop the TX queue during high traffic scenarios. When a receive queue becomes congested (approaching NSIM_RING_SIZE limit), the corresponding transmit queue on the peer device is stopped using netif_subqueue_try_stop(). Once the receive queue has sufficient capacity again, the peer's transmit queue is resumed with netif_tx_wake_queue(). Key changes: * Add nsim_stop_peer_tx_queue() to pause peer TX when RX queue is full * Add nsim_start_peer_tx_queue() to resume peer TX when RX queue drains * Implement queue mapping validation to ensure TX/RX queue count match * Wake all queues during device unlinking to prevent stuck queues * Use RCU protection when accessing peer device references * wake the queues when changing the queue numbers * Remove IFF_NO_QUEUE given it will enqueue packets now The flow control only activates when devices have matching TX/RX queue counts to ensure proper queue mapping. Suggested-by: Jakub Kicinski Signed-off-by: Breno Leitao Link: https://patch.msgid.link/20250711-netdev_flow_control-v3-1-aa1d5a155762@debian.org Signed-off-by: Jakub Kicinski --- drivers/net/netdevsim/bus.c | 3 ++ drivers/net/netdevsim/ethtool.c | 21 +++++++++++++ drivers/net/netdevsim/netdev.c | 68 ++++++++++++++++++++++++++++++++++++----- 3 files changed, 85 insertions(+), 7 deletions(-) (limited to 'drivers/net/netdevsim/netdev.c') diff --git a/drivers/net/netdevsim/bus.c b/drivers/net/netdevsim/bus.c index 830abcb98476..70e8c38ddad6 100644 --- a/drivers/net/netdevsim/bus.c +++ b/drivers/net/netdevsim/bus.c @@ -384,6 +384,9 @@ static ssize_t unlink_device_store(const struct bus_type *bus, const char *buf, err = 0; RCU_INIT_POINTER(nsim->peer, NULL); RCU_INIT_POINTER(peer->peer, NULL); + synchronize_net(); + netif_tx_wake_all_queues(dev); + netif_tx_wake_all_queues(peer->netdev); out_put_netns: put_net(ns); diff --git a/drivers/net/netdevsim/ethtool.c b/drivers/net/netdevsim/ethtool.c index 4d191a3293c7..f631d90c428a 100644 --- a/drivers/net/netdevsim/ethtool.c +++ b/drivers/net/netdevsim/ethtool.c @@ -101,6 +101,22 @@ nsim_get_channels(struct net_device *dev, struct ethtool_channels *ch) ch->combined_count = ns->ethtool.channels; } +static void +nsim_wake_queues(struct net_device *dev) +{ + struct netdevsim *ns = netdev_priv(dev); + struct netdevsim *peer; + + synchronize_net(); + netif_tx_wake_all_queues(dev); + + rcu_read_lock(); + peer = rcu_dereference(ns->peer); + if (peer) + netif_tx_wake_all_queues(peer->netdev); + rcu_read_unlock(); +} + static int nsim_set_channels(struct net_device *dev, struct ethtool_channels *ch) { @@ -113,6 +129,11 @@ nsim_set_channels(struct net_device *dev, struct ethtool_channels *ch) return err; ns->ethtool.channels = ch->combined_count; + + /* Only wake up queues if devices are linked */ + if (rcu_access_pointer(ns->peer)) + nsim_wake_queues(dev); + return 0; } diff --git a/drivers/net/netdevsim/netdev.c b/drivers/net/netdevsim/netdev.c index f316e44130f7..611e7f65291c 100644 --- a/drivers/net/netdevsim/netdev.c +++ b/drivers/net/netdevsim/netdev.c @@ -37,7 +37,53 @@ MODULE_IMPORT_NS("NETDEV_INTERNAL"); #define NSIM_RING_SIZE 256 -static int nsim_napi_rx(struct nsim_rq *rq, struct sk_buff *skb) +static void nsim_start_peer_tx_queue(struct net_device *dev, struct nsim_rq *rq) +{ + struct netdevsim *ns = netdev_priv(dev); + struct net_device *peer_dev; + struct netdevsim *peer_ns; + struct netdev_queue *txq; + u16 idx; + + idx = rq->napi.index; + rcu_read_lock(); + peer_ns = rcu_dereference(ns->peer); + if (!peer_ns) + goto out; + + /* TX device */ + peer_dev = peer_ns->netdev; + if (dev->real_num_tx_queues != peer_dev->num_rx_queues) + goto out; + + txq = netdev_get_tx_queue(peer_dev, idx); + if (!netif_tx_queue_stopped(txq)) + goto out; + + netif_tx_wake_queue(txq); +out: + rcu_read_unlock(); +} + +static void nsim_stop_tx_queue(struct net_device *tx_dev, + struct net_device *rx_dev, + struct nsim_rq *rq, + u16 idx) +{ + /* If different queues size, do not stop, since it is not + * easy to find which TX queue is mapped here + */ + if (rx_dev->real_num_tx_queues != tx_dev->num_rx_queues) + return; + + /* rq is the queue on the receive side */ + netif_subqueue_try_stop(tx_dev, idx, + NSIM_RING_SIZE - skb_queue_len(&rq->skb_queue), + NSIM_RING_SIZE / 2); +} + +static int nsim_napi_rx(struct net_device *tx_dev, struct net_device *rx_dev, + struct nsim_rq *rq, struct sk_buff *skb) { if (skb_queue_len(&rq->skb_queue) > NSIM_RING_SIZE) { dev_kfree_skb_any(skb); @@ -45,13 +91,22 @@ static int nsim_napi_rx(struct nsim_rq *rq, struct sk_buff *skb) } skb_queue_tail(&rq->skb_queue, skb); + + /* Stop the peer TX queue avoiding dropping packets later */ + if (skb_queue_len(&rq->skb_queue) >= NSIM_RING_SIZE) + nsim_stop_tx_queue(tx_dev, rx_dev, rq, + skb_get_queue_mapping(skb)); + return NET_RX_SUCCESS; } -static int nsim_forward_skb(struct net_device *dev, struct sk_buff *skb, +static int nsim_forward_skb(struct net_device *tx_dev, + struct net_device *rx_dev, + struct sk_buff *skb, struct nsim_rq *rq) { - return __dev_forward_skb(dev, skb) ?: nsim_napi_rx(rq, skb); + return __dev_forward_skb(rx_dev, skb) ?: + nsim_napi_rx(tx_dev, rx_dev, rq, skb); } static netdev_tx_t nsim_start_xmit(struct sk_buff *skb, struct net_device *dev) @@ -86,7 +141,7 @@ static netdev_tx_t nsim_start_xmit(struct sk_buff *skb, struct net_device *dev) skb_linearize(skb); skb_tx_timestamp(skb); - if (unlikely(nsim_forward_skb(peer_dev, skb, rq) == NET_RX_DROP)) + if (unlikely(nsim_forward_skb(dev, peer_dev, skb, rq) == NET_RX_DROP)) goto out_drop_cnt; if (!hrtimer_active(&rq->napi_timer)) @@ -351,6 +406,7 @@ static int nsim_rcv(struct nsim_rq *rq, int budget) dev_dstats_rx_dropped(dev); } + nsim_start_peer_tx_queue(dev, rq); return i; } @@ -864,10 +920,8 @@ static void nsim_setup(struct net_device *dev) ether_setup(dev); eth_hw_addr_random(dev); - dev->tx_queue_len = 0; dev->flags &= ~IFF_MULTICAST; - dev->priv_flags |= IFF_LIVE_ADDR_CHANGE | - IFF_NO_QUEUE; + dev->priv_flags |= IFF_LIVE_ADDR_CHANGE; dev->features |= NETIF_F_HIGHDMA | NETIF_F_SG | NETIF_F_FRAGLIST | -- cgit v1.2.3 From be09f0d1acce9ed8d730ae33969da201948608cd Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Sat, 19 Jul 2025 01:30:55 -0700 Subject: net: netdevsim: hook in XDP handling Add basic XDP support by hooking in do_xdp_generic(). This should be enough to validate most basic XDP tests. Signed-off-by: Mohsin Bashir Link: https://patch.msgid.link/20250719083059.3209169-2-mohsin.bashr@gmail.com Signed-off-by: Jakub Kicinski --- drivers/net/netdevsim/netdev.c | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) (limited to 'drivers/net/netdevsim/netdev.c') diff --git a/drivers/net/netdevsim/netdev.c b/drivers/net/netdevsim/netdev.c index 611e7f65291c..a7628f5c09af 100644 --- a/drivers/net/netdevsim/netdev.c +++ b/drivers/net/netdevsim/netdev.c @@ -387,15 +387,34 @@ static int nsim_get_iflink(const struct net_device *dev) static int nsim_rcv(struct nsim_rq *rq, int budget) { struct net_device *dev = rq->napi.dev; + struct bpf_prog *xdp_prog; + struct netdevsim *ns; struct sk_buff *skb; unsigned int skblen; int i, ret; + ns = netdev_priv(dev); + xdp_prog = READ_ONCE(ns->xdp.prog); + for (i = 0; i < budget; i++) { if (skb_queue_empty(&rq->skb_queue)) break; skb = skb_dequeue(&rq->skb_queue); + + if (xdp_prog) { + /* skb might be freed directly by XDP, save the len */ + skblen = skb->len; + + if (skb->ip_summed == CHECKSUM_PARTIAL) + skb_checksum_help(skb); + ret = do_xdp_generic(xdp_prog, &skb); + if (ret != XDP_PASS) { + dev_dstats_rx_add(dev, skblen); + continue; + } + } + /* skb might be discard at netif_receive_skb, save the len */ skblen = skb->len; skb_mark_napi_id(skb, &rq->napi); @@ -936,7 +955,7 @@ static void nsim_setup(struct net_device *dev) NETIF_F_TSO; dev->pcpu_stat_type = NETDEV_PCPU_STAT_DSTATS; dev->max_mtu = ETH_MAX_MTU; - dev->xdp_features = NETDEV_XDP_ACT_HW_OFFLOAD; + dev->xdp_features = NETDEV_XDP_ACT_BASIC | NETDEV_XDP_ACT_HW_OFFLOAD; } static int nsim_queue_init(struct netdevsim *ns) -- cgit v1.2.3 From 6fd824342a57164d97717325763daee1ef01cbcd Mon Sep 17 00:00:00 2001 From: Byungchul Park Date: Mon, 21 Jul 2025 11:18:27 +0900 Subject: netdevsim: access ->pp through netmem_desc instead of page To eliminate the use of struct page in page pool, the page pool users should use netmem descriptor and APIs instead. Make netdevsim access ->pp through netmem_desc instead of page. Signed-off-by: Byungchul Park Link: https://patch.msgid.link/20250721021835.63939-5-byungchul@sk.com Signed-off-by: Jakub Kicinski --- drivers/net/netdevsim/netdev.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'drivers/net/netdevsim/netdev.c') diff --git a/drivers/net/netdevsim/netdev.c b/drivers/net/netdevsim/netdev.c index a7628f5c09af..39fe28af48b9 100644 --- a/drivers/net/netdevsim/netdev.c +++ b/drivers/net/netdevsim/netdev.c @@ -917,7 +917,8 @@ nsim_pp_hold_write(struct file *file, const char __user *data, if (!ns->page) ret = -ENOMEM; } else { - page_pool_put_full_page(ns->page->pp, ns->page, false); + page_pool_put_full_page(pp_page_to_nmdesc(ns->page)->pp, + ns->page, false); ns->page = NULL; } @@ -1145,7 +1146,8 @@ void nsim_destroy(struct netdevsim *ns) /* Put this intentionally late to exercise the orphaning path */ if (ns->page) { - page_pool_put_full_page(ns->page->pp, ns->page, false); + page_pool_put_full_page(pp_page_to_nmdesc(ns->page)->pp, + ns->page, false); ns->page = NULL; } -- cgit v1.2.3