From f216306bfb602b449cb2c49cf03870fb8761d146 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Thu, 4 Apr 2024 19:45:24 -0700 Subject: netdevsim: report stats by default, like a real device Real devices should implement qstats. Devices which support pause or FEC configuration should also report the relevant stats. nsim was missing FEC stats completely, some of the qstats and pause stats required toggling a debugfs knob. Note that the tests which used pause always initialize the setting so they shouldn't be affected by the different starting value. Signed-off-by: Jakub Kicinski Signed-off-by: David S. Miller --- drivers/net/netdevsim/netdev.c | 49 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) (limited to 'drivers/net/netdevsim/netdev.c') diff --git a/drivers/net/netdevsim/netdev.c b/drivers/net/netdevsim/netdev.c index 8330bc0bcb7e..d7ba447db17c 100644 --- a/drivers/net/netdevsim/netdev.c +++ b/drivers/net/netdevsim/netdev.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -330,6 +331,53 @@ static const struct net_device_ops nsim_vf_netdev_ops = { .ndo_set_features = nsim_set_features, }; +/* We don't have true per-queue stats, yet, so do some random fakery here. + * Only report stuff for queue 0. + */ +static void nsim_get_queue_stats_rx(struct net_device *dev, int idx, + struct netdev_queue_stats_rx *stats) +{ + struct rtnl_link_stats64 rtstats = {}; + + if (!idx) + nsim_get_stats64(dev, &rtstats); + + stats->packets = rtstats.rx_packets - !!rtstats.rx_packets; + stats->bytes = rtstats.rx_bytes; +} + +static void nsim_get_queue_stats_tx(struct net_device *dev, int idx, + struct netdev_queue_stats_tx *stats) +{ + struct rtnl_link_stats64 rtstats = {}; + + if (!idx) + nsim_get_stats64(dev, &rtstats); + + stats->packets = rtstats.tx_packets - !!rtstats.tx_packets; + stats->bytes = rtstats.tx_bytes; +} + +static void nsim_get_base_stats(struct net_device *dev, + struct netdev_queue_stats_rx *rx, + struct netdev_queue_stats_tx *tx) +{ + struct rtnl_link_stats64 rtstats = {}; + + nsim_get_stats64(dev, &rtstats); + + rx->packets = !!rtstats.rx_packets; + rx->bytes = 0; + tx->packets = !!rtstats.tx_packets; + tx->bytes = 0; +} + +static const struct netdev_stat_ops nsim_stat_ops = { + .get_queue_stats_tx = nsim_get_queue_stats_tx, + .get_queue_stats_rx = nsim_get_queue_stats_rx, + .get_base_stats = nsim_get_base_stats, +}; + static void nsim_setup(struct net_device *dev) { ether_setup(dev); @@ -360,6 +408,7 @@ static int nsim_init_netdevsim(struct netdevsim *ns) ns->phc = phc; ns->netdev->netdev_ops = &nsim_netdev_ops; + ns->netdev->stat_ops = &nsim_stat_ops; err = nsim_udp_tunnels_info_create(ns->nsim_dev, ns->netdev); if (err) -- cgit v1.2.3 From 1580cbcbfe770b0a7fb76735c1a601483335c1c2 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Fri, 12 Apr 2024 07:14:31 -0700 Subject: net: netdevsim: add some fake page pool use Add very basic page pool use so that we can exercise the netlink uAPI in a selftest. Page pool gets created on open, destroyed on close. But we control allocating of a single page thru debugfs. This page may survive past the page pool itself so that we can test orphaned page pools. Link: https://lore.kernel.org/r/20240412141436.828666-2-kuba@kernel.org Signed-off-by: Jakub Kicinski --- drivers/net/netdevsim/netdev.c | 93 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 93 insertions(+) (limited to 'drivers/net/netdevsim/netdev.c') diff --git a/drivers/net/netdevsim/netdev.c b/drivers/net/netdevsim/netdev.c index d7ba447db17c..d127856f8f36 100644 --- a/drivers/net/netdevsim/netdev.c +++ b/drivers/net/netdevsim/netdev.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -299,6 +300,29 @@ static int nsim_get_iflink(const struct net_device *dev) return iflink; } +static int nsim_open(struct net_device *dev) +{ + struct netdevsim *ns = netdev_priv(dev); + struct page_pool_params pp = { 0 }; + + pp.pool_size = 128; + pp.dev = &dev->dev; + pp.dma_dir = DMA_BIDIRECTIONAL; + pp.netdev = dev; + + ns->pp = page_pool_create(&pp); + return PTR_ERR_OR_ZERO(ns->pp); +} + +static int nsim_stop(struct net_device *dev) +{ + struct netdevsim *ns = netdev_priv(dev); + + page_pool_destroy(ns->pp); + + return 0; +} + static const struct net_device_ops nsim_netdev_ops = { .ndo_start_xmit = nsim_start_xmit, .ndo_set_rx_mode = nsim_set_rx_mode, @@ -318,6 +342,8 @@ static const struct net_device_ops nsim_netdev_ops = { .ndo_set_features = nsim_set_features, .ndo_get_iflink = nsim_get_iflink, .ndo_bpf = nsim_bpf, + .ndo_open = nsim_open, + .ndo_stop = nsim_stop, }; static const struct net_device_ops nsim_vf_netdev_ops = { @@ -378,6 +404,60 @@ static const struct netdev_stat_ops nsim_stat_ops = { .get_base_stats = nsim_get_base_stats, }; +static ssize_t +nsim_pp_hold_read(struct file *file, char __user *data, + size_t count, loff_t *ppos) +{ + struct netdevsim *ns = file->private_data; + char buf[3] = "n\n"; + + if (ns->page) + buf[0] = 'y'; + + return simple_read_from_buffer(data, count, ppos, buf, 2); +} + +static ssize_t +nsim_pp_hold_write(struct file *file, const char __user *data, + size_t count, loff_t *ppos) +{ + struct netdevsim *ns = file->private_data; + ssize_t ret; + bool val; + + ret = kstrtobool_from_user(data, count, &val); + if (ret) + return ret; + + rtnl_lock(); + ret = count; + if (val == !!ns->page) + goto exit; + + if (!netif_running(ns->netdev) && val) { + ret = -ENETDOWN; + } else if (val) { + ns->page = page_pool_dev_alloc_pages(ns->pp); + if (!ns->page) + ret = -ENOMEM; + } else { + page_pool_put_full_page(ns->page->pp, ns->page, false); + ns->page = NULL; + } + rtnl_unlock(); + +exit: + return count; +} + +static const struct file_operations nsim_pp_hold_fops = { + .open = simple_open, + .read = nsim_pp_hold_read, + .write = nsim_pp_hold_write, + .llseek = generic_file_llseek, + .owner = THIS_MODULE, +}; + static void nsim_setup(struct net_device *dev) { ether_setup(dev); @@ -485,6 +565,10 @@ nsim_create(struct nsim_dev *nsim_dev, struct nsim_dev_port *nsim_dev_port) err = nsim_init_netdevsim_vf(ns); if (err) goto err_free_netdev; + + ns->pp_dfs = debugfs_create_file("pp_hold", 0600, nsim_dev_port->ddir, + ns, &nsim_pp_hold_fops); + return ns; err_free_netdev: @@ -497,6 +581,8 @@ void nsim_destroy(struct netdevsim *ns) struct net_device *dev = ns->netdev; struct netdevsim *peer; + debugfs_remove(ns->pp_dfs); + rtnl_lock(); peer = rtnl_dereference(ns->peer); if (peer) @@ -511,6 +597,13 @@ void nsim_destroy(struct netdevsim *ns) rtnl_unlock(); if (nsim_dev_port_is_pf(ns->nsim_dev_port)) nsim_exit_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); + ns->page = NULL; + } + free_netdev(dev); } -- cgit v1.2.3 From 1eb2cded45b35816085c1f962933c187d970f9dc Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Mon, 6 May 2024 10:28:12 +0000 Subject: net: annotate writes on dev->mtu from ndo_change_mtu() Simon reported that ndo_change_mtu() methods were never updated to use WRITE_ONCE(dev->mtu, new_mtu) as hinted in commit 501a90c94510 ("inet: protect against too small mtu values.") We read dev->mtu without holding RTNL in many places, with READ_ONCE() annotations. It is time to take care of ndo_change_mtu() methods to use corresponding WRITE_ONCE() Signed-off-by: Eric Dumazet Reported-by: Simon Horman Closes: https://lore.kernel.org/netdev/20240505144608.GB67882@kernel.org/ Reviewed-by: Jacob Keller Reviewed-by: Sabrina Dubroca Reviewed-by: Simon Horman Acked-by: Shannon Nelson Link: https://lore.kernel.org/r/20240506102812.3025432-1-edumazet@google.com Signed-off-by: Jakub Kicinski --- 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 d127856f8f36..e3a385a32269 100644 --- a/drivers/net/netdevsim/netdev.c +++ b/drivers/net/netdevsim/netdev.c @@ -74,7 +74,7 @@ static int nsim_change_mtu(struct net_device *dev, int new_mtu) if (ns->xdp.prog && new_mtu > NSIM_XDP_MAX_MTU) return -EBUSY; - dev->mtu = new_mtu; + WRITE_ONCE(dev->mtu, new_mtu); return 0; } -- cgit v1.2.3 From 3762ec05a9fbda16aaaa2568df679ab8ad13f38d Mon Sep 17 00:00:00 2001 From: David Wei Date: Tue, 7 May 2024 09:32:27 -0700 Subject: netdevsim: add NAPI support Add NAPI support to netdevim, similar to veth. * Add a nsim_rq rx queue structure to hold a NAPI instance and a skb queue. * During xmit, store the skb in the peer skb queue and schedule NAPI. * During napi_poll(), drain the skb queue and pass up the stack. * Add assoc between rxq and NAPI instance using netif_queue_set_napi(). Signed-off-by: David Wei Link: https://lore.kernel.org/r/20240507163228.2066817-2-dw@davidwei.uk Signed-off-by: Jakub Kicinski --- drivers/net/netdevsim/netdev.c | 209 ++++++++++++++++++++++++++++++++++++++--- 1 file changed, 198 insertions(+), 11 deletions(-) (limited to 'drivers/net/netdevsim/netdev.c') diff --git a/drivers/net/netdevsim/netdev.c b/drivers/net/netdevsim/netdev.c index e3a385a32269..c22897bf5509 100644 --- a/drivers/net/netdevsim/netdev.c +++ b/drivers/net/netdevsim/netdev.c @@ -28,11 +28,33 @@ #include "netdevsim.h" +#define NSIM_RING_SIZE 256 + +static int nsim_napi_rx(struct nsim_rq *rq, struct sk_buff *skb) +{ + if (skb_queue_len(&rq->skb_queue) > NSIM_RING_SIZE) { + dev_kfree_skb_any(skb); + return NET_RX_DROP; + } + + skb_queue_tail(&rq->skb_queue, skb); + return NET_RX_SUCCESS; +} + +static int nsim_forward_skb(struct net_device *dev, struct sk_buff *skb, + struct nsim_rq *rq) +{ + return __dev_forward_skb(dev, skb) ?: nsim_napi_rx(rq, skb); +} + static netdev_tx_t nsim_start_xmit(struct sk_buff *skb, struct net_device *dev) { struct netdevsim *ns = netdev_priv(dev); + struct net_device *peer_dev; unsigned int len = skb->len; struct netdevsim *peer_ns; + struct nsim_rq *rq; + int rxq; rcu_read_lock(); if (!nsim_ipsec_tx(ns, skb)) @@ -42,10 +64,18 @@ static netdev_tx_t nsim_start_xmit(struct sk_buff *skb, struct net_device *dev) if (!peer_ns) goto out_drop_free; + peer_dev = peer_ns->netdev; + rxq = skb_get_queue_mapping(skb); + if (rxq >= peer_dev->num_rx_queues) + rxq = rxq % peer_dev->num_rx_queues; + rq = &peer_ns->rq[rxq]; + skb_tx_timestamp(skb); - if (unlikely(dev_forward_skb(peer_ns->netdev, skb) == NET_RX_DROP)) + if (unlikely(nsim_forward_skb(peer_dev, skb, rq) == NET_RX_DROP)) goto out_drop_cnt; + napi_schedule(&rq->napi); + rcu_read_unlock(); u64_stats_update_begin(&ns->syncp); ns->tx_packets++; @@ -300,25 +330,146 @@ static int nsim_get_iflink(const struct net_device *dev) return iflink; } +static int nsim_rcv(struct nsim_rq *rq, int budget) +{ + struct sk_buff *skb; + int i; + + for (i = 0; i < budget; i++) { + if (skb_queue_empty(&rq->skb_queue)) + break; + + skb = skb_dequeue(&rq->skb_queue); + netif_receive_skb(skb); + } + + return i; +} + +static int nsim_poll(struct napi_struct *napi, int budget) +{ + struct nsim_rq *rq = container_of(napi, struct nsim_rq, napi); + int done; + + done = nsim_rcv(rq, budget); + napi_complete(napi); + + return done; +} + +static int nsim_create_page_pool(struct nsim_rq *rq) +{ + struct page_pool_params p = { + .order = 0, + .pool_size = NSIM_RING_SIZE, + .nid = NUMA_NO_NODE, + .dev = &rq->napi.dev->dev, + .napi = &rq->napi, + .dma_dir = DMA_BIDIRECTIONAL, + .netdev = rq->napi.dev, + }; + + rq->page_pool = page_pool_create(&p); + if (IS_ERR(rq->page_pool)) { + int err = PTR_ERR(rq->page_pool); + + rq->page_pool = NULL; + return err; + } + return 0; +} + +static int nsim_init_napi(struct netdevsim *ns) +{ + struct net_device *dev = ns->netdev; + struct nsim_rq *rq; + int err, i; + + for (i = 0; i < dev->num_rx_queues; i++) { + rq = &ns->rq[i]; + + netif_napi_add(dev, &rq->napi, nsim_poll); + } + + for (i = 0; i < dev->num_rx_queues; i++) { + rq = &ns->rq[i]; + + err = nsim_create_page_pool(rq); + if (err) + goto err_pp_destroy; + } + + return 0; + +err_pp_destroy: + while (i--) { + page_pool_destroy(ns->rq[i].page_pool); + ns->rq[i].page_pool = NULL; + } + + for (i = 0; i < dev->num_rx_queues; i++) + __netif_napi_del(&ns->rq[i].napi); + + return err; +} + +static void nsim_enable_napi(struct netdevsim *ns) +{ + struct net_device *dev = ns->netdev; + int i; + + for (i = 0; i < dev->num_rx_queues; i++) { + struct nsim_rq *rq = &ns->rq[i]; + + netif_queue_set_napi(dev, i, NETDEV_QUEUE_TYPE_RX, &rq->napi); + napi_enable(&rq->napi); + } +} + static int nsim_open(struct net_device *dev) { struct netdevsim *ns = netdev_priv(dev); - struct page_pool_params pp = { 0 }; + int err; - pp.pool_size = 128; - pp.dev = &dev->dev; - pp.dma_dir = DMA_BIDIRECTIONAL; - pp.netdev = dev; + err = nsim_init_napi(ns); + if (err) + return err; - ns->pp = page_pool_create(&pp); - return PTR_ERR_OR_ZERO(ns->pp); + nsim_enable_napi(ns); + + return 0; +} + +static void nsim_del_napi(struct netdevsim *ns) +{ + struct net_device *dev = ns->netdev; + int i; + + for (i = 0; i < dev->num_rx_queues; i++) { + struct nsim_rq *rq = &ns->rq[i]; + + napi_disable(&rq->napi); + __netif_napi_del(&rq->napi); + } + synchronize_net(); + + for (i = 0; i < dev->num_rx_queues; i++) { + page_pool_destroy(ns->rq[i].page_pool); + ns->rq[i].page_pool = NULL; + } } static int nsim_stop(struct net_device *dev) { struct netdevsim *ns = netdev_priv(dev); + struct netdevsim *peer; + + netif_carrier_off(dev); + peer = rtnl_dereference(ns->peer); + if (peer) + netif_carrier_off(peer->netdev); - page_pool_destroy(ns->pp); + nsim_del_napi(ns); return 0; } @@ -437,7 +588,7 @@ nsim_pp_hold_write(struct file *file, const char __user *data, if (!netif_running(ns->netdev) && val) { ret = -ENETDOWN; } else if (val) { - ns->page = page_pool_dev_alloc_pages(ns->pp); + ns->page = page_pool_dev_alloc_pages(ns->rq[0].page_pool); if (!ns->page) ret = -ENOMEM; } else { @@ -477,6 +628,35 @@ static void nsim_setup(struct net_device *dev) dev->xdp_features = NETDEV_XDP_ACT_HW_OFFLOAD; } +static int nsim_queue_init(struct netdevsim *ns) +{ + struct net_device *dev = ns->netdev; + int i; + + ns->rq = kvcalloc(dev->num_rx_queues, sizeof(*ns->rq), + GFP_KERNEL_ACCOUNT | __GFP_RETRY_MAYFAIL); + if (!ns->rq) + return -ENOMEM; + + for (i = 0; i < dev->num_rx_queues; i++) + skb_queue_head_init(&ns->rq[i].skb_queue); + + return 0; +} + +static void nsim_queue_free(struct netdevsim *ns) +{ + struct net_device *dev = ns->netdev; + int i; + + for (i = 0; i < dev->num_rx_queues; i++) + skb_queue_purge_reason(&ns->rq[i].skb_queue, + SKB_DROP_REASON_QUEUE_PURGE); + + kvfree(ns->rq); + ns->rq = NULL; +} + static int nsim_init_netdevsim(struct netdevsim *ns) { struct mock_phc *phc; @@ -495,10 +675,14 @@ static int nsim_init_netdevsim(struct netdevsim *ns) goto err_phc_destroy; rtnl_lock(); - err = nsim_bpf_init(ns); + err = nsim_queue_init(ns); if (err) goto err_utn_destroy; + err = nsim_bpf_init(ns); + if (err) + goto err_rq_destroy; + nsim_macsec_init(ns); nsim_ipsec_init(ns); @@ -512,6 +696,8 @@ err_ipsec_teardown: nsim_ipsec_teardown(ns); nsim_macsec_teardown(ns); nsim_bpf_uninit(ns); +err_rq_destroy: + nsim_queue_free(ns); err_utn_destroy: rtnl_unlock(); nsim_udp_tunnels_info_destroy(ns->netdev); @@ -593,6 +779,7 @@ void nsim_destroy(struct netdevsim *ns) nsim_macsec_teardown(ns); nsim_ipsec_teardown(ns); nsim_bpf_uninit(ns); + nsim_queue_free(ns); } rtnl_unlock(); if (nsim_dev_port_is_pf(ns->nsim_dev_port)) -- cgit v1.2.3