]> www.infradead.org Git - users/jedix/linux-maple.git/commitdiff
netdevsim: forward skbs from one connected port to another
authorDavid Wei <dw@davidwei.uk>
Wed, 28 Feb 2024 23:22:50 +0000 (15:22 -0800)
committerDavid S. Miller <davem@davemloft.net>
Fri, 1 Mar 2024 10:43:10 +0000 (10:43 +0000)
Forward skbs sent from one netdevsim port to its connected netdevsim
port using dev_forward_skb, in a spirit similar to veth.

Add a tx_dropped variable to struct netdevsim, tracking the number of
skbs that could not be forwarded using dev_forward_skb().

The xmit() function accessing the peer ptr is protected by an RCU read
critical section. The rcu_read_lock() is functionally redundant as since
v5.0 all softirqs are implicitly RCU read critical sections; but it is
useful for human readers.

If another CPU is concurrently in nsim_destroy(), then it will first set
the peer ptr to NULL. This does not affect any existing readers that
dereferenced a non-NULL peer. Then, in unregister_netdevice(), there is
a synchronize_rcu() before the netdev is actually unregistered and
freed. This ensures that any readers i.e. xmit() that got a non-NULL
peer will complete before the netdev is freed.

Any readers after the RCU_INIT_POINTER() but before synchronize_rcu()
will dereference NULL, making it safe.

The codepath to nsim_destroy() and nsim_create() takes both the newly
added nsim_dev_list_lock and rtnl_lock. This makes it safe with
concurrent calls to linking two netdevsims together.

Signed-off-by: David Wei <dw@davidwei.uk>
Reviewed-by: Maciek Machnikowski <maciek@machnikowski.net>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/netdevsim/netdev.c
drivers/net/netdevsim/netdevsim.h

index 9063f4f2971b9ec5624edfb5821f80e771640ece..c3f3fda5fdc09a3d853123bc18c71ef9d6c8421f 100644 (file)
 static netdev_tx_t nsim_start_xmit(struct sk_buff *skb, struct net_device *dev)
 {
        struct netdevsim *ns = netdev_priv(dev);
+       unsigned int len = skb->len;
+       struct netdevsim *peer_ns;
 
+       rcu_read_lock();
        if (!nsim_ipsec_tx(ns, skb))
-               goto out;
+               goto out_drop_free;
 
+       peer_ns = rcu_dereference(ns->peer);
+       if (!peer_ns)
+               goto out_drop_free;
+
+       skb_tx_timestamp(skb);
+       if (unlikely(dev_forward_skb(peer_ns->netdev, skb) == NET_RX_DROP))
+               goto out_drop_cnt;
+
+       rcu_read_unlock();
        u64_stats_update_begin(&ns->syncp);
        ns->tx_packets++;
-       ns->tx_bytes += skb->len;
+       ns->tx_bytes += len;
        u64_stats_update_end(&ns->syncp);
+       return NETDEV_TX_OK;
 
-out:
+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);
        return NETDEV_TX_OK;
 }
 
@@ -70,6 +87,7 @@ nsim_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *stats)
                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));
 }
 
@@ -302,7 +320,6 @@ static void nsim_setup(struct net_device *dev)
        eth_hw_addr_random(dev);
 
        dev->tx_queue_len = 0;
-       dev->flags |= IFF_NOARP;
        dev->flags &= ~IFF_MULTICAST;
        dev->priv_flags |= IFF_LIVE_ADDR_CHANGE |
                           IFF_NO_QUEUE;
index c8b45b0d955efe71c08867a3315f208c2ed655a9..553c4b9b4f63e3e0958fd1469164a51c7891cfbe 100644 (file)
@@ -98,6 +98,7 @@ struct netdevsim {
 
        u64 tx_packets;
        u64 tx_bytes;
+       u64 tx_dropped;
        struct u64_stats_sync syncp;
 
        struct nsim_bus_dev *nsim_bus_dev;