netdevsim: forward skbs from one connected port to another
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>
This commit is contained in:
parent
f532957d76
commit
9eb95228a7
|
@ -29,18 +29,35 @@
|
|||
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;
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue