* [PATCH net-next 0/3] vxlan: RCU protect vxlan_config and enable RTNL-less fill_info
@ 2026-07-08 16:04 Eric Dumazet
2026-07-08 16:04 ` [PATCH net-next 1/3] vxlan: pass vxlan_config pointer to helper functions Eric Dumazet
` (3 more replies)
0 siblings, 4 replies; 10+ messages in thread
From: Eric Dumazet @ 2026-07-08 16:04 UTC (permalink / raw)
To: David S . Miller, Jakub Kicinski, Paolo Abeni
Cc: Simon Horman, Kuniyuki Iwashima, Ido Schimmel, Andrew Lunn,
netdev, eric.dumazet, Eric Dumazet
This patch series converts VXLAN configuration (`struct vxlan_config`)
to be RCU-protected, allowing lockless readers and enabling RTNL-less
`vxlan_fill_info()` netlink link info dumping.
Work has been split into three distinct parts:
1. Refactor helper functions in RX, TX, MDB, VNIFILTER, and OVS paths to
accept a `const struct vxlan_config *cfg` pointer (or pass specific
fields such as `flags`/`saddr_family`) rather than accessing
`vxlan->cfg` directly.
2. Convert `vxlan->cfg` from an embedded struct into an RCU-protected
pointer (`struct vxlan_config __rcu *cfg`), dynamically allocated on
device creation or changelink, and freed via `kfree_rcu()`. Update
offload drivers (`mlxsw`) and OVS (`vport-vxlan.c`) accordingly.
3. Update `vxlan_fill_info()` to run under `rcu_read_lock()` instead of
relying on RTNL lock, completing the transition to lockless link info
dumping for VXLAN devices.
Eric Dumazet (3):
vxlan: pass vxlan_config pointer to helper functions
vxlan: convert configuration to RCU protection
vxlan: no longer rely on RTNL in vxlan_fill_info()
.../mellanox/mlxsw/spectrum_nve_vxlan.c | 14 +-
.../mellanox/mlxsw/spectrum_switchdev.c | 57 +-
drivers/net/vxlan/vxlan_core.c | 566 +++++++++++-------
drivers/net/vxlan/vxlan_mdb.c | 37 +-
drivers/net/vxlan/vxlan_multicast.c | 12 +-
drivers/net/vxlan/vxlan_private.h | 6 +-
drivers/net/vxlan/vxlan_vnifilter.c | 24 +-
include/net/vxlan.h | 3 +-
net/openvswitch/vport-vxlan.c | 34 +-
9 files changed, 475 insertions(+), 278 deletions(-)
--
2.55.0.795.g602f6c329a-goog
^ permalink raw reply [flat|nested] 10+ messages in thread
* [PATCH net-next 1/3] vxlan: pass vxlan_config pointer to helper functions
2026-07-08 16:04 [PATCH net-next 0/3] vxlan: RCU protect vxlan_config and enable RTNL-less fill_info Eric Dumazet
@ 2026-07-08 16:04 ` Eric Dumazet
2026-07-09 4:53 ` Pavan Chebbi
2026-07-08 16:04 ` [PATCH net-next 2/3] vxlan: convert configuration to RCU protection Eric Dumazet
` (2 subsequent siblings)
3 siblings, 1 reply; 10+ messages in thread
From: Eric Dumazet @ 2026-07-08 16:04 UTC (permalink / raw)
To: David S . Miller, Jakub Kicinski, Paolo Abeni
Cc: Simon Horman, Kuniyuki Iwashima, Ido Schimmel, Andrew Lunn,
netdev, eric.dumazet, Eric Dumazet
In preparation for converting vxlan->cfg to an RCU-protected pointer,
refactor internal helper functions in the RX, TX, MDB, VNIFILTER, and
OVS paths to accept a pointer to struct vxlan_config (or pass flags/
saddr_family where appropriate) rather than directly accessing
vxlan->cfg.
Signed-off-by: Eric Dumazet <edumazet@google.com>
---
drivers/net/vxlan/vxlan_core.c | 249 +++++++++++++++-------------
drivers/net/vxlan/vxlan_mdb.c | 20 ++-
drivers/net/vxlan/vxlan_private.h | 6 +-
drivers/net/vxlan/vxlan_vnifilter.c | 5 +-
net/openvswitch/vport-vxlan.c | 7 +-
5 files changed, 158 insertions(+), 129 deletions(-)
diff --git a/drivers/net/vxlan/vxlan_core.c b/drivers/net/vxlan/vxlan_core.c
index 67c367cc566233e809b0f70e0d939dd1c1ac0d9f..eb2608fb7139a18d905d9b1a5140f22a880818d6 100644
--- a/drivers/net/vxlan/vxlan_core.c
+++ b/drivers/net/vxlan/vxlan_core.c
@@ -377,14 +377,15 @@ static void vxlan_fdb_miss(struct vxlan_dev *vxlan, const u8 eth_addr[ETH_ALEN])
/* Look up Ethernet address in forwarding table */
static struct vxlan_fdb *vxlan_find_mac_rcu(struct vxlan_dev *vxlan,
+ const struct vxlan_config *cfg,
const u8 *mac, __be32 vni)
{
struct vxlan_fdb_key key;
memset(&key, 0, sizeof(key));
memcpy(key.eth_addr, mac, sizeof(key.eth_addr));
- if (!(vxlan->cfg.flags & VXLAN_F_COLLECT_METADATA))
- key.vni = vxlan->default_dst.remote_vni;
+ if (!(cfg->flags & VXLAN_F_COLLECT_METADATA))
+ key.vni = cfg->vni;
else
key.vni = vni;
@@ -393,11 +394,12 @@ static struct vxlan_fdb *vxlan_find_mac_rcu(struct vxlan_dev *vxlan,
}
static struct vxlan_fdb *vxlan_find_mac_tx(struct vxlan_dev *vxlan,
+ const struct vxlan_config *cfg,
const u8 *mac, __be32 vni)
{
struct vxlan_fdb *f;
- f = vxlan_find_mac_rcu(vxlan, mac, vni);
+ f = vxlan_find_mac_rcu(vxlan, cfg, mac, vni);
if (f) {
unsigned long now = jiffies;
@@ -416,7 +418,7 @@ static struct vxlan_fdb *vxlan_find_mac(struct vxlan_dev *vxlan,
lockdep_assert_held_once(&vxlan->hash_lock);
rcu_read_lock();
- f = vxlan_find_mac_rcu(vxlan, mac, vni);
+ f = vxlan_find_mac_rcu(vxlan, &vxlan->cfg, mac, vni);
rcu_read_unlock();
return f;
@@ -457,7 +459,7 @@ int vxlan_fdb_find_uc(struct net_device *dev, const u8 *mac, __be32 vni,
rcu_read_lock();
- f = vxlan_find_mac_rcu(vxlan, eth_addr, vni);
+ f = vxlan_find_mac_rcu(vxlan, &vxlan->cfg, eth_addr, vni);
if (f)
rdst = first_remote_rcu(f);
if (!rdst) {
@@ -1405,7 +1407,7 @@ static int vxlan_fdb_get(struct sk_buff *skb,
rcu_read_lock();
- f = vxlan_find_mac_rcu(vxlan, addr, vni);
+ f = vxlan_find_mac_rcu(vxlan, &vxlan->cfg, addr, vni);
if (!f) {
NL_SET_ERR_MSG(extack, "Fdb entry not found");
err = -ENOENT;
@@ -1423,6 +1425,7 @@ static int vxlan_fdb_get(struct sk_buff *skb,
* and Tunnel endpoint.
*/
static enum skb_drop_reason vxlan_snoop(struct net_device *dev,
+ const struct vxlan_config *cfg,
union vxlan_addr *src_ip,
const u8 *src_mac, u32 src_ifindex,
__be32 vni)
@@ -1441,7 +1444,7 @@ static enum skb_drop_reason vxlan_snoop(struct net_device *dev,
ifindex = src_ifindex;
#endif
- f = vxlan_find_mac_rcu(vxlan, src_mac, vni);
+ f = vxlan_find_mac_rcu(vxlan, cfg, src_mac, vni);
if (likely(f)) {
struct vxlan_rdst *rdst = first_remote_rcu(f);
unsigned long now = jiffies;
@@ -1477,9 +1480,9 @@ static enum skb_drop_reason vxlan_snoop(struct net_device *dev,
vxlan_fdb_update(vxlan, src_mac, src_ip,
NUD_REACHABLE,
NLM_F_EXCL|NLM_F_CREATE,
- vxlan->cfg.dst_port,
+ cfg->dst_port,
vni,
- vxlan->default_dst.remote_vni,
+ cfg->vni,
ifindex, NTF_SELF, 0, true, NULL);
spin_unlock(&vxlan->hash_lock);
}
@@ -1587,6 +1590,7 @@ static void vxlan_parse_gbp_hdr(struct sk_buff *skb, u32 vxflags,
}
static enum skb_drop_reason vxlan_set_mac(struct vxlan_dev *vxlan,
+ const struct vxlan_config *cfg,
struct vxlan_sock *vs,
struct sk_buff *skb, __be32 vni)
{
@@ -1612,10 +1616,10 @@ static enum skb_drop_reason vxlan_set_mac(struct vxlan_dev *vxlan,
#endif
}
- if (!(vxlan->cfg.flags & VXLAN_F_LEARN))
+ if (!(cfg->flags & VXLAN_F_LEARN))
return SKB_NOT_DROPPED_YET;
- return vxlan_snoop(skb->dev, &saddr, eth_hdr(skb)->h_source,
+ return vxlan_snoop(skb->dev, cfg, &saddr, eth_hdr(skb)->h_source,
ifindex, vni);
}
@@ -1646,18 +1650,20 @@ static bool vxlan_ecn_decapsulate(struct vxlan_sock *vs, void *oiph,
static int vxlan_rcv(struct sock *sk, struct sk_buff *skb)
{
struct vxlan_vni_node *vninode = NULL;
- const struct vxlanhdr *vh;
- struct vxlan_dev *vxlan;
- struct vxlan_sock *vs;
- struct vxlan_metadata _md;
- struct vxlan_metadata *md = &_md;
__be16 protocol = htons(ETH_P_TEB);
enum skb_drop_reason reason;
+ const struct vxlanhdr *vh;
+ struct vxlan_metadata *md;
+ struct vxlan_metadata _md;
+ struct vxlan_dev *vxlan;
bool raw_proto = false;
- void *oiph;
+ struct vxlan_sock *vs;
__be32 vni = 0;
+ void *oiph;
int nh;
+ md = &_md;
+
/* Need UDP and VXLAN header to be present */
reason = pskb_may_pull_reason(skb, VXLAN_HLEN);
if (reason)
@@ -1698,7 +1704,7 @@ static int vxlan_rcv(struct sock *sk, struct sk_buff *skb)
reason = SKB_DROP_REASON_VXLAN_INVALID_HDR;
DEV_STATS_INC(vxlan->dev, rx_frame_errors);
DEV_STATS_INC(vxlan->dev, rx_errors);
- vxlan_vnifilter_count(vxlan, vni, vninode,
+ vxlan_vnifilter_count(vxlan, &vxlan->cfg, vni, vninode,
VXLAN_VNI_STATS_RX_ERRORS, 0);
goto drop;
}
@@ -1748,7 +1754,7 @@ static int vxlan_rcv(struct sock *sk, struct sk_buff *skb)
*/
if (!raw_proto) {
- reason = vxlan_set_mac(vxlan, vs, skb, vni);
+ reason = vxlan_set_mac(vxlan, &vxlan->cfg, vs, skb, vni);
if (reason)
goto drop;
} else {
@@ -1769,7 +1775,7 @@ static int vxlan_rcv(struct sock *sk, struct sk_buff *skb)
if (reason) {
DEV_STATS_INC(vxlan->dev, rx_length_errors);
DEV_STATS_INC(vxlan->dev, rx_errors);
- vxlan_vnifilter_count(vxlan, vni, vninode,
+ vxlan_vnifilter_count(vxlan, &vxlan->cfg, vni, vninode,
VXLAN_VNI_STATS_RX_ERRORS, 0);
goto drop;
}
@@ -1781,7 +1787,7 @@ static int vxlan_rcv(struct sock *sk, struct sk_buff *skb)
reason = SKB_DROP_REASON_IP_TUNNEL_ECN;
DEV_STATS_INC(vxlan->dev, rx_frame_errors);
DEV_STATS_INC(vxlan->dev, rx_errors);
- vxlan_vnifilter_count(vxlan, vni, vninode,
+ vxlan_vnifilter_count(vxlan, &vxlan->cfg, vni, vninode,
VXLAN_VNI_STATS_RX_ERRORS, 0);
goto drop;
}
@@ -1791,14 +1797,14 @@ static int vxlan_rcv(struct sock *sk, struct sk_buff *skb)
if (unlikely(!(vxlan->dev->flags & IFF_UP))) {
rcu_read_unlock();
dev_dstats_rx_dropped(vxlan->dev);
- vxlan_vnifilter_count(vxlan, vni, vninode,
+ vxlan_vnifilter_count(vxlan, &vxlan->cfg, vni, vninode,
VXLAN_VNI_STATS_RX_DROPS, 0);
reason = SKB_DROP_REASON_DEV_READY;
goto drop;
}
dev_dstats_rx_add(vxlan->dev, skb->len);
- vxlan_vnifilter_count(vxlan, vni, vninode, VXLAN_VNI_STATS_RX, skb->len);
+ vxlan_vnifilter_count(vxlan, &vxlan->cfg, vni, vninode, VXLAN_VNI_STATS_RX, skb->len);
gro_cells_receive(&vxlan->gro_cells, skb);
rcu_read_unlock();
@@ -1839,7 +1845,7 @@ static int vxlan_err_lookup(struct sock *sk, struct sk_buff *skb)
return 0;
}
-static int arp_reduce(struct net_device *dev, struct sk_buff *skb, __be32 vni)
+static int arp_reduce(struct net_device *dev, struct sk_buff *skb, __be32 vni, u32 flags)
{
struct vxlan_dev *vxlan = netdev_priv(dev);
struct arphdr *parp;
@@ -1852,7 +1858,7 @@ static int arp_reduce(struct net_device *dev, struct sk_buff *skb, __be32 vni)
if (!pskb_may_pull(skb, arp_hdr_len(dev))) {
dev_dstats_tx_dropped(dev);
- vxlan_vnifilter_count(vxlan, vni, NULL,
+ vxlan_vnifilter_count(vxlan, &vxlan->cfg, vni, NULL,
VXLAN_VNI_STATS_TX_DROPS, 0);
goto out;
}
@@ -1890,7 +1896,7 @@ static int arp_reduce(struct net_device *dev, struct sk_buff *skb, __be32 vni)
}
rcu_read_lock();
- f = vxlan_find_mac_tx(vxlan, n->ha, vni);
+ f = vxlan_find_mac_tx(vxlan, &vxlan->cfg, n->ha, vni);
if (f)
rdst = first_remote_rcu(f);
if (rdst && vxlan_addr_any(&rdst->remote_ip)) {
@@ -1916,11 +1922,11 @@ static int arp_reduce(struct net_device *dev, struct sk_buff *skb, __be32 vni)
if (netif_rx(reply) == NET_RX_DROP) {
dev_dstats_rx_dropped(dev);
- vxlan_vnifilter_count(vxlan, vni, NULL,
+ vxlan_vnifilter_count(vxlan, &vxlan->cfg, vni, NULL,
VXLAN_VNI_STATS_RX_DROPS, 0);
}
- } else if (vxlan->cfg.flags & VXLAN_F_L3MISS) {
+ } else if (flags & VXLAN_F_L3MISS) {
union vxlan_addr ipa = {
.sin.sin_addr.s_addr = tip,
.sin.sin_family = AF_INET,
@@ -2027,7 +2033,7 @@ static struct sk_buff *vxlan_na_create(struct sk_buff *request,
return reply;
}
-static int neigh_reduce(struct net_device *dev, struct sk_buff *skb, __be32 vni)
+static int neigh_reduce(struct net_device *dev, struct sk_buff *skb, __be32 vni, u32 flags)
{
struct vxlan_dev *vxlan = netdev_priv(dev);
const struct in6_addr *daddr;
@@ -2059,7 +2065,7 @@ static int neigh_reduce(struct net_device *dev, struct sk_buff *skb, __be32 vni)
goto out;
}
- f = vxlan_find_mac_tx(vxlan, n->ha, vni);
+ f = vxlan_find_mac_tx(vxlan, &vxlan->cfg, n->ha, vni);
if (f)
rdst = first_remote_rcu(f);
if (rdst && vxlan_addr_any(&rdst->remote_ip)) {
@@ -2078,10 +2084,10 @@ static int neigh_reduce(struct net_device *dev, struct sk_buff *skb, __be32 vni)
if (netif_rx(reply) == NET_RX_DROP) {
dev_dstats_rx_dropped(dev);
- vxlan_vnifilter_count(vxlan, vni, NULL,
+ vxlan_vnifilter_count(vxlan, &vxlan->cfg, vni, NULL,
VXLAN_VNI_STATS_RX_DROPS, 0);
}
- } else if (vxlan->cfg.flags & VXLAN_F_L3MISS) {
+ } else if (flags & VXLAN_F_L3MISS) {
union vxlan_addr ipa = {
.sin6.sin6_addr = msg->target,
.sin6.sin6_family = AF_INET6,
@@ -2097,9 +2103,9 @@ static int neigh_reduce(struct net_device *dev, struct sk_buff *skb, __be32 vni)
}
#endif
-static bool route_shortcircuit(struct net_device *dev, struct sk_buff *skb)
+static bool route_shortcircuit(struct net_device *dev, struct sk_buff *skb,
+ const struct vxlan_config *cfg)
{
- struct vxlan_dev *vxlan = netdev_priv(dev);
struct neighbour *n;
if (is_multicast_ether_addr(eth_hdr(skb)->h_dest))
@@ -2115,7 +2121,7 @@ static bool route_shortcircuit(struct net_device *dev, struct sk_buff *skb)
return false;
pip = ip_hdr(skb);
n = neigh_lookup(&arp_tbl, &pip->daddr, dev);
- if (!n && (vxlan->cfg.flags & VXLAN_F_L3MISS)) {
+ if (!n && (cfg->flags & VXLAN_F_L3MISS)) {
union vxlan_addr ipa = {
.sin.sin_addr.s_addr = pip->daddr,
.sin.sin_family = AF_INET,
@@ -2141,7 +2147,7 @@ static bool route_shortcircuit(struct net_device *dev, struct sk_buff *skb)
return false;
pip6 = ipv6_hdr(skb);
n = neigh_lookup(&nd_tbl, &pip6->daddr, dev);
- if (!n && (vxlan->cfg.flags & VXLAN_F_L3MISS)) {
+ if (!n && (cfg->flags & VXLAN_F_L3MISS)) {
union vxlan_addr ipa = {
.sin6.sin6_addr = pip6->daddr,
.sin6.sin6_family = AF_INET6,
@@ -2253,20 +2259,20 @@ static int vxlan_build_skb(struct sk_buff *skb, struct dst_entry *dst,
/* Bypass encapsulation if the destination is local */
static void vxlan_encap_bypass(struct sk_buff *skb, struct vxlan_dev *src_vxlan,
- struct vxlan_dev *dst_vxlan, __be32 vni,
- bool snoop)
+ struct vxlan_dev *dst_vxlan,
+ const struct vxlan_config *cfg,
+ __be32 vni, bool snoop)
{
union vxlan_addr loopback;
- union vxlan_addr *remote_ip = &dst_vxlan->default_dst.remote_ip;
unsigned int len = skb->len;
- struct net_device *dev;
+ struct net_device *dev = dst_vxlan->dev;
skb->pkt_type = PACKET_HOST;
skb->encapsulation = 0;
- skb->dev = dst_vxlan->dev;
+ skb->dev = dev;
__skb_pull(skb, skb_network_offset(skb));
- if (remote_ip->sa.sa_family == AF_INET) {
+ if (cfg->remote_ip.sa.sa_family == AF_INET) {
loopback.sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
loopback.sa.sa_family = AF_INET;
#if IS_ENABLED(CONFIG_IPV6)
@@ -2277,26 +2283,25 @@ static void vxlan_encap_bypass(struct sk_buff *skb, struct vxlan_dev *src_vxlan,
}
rcu_read_lock();
- dev = skb->dev;
if (unlikely(!(dev->flags & IFF_UP))) {
kfree_skb_reason(skb, SKB_DROP_REASON_DEV_READY);
goto drop;
}
- if ((dst_vxlan->cfg.flags & VXLAN_F_LEARN) && snoop)
- vxlan_snoop(dev, &loopback, eth_hdr(skb)->h_source, 0, vni);
+ if ((cfg->flags & VXLAN_F_LEARN) && snoop)
+ vxlan_snoop(dev, cfg, &loopback, eth_hdr(skb)->h_source, 0, vni);
dev_dstats_tx_add(src_vxlan->dev, len);
- vxlan_vnifilter_count(src_vxlan, vni, NULL, VXLAN_VNI_STATS_TX, len);
+ vxlan_vnifilter_count(src_vxlan, cfg, vni, NULL, VXLAN_VNI_STATS_TX, len);
if (__netif_rx(skb) == NET_RX_SUCCESS) {
dev_dstats_rx_add(dst_vxlan->dev, len);
- vxlan_vnifilter_count(dst_vxlan, vni, NULL, VXLAN_VNI_STATS_RX,
+ vxlan_vnifilter_count(dst_vxlan, cfg, vni, NULL, VXLAN_VNI_STATS_RX,
len);
} else {
drop:
dev_dstats_rx_dropped(dev);
- vxlan_vnifilter_count(dst_vxlan, vni, NULL,
+ vxlan_vnifilter_count(dst_vxlan, cfg, vni, NULL,
VXLAN_VNI_STATS_RX_DROPS, 0);
}
rcu_read_unlock();
@@ -2304,6 +2309,7 @@ static void vxlan_encap_bypass(struct sk_buff *skb, struct vxlan_dev *src_vxlan,
static int encap_bypass_if_local(struct sk_buff *skb, struct net_device *dev,
struct vxlan_dev *vxlan,
+ const struct vxlan_config *cfg,
int addr_family,
__be16 dst_port, int dst_ifindex, __be32 vni,
struct dst_entry *dst,
@@ -2319,22 +2325,22 @@ static int encap_bypass_if_local(struct sk_buff *skb, struct net_device *dev,
/* Bypass encapsulation if the destination is local */
if (rt_flags & RTCF_LOCAL &&
!(rt_flags & (RTCF_BROADCAST | RTCF_MULTICAST)) &&
- vxlan->cfg.flags & VXLAN_F_LOCALBYPASS) {
+ cfg->flags & VXLAN_F_LOCALBYPASS) {
struct vxlan_dev *dst_vxlan;
dst_release(dst);
dst_vxlan = vxlan_find_vni(vxlan->net, dst_ifindex, vni,
addr_family, dst_port,
- vxlan->cfg.flags);
+ cfg->flags);
if (!dst_vxlan) {
DEV_STATS_INC(dev, tx_errors);
- vxlan_vnifilter_count(vxlan, vni, NULL,
+ vxlan_vnifilter_count(vxlan, cfg, vni, NULL,
VXLAN_VNI_STATS_TX_ERRORS, 0);
kfree_skb_reason(skb, SKB_DROP_REASON_VXLAN_VNI_NOT_FOUND);
return -ENOENT;
}
- vxlan_encap_bypass(skb, vxlan, dst_vxlan, vni, true);
+ vxlan_encap_bypass(skb, vxlan, dst_vxlan, cfg, vni, true);
return 1;
}
@@ -2342,30 +2348,35 @@ static int encap_bypass_if_local(struct sk_buff *skb, struct net_device *dev,
}
void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev,
+ const struct vxlan_config *cfg,
__be32 default_vni, struct vxlan_rdst *rdst, bool did_rsc)
{
+ unsigned int pkt_len = skb->len;
+ __be16 src_port = 0, dst_port;
+ struct dst_entry *ndst = NULL;
+ enum skb_drop_reason reason;
+ struct vxlan_dev *vxlan;
struct dst_cache *dst_cache;
+ const struct iphdr *old_iph;
struct ip_tunnel_info *info;
struct ip_tunnel_key *pkey;
- struct ip_tunnel_key key;
- struct vxlan_dev *vxlan = netdev_priv(dev);
- const struct iphdr *old_iph;
+ struct vxlan_metadata *md;
struct vxlan_metadata _md;
- struct vxlan_metadata *md = &_md;
- unsigned int pkt_len = skb->len;
- __be16 src_port = 0, dst_port;
- struct dst_entry *ndst = NULL;
+ struct ip_tunnel_key key;
+ u32 flags = cfg->flags;
+ bool udp_sum = false;
+ bool no_eth_encap;
int addr_family;
+ bool use_cache;
+ __be32 vni = 0;
+ bool xnet;
__u8 tos, ttl;
int ifindex;
int err = 0;
- u32 flags = vxlan->cfg.flags;
- bool use_cache;
- bool udp_sum = false;
- bool xnet = !net_eq(vxlan->net, dev_net(vxlan->dev));
- enum skb_drop_reason reason;
- bool no_eth_encap;
- __be32 vni = 0;
+
+ vxlan = netdev_priv(dev);
+ xnet = !net_eq(vxlan->net, dev_net(vxlan->dev));
+ md = &_md;
no_eth_encap = flags & VXLAN_F_GPE && skb->protocol != htons(ETH_P_TEB);
reason = skb_vlan_inet_prepare(skb, no_eth_encap);
@@ -2385,23 +2396,23 @@ void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev,
if (vxlan_addr_any(&rdst->remote_ip)) {
if (did_rsc) {
/* short-circuited back to local bridge */
- vxlan_encap_bypass(skb, vxlan, vxlan,
+ vxlan_encap_bypass(skb, vxlan, vxlan, cfg,
default_vni, true);
return;
}
goto drop;
}
- addr_family = vxlan->cfg.saddr.sa.sa_family;
- dst_port = rdst->remote_port ? rdst->remote_port : vxlan->cfg.dst_port;
+ addr_family = cfg->saddr.sa.sa_family;
+ dst_port = rdst->remote_port ? rdst->remote_port : cfg->dst_port;
vni = (rdst->remote_vni) ? : default_vni;
ifindex = rdst->remote_ifindex;
if (addr_family == AF_INET) {
- key.u.ipv4.src = vxlan->cfg.saddr.sin.sin_addr.s_addr;
+ key.u.ipv4.src = cfg->saddr.sin.sin_addr.s_addr;
key.u.ipv4.dst = rdst->remote_ip.sin.sin_addr.s_addr;
} else {
- key.u.ipv6.src = vxlan->cfg.saddr.sin6.sin6_addr;
+ key.u.ipv6.src = cfg->saddr.sin6.sin6_addr;
key.u.ipv6.dst = rdst->remote_ip.sin6.sin6_addr;
}
@@ -2410,11 +2421,11 @@ void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev,
if (flags & VXLAN_F_TTL_INHERIT) {
ttl = ip_tunnel_get_ttl(old_iph, skb);
} else {
- ttl = vxlan->cfg.ttl;
+ ttl = cfg->ttl;
if (!ttl && vxlan_addr_multicast(&rdst->remote_ip))
ttl = 1;
}
- tos = vxlan->cfg.tos;
+ tos = cfg->tos;
if (tos == 1)
tos = ip_tunnel_get_dsfield(old_iph, skb);
if (tos && !info)
@@ -2425,9 +2436,9 @@ void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev,
else
udp_sum = !(flags & VXLAN_F_UDP_ZERO_CSUM6_TX);
#if IS_ENABLED(CONFIG_IPV6)
- switch (vxlan->cfg.label_policy) {
+ switch (cfg->label_policy) {
case VXLAN_LABEL_FIXED:
- key.label = vxlan->cfg.label;
+ key.label = cfg->label;
break;
case VXLAN_LABEL_INHERIT:
key.label = ip_tunnel_get_flowlabel(old_iph, skb);
@@ -2445,7 +2456,7 @@ void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev,
}
pkey = &info->key;
addr_family = ip_tunnel_info_af(info);
- dst_port = info->key.tp_dst ? : vxlan->cfg.dst_port;
+ dst_port = info->key.tp_dst ? : cfg->dst_port;
vni = tunnel_id_to_key32(info->key.tun_id);
ifindex = 0;
dst_cache = &info->dst_cache;
@@ -2458,8 +2469,8 @@ void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev,
tos = info->key.tos;
udp_sum = test_bit(IP_TUNNEL_CSUM_BIT, info->key.tun_flags);
}
- src_port = udp_flow_src_port(dev_net(dev), skb, vxlan->cfg.port_min,
- vxlan->cfg.port_max, true);
+ src_port = udp_flow_src_port(dev_net(dev), skb, cfg->port_min,
+ cfg->port_max, true);
rcu_read_lock();
if (addr_family == AF_INET) {
@@ -2492,15 +2503,15 @@ void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev,
if (!info) {
/* Bypass encapsulation if the destination is local */
- err = encap_bypass_if_local(skb, dev, vxlan, AF_INET,
+ err = encap_bypass_if_local(skb, dev, vxlan, cfg, AF_INET,
dst_port, ifindex, vni,
&rt->dst, rt->rt_flags);
if (err)
goto out_unlock;
- if (vxlan->cfg.df == VXLAN_DF_SET) {
+ if (cfg->df == VXLAN_DF_SET) {
df = htons(IP_DF);
- } else if (vxlan->cfg.df == VXLAN_DF_INHERIT) {
+ } else if (cfg->df == VXLAN_DF_INHERIT) {
struct ethhdr *eth = eth_hdr(skb);
if (ntohs(eth->h_proto) == ETH_P_IPV6 ||
@@ -2529,7 +2540,7 @@ void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev,
unclone->key.u.ipv4.src = pkey->u.ipv4.dst;
unclone->key.u.ipv4.dst = saddr;
}
- vxlan_encap_bypass(skb, vxlan, vxlan, vni, false);
+ vxlan_encap_bypass(skb, vxlan, vxlan, cfg, vni, false);
dst_release(ndst);
goto out_unlock;
}
@@ -2579,7 +2590,7 @@ void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev,
if (!info) {
u32 rt6i_flags = dst_rt6_info(ndst)->rt6i_flags;
- err = encap_bypass_if_local(skb, dev, vxlan, AF_INET6,
+ err = encap_bypass_if_local(skb, dev, vxlan, cfg, AF_INET6,
dst_port, ifindex, vni,
ndst, rt6i_flags);
if (err)
@@ -2603,7 +2614,7 @@ void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev,
unclone->key.u.ipv6.dst = saddr;
}
- vxlan_encap_bypass(skb, vxlan, vxlan, vni, false);
+ vxlan_encap_bypass(skb, vxlan, vxlan, cfg, vni, false);
dst_release(ndst);
goto out_unlock;
}
@@ -2624,14 +2635,14 @@ void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev,
ip6cb_flags);
#endif
}
- vxlan_vnifilter_count(vxlan, vni, NULL, VXLAN_VNI_STATS_TX, pkt_len);
+ vxlan_vnifilter_count(vxlan, cfg, vni, NULL, VXLAN_VNI_STATS_TX, pkt_len);
out_unlock:
rcu_read_unlock();
return;
drop:
dev_dstats_tx_dropped(dev);
- vxlan_vnifilter_count(vxlan, vni, NULL, VXLAN_VNI_STATS_TX_DROPS, 0);
+ vxlan_vnifilter_count(vxlan, cfg, vni, NULL, VXLAN_VNI_STATS_TX_DROPS, 0);
kfree_skb_reason(skb, reason);
return;
@@ -2643,11 +2654,12 @@ void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev,
DEV_STATS_INC(dev, tx_carrier_errors);
dst_release(ndst);
DEV_STATS_INC(dev, tx_errors);
- vxlan_vnifilter_count(vxlan, vni, NULL, VXLAN_VNI_STATS_TX_ERRORS, 0);
+ vxlan_vnifilter_count(vxlan, cfg, vni, NULL, VXLAN_VNI_STATS_TX_ERRORS, 0);
kfree_skb_reason(skb, reason);
}
static void vxlan_xmit_nh(struct sk_buff *skb, struct net_device *dev,
+ const struct vxlan_config *cfg,
struct vxlan_fdb *f, __be32 vni, bool did_rsc)
{
struct vxlan_rdst nh_rdst;
@@ -2664,7 +2676,7 @@ static void vxlan_xmit_nh(struct sk_buff *skb, struct net_device *dev,
do_xmit = vxlan_fdb_nh_path_select(nh, hash, &nh_rdst);
if (likely(do_xmit))
- vxlan_xmit_one(skb, dev, vni, &nh_rdst, did_rsc);
+ vxlan_xmit_one(skb, dev, cfg, vni, &nh_rdst, did_rsc);
else
goto drop;
@@ -2672,15 +2684,15 @@ static void vxlan_xmit_nh(struct sk_buff *skb, struct net_device *dev,
drop:
dev_dstats_tx_dropped(dev);
- vxlan_vnifilter_count(netdev_priv(dev), vni, NULL,
+ vxlan_vnifilter_count(netdev_priv(dev), cfg, vni, NULL,
VXLAN_VNI_STATS_TX_DROPS, 0);
dev_kfree_skb(skb);
}
static netdev_tx_t vxlan_xmit_nhid(struct sk_buff *skb, struct net_device *dev,
- u32 nhid, __be32 vni)
+ u32 nhid, __be32 vni, int saddr_family,
+ const struct vxlan_config *cfg)
{
- struct vxlan_dev *vxlan = netdev_priv(dev);
struct vxlan_rdst nh_rdst;
struct nexthop *nh;
bool do_xmit;
@@ -2698,11 +2710,11 @@ static netdev_tx_t vxlan_xmit_nhid(struct sk_buff *skb, struct net_device *dev,
do_xmit = vxlan_fdb_nh_path_select(nh, hash, &nh_rdst);
rcu_read_unlock();
- if (vxlan->cfg.saddr.sa.sa_family != nh_rdst.remote_ip.sa.sa_family)
+ if (saddr_family != nh_rdst.remote_ip.sa.sa_family)
goto drop;
if (likely(do_xmit))
- vxlan_xmit_one(skb, dev, vni, &nh_rdst, false);
+ vxlan_xmit_one(skb, dev, cfg, vni, &nh_rdst, false);
else
goto drop;
@@ -2710,7 +2722,7 @@ static netdev_tx_t vxlan_xmit_nhid(struct sk_buff *skb, struct net_device *dev,
drop:
dev_dstats_tx_dropped(dev);
- vxlan_vnifilter_count(netdev_priv(dev), vni, NULL,
+ vxlan_vnifilter_count(netdev_priv(dev), cfg, vni, NULL,
VXLAN_VNI_STATS_TX_DROPS, 0);
dev_kfree_skb(skb);
return NETDEV_TX_OK;
@@ -2727,34 +2739,43 @@ static netdev_tx_t vxlan_xmit(struct sk_buff *skb, struct net_device *dev)
struct vxlan_dev *vxlan = netdev_priv(dev);
struct vxlan_rdst *rdst, *fdst = NULL;
const struct ip_tunnel_info *info;
+ const struct vxlan_config *cfg;
+ __be32 default_vni;
struct vxlan_fdb *f;
struct ethhdr *eth;
+ int saddr_family;
__be32 vni = 0;
- u32 nhid = 0;
bool did_rsc;
+ u32 nhid = 0;
+ u32 flags;
+
+ cfg = &vxlan->cfg;
+ flags = cfg->flags;
+ default_vni = cfg->vni;
+ saddr_family = cfg->saddr.sa.sa_family;
info = skb_tunnel_info(skb);
skb_reset_mac_header(skb);
- if (vxlan->cfg.flags & VXLAN_F_COLLECT_METADATA) {
+ if (flags & VXLAN_F_COLLECT_METADATA) {
if (info && info->mode & IP_TUNNEL_INFO_BRIDGE &&
info->mode & IP_TUNNEL_INFO_TX) {
vni = tunnel_id_to_key32(info->key.tun_id);
nhid = info->key.nhid;
} else {
if (info && info->mode & IP_TUNNEL_INFO_TX)
- vxlan_xmit_one(skb, dev, vni, NULL, false);
+ vxlan_xmit_one(skb, dev, cfg, vni, NULL, false);
else
kfree_skb_reason(skb, SKB_DROP_REASON_TUNNEL_TXINFO);
return NETDEV_TX_OK;
}
}
- if (vxlan->cfg.flags & VXLAN_F_PROXY) {
+ if (flags & VXLAN_F_PROXY) {
eth = eth_hdr(skb);
if (ntohs(eth->h_proto) == ETH_P_ARP)
- return arp_reduce(dev, skb, vni);
+ return arp_reduce(dev, skb, vni, flags);
#if IS_ENABLED(CONFIG_IPV6)
else if (ntohs(eth->h_proto) == ETH_P_IPV6 &&
pskb_may_pull(skb, sizeof(struct ipv6hdr) +
@@ -2764,15 +2785,15 @@ static netdev_tx_t vxlan_xmit(struct sk_buff *skb, struct net_device *dev)
if (m->icmph.icmp6_code == 0 &&
m->icmph.icmp6_type == NDISC_NEIGHBOUR_SOLICITATION)
- return neigh_reduce(dev, skb, vni);
+ return neigh_reduce(dev, skb, vni, flags);
}
#endif
}
if (nhid)
- return vxlan_xmit_nhid(skb, dev, nhid, vni);
+ return vxlan_xmit_nhid(skb, dev, nhid, vni, saddr_family, cfg);
- if (vxlan->cfg.flags & VXLAN_F_MDB) {
+ if (flags & VXLAN_F_MDB) {
struct vxlan_mdb_entry *mdb_entry;
rcu_read_lock();
@@ -2789,26 +2810,26 @@ static netdev_tx_t vxlan_xmit(struct sk_buff *skb, struct net_device *dev)
eth = eth_hdr(skb);
rcu_read_lock();
- f = vxlan_find_mac_tx(vxlan, eth->h_dest, vni);
+ f = vxlan_find_mac_tx(vxlan, cfg, eth->h_dest, vni);
did_rsc = false;
- if (f && (f->flags & NTF_ROUTER) && (vxlan->cfg.flags & VXLAN_F_RSC) &&
+ if (f && (f->flags & NTF_ROUTER) && (flags & VXLAN_F_RSC) &&
(ntohs(eth->h_proto) == ETH_P_IP ||
ntohs(eth->h_proto) == ETH_P_IPV6)) {
- did_rsc = route_shortcircuit(dev, skb);
+ did_rsc = route_shortcircuit(dev, skb, cfg);
if (did_rsc)
- f = vxlan_find_mac_tx(vxlan, eth->h_dest, vni);
+ f = vxlan_find_mac_tx(vxlan, cfg, eth->h_dest, vni);
}
if (f == NULL) {
- f = vxlan_find_mac_tx(vxlan, all_zeros_mac, vni);
+ f = vxlan_find_mac_tx(vxlan, cfg, all_zeros_mac, vni);
if (f == NULL) {
- if ((vxlan->cfg.flags & VXLAN_F_L2MISS) &&
+ if ((flags & VXLAN_F_L2MISS) &&
!is_multicast_ether_addr(eth->h_dest))
vxlan_fdb_miss(vxlan, eth->h_dest);
dev_dstats_tx_dropped(dev);
- vxlan_vnifilter_count(vxlan, vni, NULL,
+ vxlan_vnifilter_count(vxlan, cfg, vni, NULL,
VXLAN_VNI_STATS_TX_DROPS, 0);
kfree_skb_reason(skb, SKB_DROP_REASON_NO_TX_TARGET);
goto out;
@@ -2816,8 +2837,8 @@ static netdev_tx_t vxlan_xmit(struct sk_buff *skb, struct net_device *dev)
}
if (rcu_access_pointer(f->nh)) {
- vxlan_xmit_nh(skb, dev, f,
- (vni ? : vxlan->default_dst.remote_vni), did_rsc);
+ vxlan_xmit_nh(skb, dev, cfg, f,
+ (vni ? : default_vni), did_rsc);
} else {
list_for_each_entry_rcu(rdst, &f->remotes, list) {
struct sk_buff *skb1;
@@ -2828,10 +2849,10 @@ static netdev_tx_t vxlan_xmit(struct sk_buff *skb, struct net_device *dev)
}
skb1 = skb_clone(skb, GFP_ATOMIC);
if (skb1)
- vxlan_xmit_one(skb1, dev, vni, rdst, did_rsc);
+ vxlan_xmit_one(skb1, dev, cfg, vni, rdst, did_rsc);
}
if (fdst)
- vxlan_xmit_one(skb, dev, vni, fdst, did_rsc);
+ vxlan_xmit_one(skb, dev, cfg, vni, fdst, did_rsc);
else
kfree_skb_reason(skb, SKB_DROP_REASON_NO_TX_TARGET);
}
@@ -3701,7 +3722,7 @@ static int vxlan_sock_add(struct vxlan_dev *vxlan)
}
int vxlan_vni_in_use(struct net *src_net, struct vxlan_dev *vxlan,
- struct vxlan_config *conf, __be32 vni)
+ const struct vxlan_config *conf, __be32 vni)
{
struct vxlan_net *vn = net_generic(src_net, vxlan_net_id);
struct vxlan_dev *tmp;
diff --git a/drivers/net/vxlan/vxlan_mdb.c b/drivers/net/vxlan/vxlan_mdb.c
index 055a4969f593c9a949399242298d3a1e44e43988..c92217906d5ee90c92f3da50ca9a276793ed9aca 100644
--- a/drivers/net/vxlan/vxlan_mdb.c
+++ b/drivers/net/vxlan/vxlan_mdb.c
@@ -164,6 +164,7 @@ static int vxlan_mdb_entry_info_fill(const struct vxlan_dev *vxlan,
const struct vxlan_mdb_entry *mdb_entry,
const struct vxlan_mdb_remote *remote)
{
+ const struct vxlan_config *cfg = &vxlan->cfg;
struct vxlan_rdst *rd = rtnl_dereference(remote->rd);
struct br_mdb_entry e;
struct nlattr *nest;
@@ -188,7 +189,7 @@ static int vxlan_mdb_entry_info_fill(const struct vxlan_dev *vxlan,
vxlan_nla_put_addr(skb, MDBA_MDB_EATTR_DST, &rd->remote_ip))
goto nest_err;
- if (rd->remote_port && rd->remote_port != vxlan->cfg.dst_port &&
+ if (rd->remote_port && rd->remote_port != cfg->dst_port &&
nla_put_u16(skb, MDBA_MDB_EATTR_DST_PORT,
be16_to_cpu(rd->remote_port)))
goto nest_err;
@@ -201,7 +202,7 @@ static int vxlan_mdb_entry_info_fill(const struct vxlan_dev *vxlan,
nla_put_u32(skb, MDBA_MDB_EATTR_IFINDEX, rd->remote_ifindex))
goto nest_err;
- if ((vxlan->cfg.flags & VXLAN_F_COLLECT_METADATA) &&
+ if ((cfg->flags & VXLAN_F_COLLECT_METADATA) &&
mdb_entry->key.vni && nla_put_u32(skb, MDBA_MDB_EATTR_SRC_VNI,
be32_to_cpu(mdb_entry->key.vni)))
goto nest_err;
@@ -604,6 +605,7 @@ static int vxlan_mdb_config_init(struct vxlan_mdb_config *cfg,
{
struct br_mdb_entry *entry = nla_data(tb[MDBA_SET_ENTRY]);
struct vxlan_dev *vxlan = netdev_priv(dev);
+ const struct vxlan_config *vcfg = &vxlan->cfg;
memset(cfg, 0, sizeof(*cfg));
cfg->vxlan = vxlan;
@@ -613,7 +615,7 @@ static int vxlan_mdb_config_init(struct vxlan_mdb_config *cfg,
cfg->filter_mode = MCAST_EXCLUDE;
cfg->rt_protocol = RTPROT_STATIC;
cfg->remote_vni = vxlan->default_dst.remote_vni;
- cfg->remote_port = vxlan->cfg.dst_port;
+ cfg->remote_port = vcfg->dst_port;
if (entry->ifindex != dev->ifindex) {
NL_SET_ERR_MSG_MOD(extack, "Port net device must be the VXLAN net device");
@@ -938,6 +940,7 @@ vxlan_mdb_nlmsg_remote_size(const struct vxlan_dev *vxlan,
const struct vxlan_mdb_entry *mdb_entry,
const struct vxlan_mdb_remote *remote)
{
+ const struct vxlan_config *cfg = &vxlan->cfg;
const struct vxlan_mdb_entry_key *group = &mdb_entry->key;
struct vxlan_rdst *rd = rtnl_dereference(remote->rd);
size_t nlmsg_size;
@@ -959,7 +962,7 @@ vxlan_mdb_nlmsg_remote_size(const struct vxlan_dev *vxlan,
/* MDBA_MDB_EATTR_DST */
nlmsg_size += nla_total_size(vxlan_addr_size(&rd->remote_ip));
/* MDBA_MDB_EATTR_DST_PORT */
- if (rd->remote_port && rd->remote_port != vxlan->cfg.dst_port)
+ if (rd->remote_port && rd->remote_port != cfg->dst_port)
nlmsg_size += nla_total_size(sizeof(u16));
/* MDBA_MDB_EATTR_VNI */
if (rd->remote_vni != vxlan->default_dst.remote_vni)
@@ -968,7 +971,7 @@ vxlan_mdb_nlmsg_remote_size(const struct vxlan_dev *vxlan,
if (rd->remote_ifindex)
nlmsg_size += nla_total_size(sizeof(u32));
/* MDBA_MDB_EATTR_SRC_VNI */
- if ((vxlan->cfg.flags & VXLAN_F_COLLECT_METADATA) && group->vni)
+ if ((cfg->flags & VXLAN_F_COLLECT_METADATA) && group->vni)
nlmsg_size += nla_total_size(sizeof(u32));
return nlmsg_size;
@@ -1607,6 +1610,7 @@ struct vxlan_mdb_entry *vxlan_mdb_entry_skb_get(struct vxlan_dev *vxlan,
struct sk_buff *skb,
__be32 src_vni)
{
+ const struct vxlan_config *cfg = &vxlan->cfg;
struct vxlan_mdb_entry *mdb_entry;
struct vxlan_mdb_entry_key group;
@@ -1617,7 +1621,7 @@ struct vxlan_mdb_entry *vxlan_mdb_entry_skb_get(struct vxlan_dev *vxlan,
/* When not in collect metadata mode, 'src_vni' is zero, but MDB
* entries are stored with the VNI of the VXLAN device.
*/
- if (!(vxlan->cfg.flags & VXLAN_F_COLLECT_METADATA))
+ if (!(cfg->flags & VXLAN_F_COLLECT_METADATA))
src_vni = vxlan->default_dst.remote_vni;
memset(&group, 0, sizeof(group));
@@ -1704,12 +1708,12 @@ netdev_tx_t vxlan_mdb_xmit(struct vxlan_dev *vxlan,
skb1 = skb_clone(skb, GFP_ATOMIC);
if (skb1)
- vxlan_xmit_one(skb1, vxlan->dev, src_vni,
+ vxlan_xmit_one(skb1, vxlan->dev, &vxlan->cfg, src_vni,
rcu_dereference(remote->rd), false);
}
if (fremote)
- vxlan_xmit_one(skb, vxlan->dev, src_vni,
+ vxlan_xmit_one(skb, vxlan->dev, &vxlan->cfg, src_vni,
rcu_dereference(fremote->rd), false);
else
kfree_skb_reason(skb, SKB_DROP_REASON_NO_TX_TARGET);
diff --git a/drivers/net/vxlan/vxlan_private.h b/drivers/net/vxlan/vxlan_private.h
index b1eec221636088aa1c1674221d5ef0f13698b53f..3d5f21c11ab4ff55ae6c47f8dabdc21331d37290 100644
--- a/drivers/net/vxlan/vxlan_private.h
+++ b/drivers/net/vxlan/vxlan_private.h
@@ -195,9 +195,10 @@ int vxlan_fdb_update(struct vxlan_dev *vxlan,
__u32 ifindex, __u16 ndm_flags, u32 nhid,
bool swdev_notify, struct netlink_ext_ack *extack);
void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev,
+ const struct vxlan_config *cfg,
__be32 default_vni, struct vxlan_rdst *rdst, bool did_rsc);
int vxlan_vni_in_use(struct net *src_net, struct vxlan_dev *vxlan,
- struct vxlan_config *conf, __be32 vni);
+ const struct vxlan_config *conf, __be32 vni);
/* vxlan_vnifilter.c */
int vxlan_vnigroup_init(struct vxlan_dev *vxlan);
@@ -205,7 +206,8 @@ void vxlan_vnigroup_uninit(struct vxlan_dev *vxlan);
int vxlan_vnifilter_init(void);
void vxlan_vnifilter_uninit(void);
-void vxlan_vnifilter_count(struct vxlan_dev *vxlan, __be32 vni,
+void vxlan_vnifilter_count(struct vxlan_dev *vxlan,
+ const struct vxlan_config *cfg, __be32 vni,
struct vxlan_vni_node *vninode,
int type, unsigned int len);
diff --git a/drivers/net/vxlan/vxlan_vnifilter.c b/drivers/net/vxlan/vxlan_vnifilter.c
index 3e76f4e210944ffecd35e872c7b7ce36923117b5..3674dec5a59ae4cdb94319c8789826fbb9f0104c 100644
--- a/drivers/net/vxlan/vxlan_vnifilter.c
+++ b/drivers/net/vxlan/vxlan_vnifilter.c
@@ -171,13 +171,14 @@ static void vxlan_vnifilter_stats_add(struct vxlan_vni_node *vninode,
u64_stats_update_end(&pstats->syncp);
}
-void vxlan_vnifilter_count(struct vxlan_dev *vxlan, __be32 vni,
+void vxlan_vnifilter_count(struct vxlan_dev *vxlan,
+ const struct vxlan_config *cfg, __be32 vni,
struct vxlan_vni_node *vninode,
int type, unsigned int len)
{
struct vxlan_vni_node *vnode;
- if (!(vxlan->cfg.flags & VXLAN_F_VNIFILTER))
+ if (!cfg || !(cfg->flags & VXLAN_F_VNIFILTER))
return;
if (vninode) {
diff --git a/net/openvswitch/vport-vxlan.c b/net/openvswitch/vport-vxlan.c
index c1b37b50d29e15540f22787d0dcdb12da181a5fd..b0a084864a858453715d8e51b3e95cc7b2d9370f 100644
--- a/net/openvswitch/vport-vxlan.c
+++ b/net/openvswitch/vport-vxlan.c
@@ -22,19 +22,20 @@ static struct vport_ops ovs_vxlan_netdev_vport_ops;
static int vxlan_get_options(const struct vport *vport, struct sk_buff *skb)
{
struct vxlan_dev *vxlan = netdev_priv(vport->dev);
- __be16 dst_port = vxlan->cfg.dst_port;
+ const struct vxlan_config *cfg = &vxlan->cfg;
+ __be16 dst_port = cfg->dst_port;
if (nla_put_u16(skb, OVS_TUNNEL_ATTR_DST_PORT, ntohs(dst_port)))
return -EMSGSIZE;
- if (vxlan->cfg.flags & VXLAN_F_GBP) {
+ if (cfg->flags & VXLAN_F_GBP) {
struct nlattr *exts;
exts = nla_nest_start_noflag(skb, OVS_TUNNEL_ATTR_EXTENSION);
if (!exts)
return -EMSGSIZE;
- if (vxlan->cfg.flags & VXLAN_F_GBP &&
+ if (cfg->flags & VXLAN_F_GBP &&
nla_put_flag(skb, OVS_VXLAN_EXT_GBP))
return -EMSGSIZE;
--
2.55.0.795.g602f6c329a-goog
^ permalink raw reply related [flat|nested] 10+ messages in thread
* [PATCH net-next 2/3] vxlan: convert configuration to RCU protection
2026-07-08 16:04 [PATCH net-next 0/3] vxlan: RCU protect vxlan_config and enable RTNL-less fill_info Eric Dumazet
2026-07-08 16:04 ` [PATCH net-next 1/3] vxlan: pass vxlan_config pointer to helper functions Eric Dumazet
@ 2026-07-08 16:04 ` Eric Dumazet
2026-07-09 4:54 ` Pavan Chebbi
2026-07-08 16:04 ` [PATCH net-next 3/3] vxlan: no longer rely on RTNL in vxlan_fill_info() Eric Dumazet
2026-07-10 14:20 ` [PATCH net-next 0/3] vxlan: RCU protect vxlan_config and enable RTNL-less fill_info Paolo Abeni
3 siblings, 1 reply; 10+ messages in thread
From: Eric Dumazet @ 2026-07-08 16:04 UTC (permalink / raw)
To: David S . Miller, Jakub Kicinski, Paolo Abeni
Cc: Simon Horman, Kuniyuki Iwashima, Ido Schimmel, Andrew Lunn,
netdev, eric.dumazet, Eric Dumazet
In order to allow lockless readers in future patches, dynamically allocate
'struct vxlan_config' and convert 'vxlan->cfg' to an RCU protected pointer.
Updating configuration via vxlan_changelink() or __vxlan_dev_create()
now allocates a new struct vxlan_config, initializes it, and uses
rcu_assign_pointer() to publish it, freeing the previous config with
kfree_rcu().
Signed-off-by: Eric Dumazet <edumazet@google.com>
---
.../mellanox/mlxsw/spectrum_nve_vxlan.c | 14 +-
.../mellanox/mlxsw/spectrum_switchdev.c | 57 ++-
drivers/net/vxlan/vxlan_core.c | 336 +++++++++++-------
drivers/net/vxlan/vxlan_mdb.c | 29 +-
drivers/net/vxlan/vxlan_multicast.c | 12 +-
drivers/net/vxlan/vxlan_vnifilter.c | 21 +-
include/net/vxlan.h | 3 +-
net/openvswitch/vport-vxlan.c | 31 +-
8 files changed, 331 insertions(+), 172 deletions(-)
diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_nve_vxlan.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_nve_vxlan.c
index 52c2fe3644d4b9b27f1d589d9f7f597748339782..50cea39323f570e04067f2f98aff4d97e4a409fc 100644
--- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_nve_vxlan.c
+++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_nve_vxlan.c
@@ -59,8 +59,11 @@ static bool mlxsw_sp_nve_vxlan_can_offload(const struct mlxsw_sp_nve *nve,
const struct mlxsw_sp_nve_params *params,
struct netlink_ext_ack *extack)
{
- struct vxlan_dev *vxlan = netdev_priv(params->dev);
- struct vxlan_config *cfg = &vxlan->cfg;
+ const struct vxlan_config *cfg;
+ struct vxlan_dev *vxlan;
+
+ vxlan = netdev_priv(params->dev);
+ cfg = rtnl_dereference(vxlan->cfg);
if (vxlan_addr_multicast(&cfg->remote_ip)) {
NL_SET_ERR_MSG_MOD(extack, "VxLAN: Multicast destination IP is not supported");
@@ -148,8 +151,11 @@ static void mlxsw_sp_nve_vxlan_config(const struct mlxsw_sp_nve *nve,
const struct mlxsw_sp_nve_params *params,
struct mlxsw_sp_nve_config *config)
{
- struct vxlan_dev *vxlan = netdev_priv(params->dev);
- struct vxlan_config *cfg = &vxlan->cfg;
+ const struct vxlan_config *cfg;
+ struct vxlan_dev *vxlan;
+
+ vxlan = netdev_priv(params->dev);
+ cfg = rtnl_dereference(vxlan->cfg);
config->type = MLXSW_SP_NVE_TYPE_VXLAN;
config->ttl = cfg->ttl;
diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c
index fe45e533a4b2efb532b85960009c586a53ade340..f25105ceb3f1bff87ec6addcae70c606bbe048ba 100644
--- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c
+++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c
@@ -2513,15 +2513,17 @@ mlxsw_sp_bridge_vlan_aware_vxlan_join(struct mlxsw_sp_bridge_device *bridge_devi
{
struct mlxsw_sp *mlxsw_sp = mlxsw_sp_lower_get(bridge_device->dev);
struct vxlan_dev *vxlan = netdev_priv(vxlan_dev);
- struct mlxsw_sp_nve_params params = {
- .type = MLXSW_SP_NVE_TYPE_VXLAN,
- .vni = vxlan->cfg.vni,
- .dev = vxlan_dev,
- .ethertype = ethertype,
- };
+ struct mlxsw_sp_nve_params params;
+ const struct vxlan_config *cfg;
struct mlxsw_sp_fid *fid;
int err;
+ cfg = rtnl_dereference(vxlan->cfg);
+ params.type = MLXSW_SP_NVE_TYPE_VXLAN;
+ params.vni = cfg->vni;
+ params.dev = vxlan_dev;
+ params.ethertype = ethertype;
+
/* If the VLAN is 0, we need to find the VLAN that is configured as
* PVID and egress untagged on the bridge port of the VxLAN device.
* It is possible no such VLAN exists
@@ -2704,15 +2706,17 @@ mlxsw_sp_bridge_8021d_vxlan_join(struct mlxsw_sp_bridge_device *bridge_device,
{
struct mlxsw_sp *mlxsw_sp = mlxsw_sp_lower_get(bridge_device->dev);
struct vxlan_dev *vxlan = netdev_priv(vxlan_dev);
- struct mlxsw_sp_nve_params params = {
- .type = MLXSW_SP_NVE_TYPE_VXLAN,
- .vni = vxlan->cfg.vni,
- .dev = vxlan_dev,
- .ethertype = ETH_P_8021Q,
- };
+ struct mlxsw_sp_nve_params params;
+ const struct vxlan_config *cfg;
struct mlxsw_sp_fid *fid;
int err;
+ cfg = rtnl_dereference(vxlan->cfg);
+ params.type = MLXSW_SP_NVE_TYPE_VXLAN;
+ params.vni = cfg->vni;
+ params.dev = vxlan_dev;
+ params.ethertype = ETH_P_8021Q;
+
fid = mlxsw_sp_fid_8021d_get(mlxsw_sp, bridge_device->dev->ifindex);
if (IS_ERR(fid)) {
NL_SET_ERR_MSG_MOD(extack, "Failed to create 802.1D FID");
@@ -2933,10 +2937,13 @@ static void __mlxsw_sp_bridge_vxlan_leave(struct mlxsw_sp *mlxsw_sp,
const struct net_device *vxlan_dev)
{
struct vxlan_dev *vxlan = netdev_priv(vxlan_dev);
+ const struct vxlan_config *cfg;
struct mlxsw_sp_fid *fid;
+ cfg = rtnl_dereference(vxlan->cfg);
+
/* If the VxLAN device is down, then the FID does not have a VNI */
- fid = mlxsw_sp_fid_lookup_by_vni(mlxsw_sp, vxlan->cfg.vni);
+ fid = mlxsw_sp_fid_lookup_by_vni(mlxsw_sp, cfg->vni);
if (!fid)
return;
@@ -3029,11 +3036,13 @@ static void mlxsw_sp_fdb_vxlan_call_notifiers(struct net_device *dev,
struct switchdev_notifier_vxlan_fdb_info info;
struct vxlan_dev *vxlan = netdev_priv(dev);
enum switchdev_notifier_type type;
+ const struct vxlan_config *cfg;
+ cfg = rtnl_dereference(vxlan->cfg);
type = adding ? SWITCHDEV_VXLAN_FDB_ADD_TO_BRIDGE :
SWITCHDEV_VXLAN_FDB_DEL_TO_BRIDGE;
mlxsw_sp_switchdev_addr_vxlan_convert(proto, addr, &info.remote_ip);
- info.remote_port = vxlan->cfg.dst_port;
+ info.remote_port = cfg->dst_port;
info.remote_vni = vni;
info.remote_ifindex = 0;
ether_addr_copy(info.eth_addr, mac);
@@ -3236,8 +3245,10 @@ __mlxsw_sp_fdb_notify_mac_uc_tunnel_process(struct mlxsw_sp *mlxsw_sp,
if (adding && netif_is_vxlan(dev)) {
struct vxlan_dev *vxlan = netdev_priv(dev);
+ const struct vxlan_config *cfg;
- if (!(vxlan->cfg.flags & VXLAN_F_LEARN))
+ cfg = rtnl_dereference(vxlan->cfg);
+ if (!(cfg->flags & VXLAN_F_LEARN))
return -EINVAL;
}
@@ -3722,9 +3733,11 @@ mlxsw_sp_switchdev_vxlan_work_prepare(struct mlxsw_sp_switchdev_event_work *
{
struct vxlan_dev *vxlan = netdev_priv(switchdev_work->dev);
struct switchdev_notifier_vxlan_fdb_info *vxlan_fdb_info;
- struct vxlan_config *cfg = &vxlan->cfg;
+ const struct vxlan_config *cfg;
struct netlink_ext_ack *extack;
+ cfg = rtnl_dereference(vxlan->cfg);
+
extack = switchdev_notifier_info_to_extack(info);
vxlan_fdb_info = container_of(info,
struct switchdev_notifier_vxlan_fdb_info,
@@ -3851,11 +3864,15 @@ mlxsw_sp_switchdev_vxlan_vlan_add(struct mlxsw_sp *mlxsw_sp,
struct netlink_ext_ack *extack)
{
struct vxlan_dev *vxlan = netdev_priv(vxlan_dev);
- __be32 vni = vxlan->cfg.vni;
+ const struct vxlan_config *cfg;
struct mlxsw_sp_fid *fid;
u16 old_vid;
+ __be32 vni;
int err;
+ cfg = rtnl_dereference(vxlan->cfg);
+ vni = cfg->vni;
+
/* We cannot have the same VLAN as PVID and egress untagged on multiple
* VxLAN devices. Note that we get this notification before the VLAN is
* actually added to the bridge's database, so it is not possible for
@@ -3935,12 +3952,16 @@ mlxsw_sp_switchdev_vxlan_vlan_del(struct mlxsw_sp *mlxsw_sp,
const struct net_device *vxlan_dev, u16 vid)
{
struct vxlan_dev *vxlan = netdev_priv(vxlan_dev);
- __be32 vni = vxlan->cfg.vni;
+ const struct vxlan_config *cfg;
struct mlxsw_sp_fid *fid;
+ __be32 vni;
if (!netif_running(vxlan_dev))
return;
+ cfg = rtnl_dereference(vxlan->cfg);
+ vni = cfg->vni;
+
fid = mlxsw_sp_fid_lookup_by_vni(mlxsw_sp, vni);
if (!fid)
return;
diff --git a/drivers/net/vxlan/vxlan_core.c b/drivers/net/vxlan/vxlan_core.c
index eb2608fb7139a18d905d9b1a5140f22a880818d6..96ac74fb1eb064d4e280af8ffa68cff37aacbca7 100644
--- a/drivers/net/vxlan/vxlan_core.c
+++ b/drivers/net/vxlan/vxlan_core.c
@@ -110,20 +110,23 @@ static struct vxlan_dev *vxlan_vs_find_vni(struct vxlan_sock *vs,
vni = 0;
hlist_for_each_entry_rcu(node, vni_head(vs, vni), hlist) {
+ const struct vxlan_config *cfg;
+
if (!node->vxlan)
continue;
+
+ cfg = rcu_dereference(node->vxlan->cfg);
+
vnode = NULL;
- if (node->vxlan->cfg.flags & VXLAN_F_VNIFILTER) {
+ if (cfg->flags & VXLAN_F_VNIFILTER) {
vnode = vxlan_vnifilter_lookup(node->vxlan, vni);
if (!vnode)
continue;
- } else if (node->vxlan->default_dst.remote_vni != vni) {
+ } else if (cfg->vni != vni) {
continue;
}
if (IS_ENABLED(CONFIG_IPV6)) {
- const struct vxlan_config *cfg = &node->vxlan->cfg;
-
if ((cfg->flags & VXLAN_F_IPV6_LINKLOCAL) &&
cfg->remote_ifindex != ifindex)
continue;
@@ -157,6 +160,7 @@ static int vxlan_fdb_info(struct sk_buff *skb, struct vxlan_dev *vxlan,
u32 portid, u32 seq, int type, unsigned int flags,
const struct vxlan_rdst *rdst)
{
+ const struct vxlan_config *cfg = rcu_dereference_rtnl(vxlan->cfg);
unsigned long now = jiffies;
struct nda_cacheinfo ci;
bool send_ip, send_eth;
@@ -216,10 +220,10 @@ static int vxlan_fdb_info(struct sk_buff *skb, struct vxlan_dev *vxlan,
goto nla_put_failure;
if (rdst->remote_port &&
- rdst->remote_port != vxlan->cfg.dst_port &&
+ rdst->remote_port != cfg->dst_port &&
nla_put_be16(skb, NDA_PORT, rdst->remote_port))
goto nla_put_failure;
- if (rdst->remote_vni != vxlan->default_dst.remote_vni &&
+ if (rdst->remote_vni != cfg->vni &&
nla_put_u32(skb, NDA_VNI, be32_to_cpu(rdst->remote_vni)))
goto nla_put_failure;
if (rdst->remote_ifindex &&
@@ -227,7 +231,7 @@ static int vxlan_fdb_info(struct sk_buff *skb, struct vxlan_dev *vxlan,
goto nla_put_failure;
}
- if ((vxlan->cfg.flags & VXLAN_F_COLLECT_METADATA) && fdb->key.vni &&
+ if ((cfg->flags & VXLAN_F_COLLECT_METADATA) && fdb->key.vni &&
nla_put_u32(skb, NDA_SRC_VNI,
be32_to_cpu(fdb->key.vni)))
goto nla_put_failure;
@@ -418,7 +422,7 @@ static struct vxlan_fdb *vxlan_find_mac(struct vxlan_dev *vxlan,
lockdep_assert_held_once(&vxlan->hash_lock);
rcu_read_lock();
- f = vxlan_find_mac_rcu(vxlan, &vxlan->cfg, mac, vni);
+ f = vxlan_find_mac_rcu(vxlan, rcu_dereference(vxlan->cfg), mac, vni);
rcu_read_unlock();
return f;
@@ -459,7 +463,7 @@ int vxlan_fdb_find_uc(struct net_device *dev, const u8 *mac, __be32 vni,
rcu_read_lock();
- f = vxlan_find_mac_rcu(vxlan, &vxlan->cfg, eth_addr, vni);
+ f = vxlan_find_mac_rcu(vxlan, rcu_dereference(vxlan->cfg), eth_addr, vni);
if (f)
rdst = first_remote_rcu(f);
if (!rdst) {
@@ -865,12 +869,13 @@ int vxlan_fdb_create(struct vxlan_dev *vxlan,
u32 nhid, struct vxlan_fdb **fdb,
struct netlink_ext_ack *extack)
{
+ const struct vxlan_config *cfg = rtnl_dereference(vxlan->cfg);
struct vxlan_rdst *rd = NULL;
struct vxlan_fdb *f;
int rc;
- if (vxlan->cfg.addrmax &&
- vxlan->addrcnt >= vxlan->cfg.addrmax)
+ if (cfg->addrmax &&
+ vxlan->addrcnt >= cfg->addrmax)
return -ENOSPC;
netdev_dbg(vxlan->dev, "add %pM -> %pIS\n", mac, ip);
@@ -1150,6 +1155,7 @@ static int vxlan_fdb_parse(struct nlattr *tb[], struct vxlan_dev *vxlan,
__be32 *vni, u32 *ifindex, u32 *nhid,
struct netlink_ext_ack *extack)
{
+ const struct vxlan_config *cfg = rtnl_dereference(vxlan->cfg);
struct net *net = dev_net(vxlan->dev);
int err;
@@ -1166,7 +1172,7 @@ static int vxlan_fdb_parse(struct nlattr *tb[], struct vxlan_dev *vxlan,
return err;
}
} else {
- union vxlan_addr *remote = &vxlan->default_dst.remote_ip;
+ const union vxlan_addr *remote = &cfg->remote_ip;
if (remote->sa.sa_family == AF_INET) {
ip->sin.sin_addr.s_addr = htonl(INADDR_ANY);
@@ -1186,7 +1192,7 @@ static int vxlan_fdb_parse(struct nlattr *tb[], struct vxlan_dev *vxlan,
}
*port = nla_get_be16(tb[NDA_PORT]);
} else {
- *port = vxlan->cfg.dst_port;
+ *port = cfg->dst_port;
}
if (tb[NDA_VNI]) {
@@ -1196,7 +1202,7 @@ static int vxlan_fdb_parse(struct nlattr *tb[], struct vxlan_dev *vxlan,
}
*vni = cpu_to_be32(nla_get_u32(tb[NDA_VNI]));
} else {
- *vni = vxlan->default_dst.remote_vni;
+ *vni = cfg->vni;
}
if (tb[NDA_SRC_VNI]) {
@@ -1206,7 +1212,7 @@ static int vxlan_fdb_parse(struct nlattr *tb[], struct vxlan_dev *vxlan,
}
*src_vni = cpu_to_be32(nla_get_u32(tb[NDA_SRC_VNI]));
} else {
- *src_vni = vxlan->default_dst.remote_vni;
+ *src_vni = cfg->vni;
}
if (tb[NDA_IFINDEX]) {
@@ -1396,18 +1402,23 @@ static int vxlan_fdb_get(struct sk_buff *skb,
struct netlink_ext_ack *extack)
{
struct vxlan_dev *vxlan = netdev_priv(dev);
+ const struct vxlan_config *cfg;
struct vxlan_fdb *f;
__be32 vni;
int err;
+ cfg = rtnl_dereference(vxlan->cfg);
+
if (tb[NDA_VNI])
vni = cpu_to_be32(nla_get_u32(tb[NDA_VNI]));
+ else if (cfg)
+ vni = cfg->vni;
else
- vni = vxlan->default_dst.remote_vni;
+ return -ENODEV;
rcu_read_lock();
- f = vxlan_find_mac_rcu(vxlan, &vxlan->cfg, addr, vni);
+ f = vxlan_find_mac_rcu(vxlan, cfg, addr, vni);
if (!f) {
NL_SET_ERR_MSG(extack, "Fdb entry not found");
err = -ENOENT;
@@ -1510,6 +1521,7 @@ static bool __vxlan_sock_release_prep(struct vxlan_sock *vs)
static void vxlan_sock_release(struct vxlan_dev *vxlan)
{
+ const struct vxlan_config *cfg = rtnl_dereference(vxlan->cfg);
struct vxlan_sock *sock4 = rtnl_dereference(vxlan->vn4_sock);
#if IS_ENABLED(CONFIG_IPV6)
struct vxlan_sock *sock6 = rtnl_dereference(vxlan->vn6_sock);
@@ -1519,7 +1531,7 @@ static void vxlan_sock_release(struct vxlan_dev *vxlan)
RCU_INIT_POINTER(vxlan->vn4_sock, NULL);
- if (vxlan->cfg.flags & VXLAN_F_VNIFILTER)
+ if (cfg && (cfg->flags & VXLAN_F_VNIFILTER))
vxlan_vs_del_vnigrp(vxlan);
else
vxlan_vs_del_dev(vxlan);
@@ -1650,6 +1662,7 @@ static bool vxlan_ecn_decapsulate(struct vxlan_sock *vs, void *oiph,
static int vxlan_rcv(struct sock *sk, struct sk_buff *skb)
{
struct vxlan_vni_node *vninode = NULL;
+ const struct vxlan_config *cfg;
__be16 protocol = htons(ETH_P_TEB);
enum skb_drop_reason reason;
const struct vxlanhdr *vh;
@@ -1691,8 +1704,10 @@ static int vxlan_rcv(struct sock *sk, struct sk_buff *skb)
goto drop;
}
- if (vh->vx_flags & vxlan->cfg.reserved_bits.vx_flags ||
- vh->vx_vni & vxlan->cfg.reserved_bits.vx_vni) {
+ cfg = rcu_dereference(vxlan->cfg);
+
+ if (vh->vx_flags & cfg->reserved_bits.vx_flags ||
+ vh->vx_vni & cfg->reserved_bits.vx_vni) {
/* If the header uses bits besides those enabled by the
* netdevice configuration, treat this as a malformed packet.
* This behavior diverges from VXLAN RFC (RFC7348) which
@@ -1704,12 +1719,12 @@ static int vxlan_rcv(struct sock *sk, struct sk_buff *skb)
reason = SKB_DROP_REASON_VXLAN_INVALID_HDR;
DEV_STATS_INC(vxlan->dev, rx_frame_errors);
DEV_STATS_INC(vxlan->dev, rx_errors);
- vxlan_vnifilter_count(vxlan, &vxlan->cfg, vni, vninode,
+ vxlan_vnifilter_count(vxlan, cfg, vni, vninode,
VXLAN_VNI_STATS_RX_ERRORS, 0);
goto drop;
}
- if (vxlan->cfg.flags & VXLAN_F_GPE) {
+ if (cfg->flags & VXLAN_F_GPE) {
if (!vxlan_parse_gpe_proto(vh, &protocol))
goto drop;
raw_proto = true;
@@ -1721,8 +1736,8 @@ static int vxlan_rcv(struct sock *sk, struct sk_buff *skb)
goto drop;
}
- if (vxlan->cfg.flags & VXLAN_F_REMCSUM_RX) {
- reason = vxlan_remcsum(skb, vxlan->cfg.flags);
+ if (cfg->flags & VXLAN_F_REMCSUM_RX) {
+ reason = vxlan_remcsum(skb, cfg->flags);
if (unlikely(reason))
goto drop;
}
@@ -1747,14 +1762,14 @@ static int vxlan_rcv(struct sock *sk, struct sk_buff *skb)
memset(md, 0, sizeof(*md));
}
- if (vxlan->cfg.flags & VXLAN_F_GBP)
- vxlan_parse_gbp_hdr(skb, vxlan->cfg.flags, md);
+ if (cfg->flags & VXLAN_F_GBP)
+ vxlan_parse_gbp_hdr(skb, cfg->flags, md);
/* Note that GBP and GPE can never be active together. This is
* ensured in vxlan_dev_configure.
*/
if (!raw_proto) {
- reason = vxlan_set_mac(vxlan, &vxlan->cfg, vs, skb, vni);
+ reason = vxlan_set_mac(vxlan, cfg, vs, skb, vni);
if (reason)
goto drop;
} else {
@@ -1775,7 +1790,7 @@ static int vxlan_rcv(struct sock *sk, struct sk_buff *skb)
if (reason) {
DEV_STATS_INC(vxlan->dev, rx_length_errors);
DEV_STATS_INC(vxlan->dev, rx_errors);
- vxlan_vnifilter_count(vxlan, &vxlan->cfg, vni, vninode,
+ vxlan_vnifilter_count(vxlan, cfg, vni, vninode,
VXLAN_VNI_STATS_RX_ERRORS, 0);
goto drop;
}
@@ -1787,7 +1802,7 @@ static int vxlan_rcv(struct sock *sk, struct sk_buff *skb)
reason = SKB_DROP_REASON_IP_TUNNEL_ECN;
DEV_STATS_INC(vxlan->dev, rx_frame_errors);
DEV_STATS_INC(vxlan->dev, rx_errors);
- vxlan_vnifilter_count(vxlan, &vxlan->cfg, vni, vninode,
+ vxlan_vnifilter_count(vxlan, cfg, vni, vninode,
VXLAN_VNI_STATS_RX_ERRORS, 0);
goto drop;
}
@@ -1797,14 +1812,14 @@ static int vxlan_rcv(struct sock *sk, struct sk_buff *skb)
if (unlikely(!(vxlan->dev->flags & IFF_UP))) {
rcu_read_unlock();
dev_dstats_rx_dropped(vxlan->dev);
- vxlan_vnifilter_count(vxlan, &vxlan->cfg, vni, vninode,
+ vxlan_vnifilter_count(vxlan, cfg, vni, vninode,
VXLAN_VNI_STATS_RX_DROPS, 0);
reason = SKB_DROP_REASON_DEV_READY;
goto drop;
}
dev_dstats_rx_add(vxlan->dev, skb->len);
- vxlan_vnifilter_count(vxlan, &vxlan->cfg, vni, vninode, VXLAN_VNI_STATS_RX, skb->len);
+ vxlan_vnifilter_count(vxlan, cfg, vni, vninode, VXLAN_VNI_STATS_RX, skb->len);
gro_cells_receive(&vxlan->gro_cells, skb);
rcu_read_unlock();
@@ -1848,17 +1863,20 @@ static int vxlan_err_lookup(struct sock *sk, struct sk_buff *skb)
static int arp_reduce(struct net_device *dev, struct sk_buff *skb, __be32 vni, u32 flags)
{
struct vxlan_dev *vxlan = netdev_priv(dev);
+ const struct vxlan_config *cfg;
struct arphdr *parp;
u8 *arpptr, *sha;
__be32 sip, tip;
struct neighbour *n;
+ cfg = rcu_dereference(vxlan->cfg);
+
if (dev->flags & IFF_NOARP)
goto out;
if (!pskb_may_pull(skb, arp_hdr_len(dev))) {
dev_dstats_tx_dropped(dev);
- vxlan_vnifilter_count(vxlan, &vxlan->cfg, vni, NULL,
+ vxlan_vnifilter_count(vxlan, cfg, vni, NULL,
VXLAN_VNI_STATS_TX_DROPS, 0);
goto out;
}
@@ -1896,7 +1914,7 @@ static int arp_reduce(struct net_device *dev, struct sk_buff *skb, __be32 vni, u
}
rcu_read_lock();
- f = vxlan_find_mac_tx(vxlan, &vxlan->cfg, n->ha, vni);
+ f = vxlan_find_mac_tx(vxlan, cfg, n->ha, vni);
if (f)
rdst = first_remote_rcu(f);
if (rdst && vxlan_addr_any(&rdst->remote_ip)) {
@@ -1922,7 +1940,7 @@ static int arp_reduce(struct net_device *dev, struct sk_buff *skb, __be32 vni, u
if (netif_rx(reply) == NET_RX_DROP) {
dev_dstats_rx_dropped(dev);
- vxlan_vnifilter_count(vxlan, &vxlan->cfg, vni, NULL,
+ vxlan_vnifilter_count(vxlan, cfg, vni, NULL,
VXLAN_VNI_STATS_RX_DROPS, 0);
}
@@ -2036,6 +2054,7 @@ static struct sk_buff *vxlan_na_create(struct sk_buff *request,
static int neigh_reduce(struct net_device *dev, struct sk_buff *skb, __be32 vni, u32 flags)
{
struct vxlan_dev *vxlan = netdev_priv(dev);
+ const struct vxlan_config *cfg;
const struct in6_addr *daddr;
const struct ipv6hdr *iphdr;
struct neighbour *n;
@@ -2045,6 +2064,8 @@ static int neigh_reduce(struct net_device *dev, struct sk_buff *skb, __be32 vni,
if (unlikely(!ipv6_mod_enabled()))
goto out;
+ cfg = rcu_dereference(vxlan->cfg);
+
iphdr = ipv6_hdr(skb);
daddr = &iphdr->daddr;
msg = (struct nd_msg *)(iphdr + 1);
@@ -2065,7 +2086,7 @@ static int neigh_reduce(struct net_device *dev, struct sk_buff *skb, __be32 vni,
goto out;
}
- f = vxlan_find_mac_tx(vxlan, &vxlan->cfg, n->ha, vni);
+ f = vxlan_find_mac_tx(vxlan, cfg, n->ha, vni);
if (f)
rdst = first_remote_rcu(f);
if (rdst && vxlan_addr_any(&rdst->remote_ip)) {
@@ -2084,7 +2105,7 @@ static int neigh_reduce(struct net_device *dev, struct sk_buff *skb, __be32 vni,
if (netif_rx(reply) == NET_RX_DROP) {
dev_dstats_rx_dropped(dev);
- vxlan_vnifilter_count(vxlan, &vxlan->cfg, vni, NULL,
+ vxlan_vnifilter_count(vxlan, cfg, vni, NULL,
VXLAN_VNI_STATS_RX_DROPS, 0);
}
} else if (flags & VXLAN_F_L3MISS) {
@@ -2749,10 +2770,12 @@ static netdev_tx_t vxlan_xmit(struct sk_buff *skb, struct net_device *dev)
u32 nhid = 0;
u32 flags;
- cfg = &vxlan->cfg;
+ rcu_read_lock();
+ cfg = rcu_dereference(vxlan->cfg);
flags = cfg->flags;
default_vni = cfg->vni;
saddr_family = cfg->saddr.sa.sa_family;
+ rcu_read_unlock();
info = skb_tunnel_info(skb);
@@ -2867,12 +2890,17 @@ static void vxlan_cleanup(struct timer_list *t)
{
struct vxlan_dev *vxlan = timer_container_of(vxlan, t, age_timer);
unsigned long next_timer = jiffies + FDB_AGE_INTERVAL;
+ const struct vxlan_config *cfg;
struct vxlan_fdb *f;
if (!netif_running(vxlan->dev))
return;
rcu_read_lock();
+ cfg = rcu_dereference(vxlan->cfg);
+ if (!cfg)
+ goto out;
+
hlist_for_each_entry_rcu(f, &vxlan->fdb_list, fdb_node) {
unsigned long timeout;
@@ -2882,7 +2910,7 @@ static void vxlan_cleanup(struct timer_list *t)
if (f->flags & NTF_EXT_LEARNED)
continue;
- timeout = READ_ONCE(f->updated) + vxlan->cfg.age_interval * HZ;
+ timeout = READ_ONCE(f->updated) + cfg->age_interval * HZ;
if (time_before_eq(timeout, jiffies)) {
spin_lock(&vxlan->hash_lock);
if (!hlist_unhashed(&f->fdb_node)) {
@@ -2896,6 +2924,7 @@ static void vxlan_cleanup(struct timer_list *t)
next_timer = timeout;
}
}
+out:
rcu_read_unlock();
mod_timer(&vxlan->age_timer, next_timer);
@@ -2926,13 +2955,16 @@ static void vxlan_vs_add_dev(struct vxlan_sock *vs, struct vxlan_dev *vxlan,
static int vxlan_init(struct net_device *dev)
{
struct vxlan_dev *vxlan = netdev_priv(dev);
+ const struct vxlan_config *cfg;
int err;
+ cfg = rtnl_dereference(vxlan->cfg);
+
err = rhashtable_init(&vxlan->fdb_hash_tbl, &vxlan_fdb_rht_params);
if (err)
return err;
- if (vxlan->cfg.flags & VXLAN_F_VNIFILTER) {
+ if (cfg->flags & VXLAN_F_VNIFILTER) {
err = vxlan_vnigroup_init(vxlan);
if (err)
goto err_rhashtable_destroy;
@@ -2952,7 +2984,7 @@ static int vxlan_init(struct net_device *dev)
err_gro_cells_destroy:
gro_cells_destroy(&vxlan->gro_cells);
err_vnigroup_uninit:
- if (vxlan->cfg.flags & VXLAN_F_VNIFILTER)
+ if (cfg->flags & VXLAN_F_VNIFILTER)
vxlan_vnigroup_uninit(vxlan);
err_rhashtable_destroy:
rhashtable_destroy(&vxlan->fdb_hash_tbl);
@@ -2962,10 +2994,13 @@ static int vxlan_init(struct net_device *dev)
static void vxlan_uninit(struct net_device *dev)
{
struct vxlan_dev *vxlan = netdev_priv(dev);
+ const struct vxlan_config *cfg;
+
+ cfg = rtnl_dereference(vxlan->cfg);
vxlan_mdb_fini(vxlan);
- if (vxlan->cfg.flags & VXLAN_F_VNIFILTER)
+ if (cfg && (cfg->flags & VXLAN_F_VNIFILTER))
vxlan_vnigroup_uninit(vxlan);
gro_cells_destroy(&vxlan->gro_cells);
@@ -2977,6 +3012,7 @@ static void vxlan_uninit(struct net_device *dev)
static int vxlan_open(struct net_device *dev)
{
struct vxlan_dev *vxlan = netdev_priv(dev);
+ const struct vxlan_config *cfg;
int ret;
ret = vxlan_sock_add(vxlan);
@@ -2989,7 +3025,8 @@ static int vxlan_open(struct net_device *dev)
return ret;
}
- if (vxlan->cfg.age_interval)
+ cfg = rtnl_dereference(vxlan->cfg);
+ if (cfg && cfg->age_interval)
mod_timer(&vxlan->age_timer, jiffies + FDB_AGE_INTERVAL);
return ret;
@@ -3011,8 +3048,10 @@ struct vxlan_fdb_flush_desc {
static bool vxlan_fdb_is_default_entry(const struct vxlan_fdb *f,
const struct vxlan_dev *vxlan)
{
+ const struct vxlan_config *cfg = rtnl_dereference(vxlan->cfg);
+
return is_zero_ether_addr(f->key.eth_addr) &&
- f->key.vni == vxlan->cfg.vni;
+ f->key.vni == cfg->vni;
}
static bool vxlan_fdb_nhid_matches(const struct vxlan_fdb *f, u32 nhid)
@@ -3229,14 +3268,18 @@ static int vxlan_change_mtu(struct net_device *dev, int new_mtu)
{
struct vxlan_dev *vxlan = netdev_priv(dev);
struct vxlan_rdst *dst = &vxlan->default_dst;
- struct net_device *lowerdev = __dev_get_by_index(vxlan->net,
- dst->remote_ifindex);
+ const struct vxlan_config *cfg;
+ struct net_device *lowerdev;
+
+ cfg = rtnl_dereference(vxlan->cfg);
+
+ lowerdev = __dev_get_by_index(vxlan->net, dst->remote_ifindex);
/* This check is different than dev->max_mtu, because it looks at
* the lowerdev->mtu, rather than the static dev->max_mtu
*/
if (lowerdev) {
- int max_mtu = lowerdev->mtu - vxlan_headroom(vxlan->cfg.flags);
+ int max_mtu = lowerdev->mtu - vxlan_headroom(cfg->flags);
if (new_mtu > max_mtu)
return -EINVAL;
}
@@ -3249,11 +3292,14 @@ static int vxlan_fill_metadata_dst(struct net_device *dev, struct sk_buff *skb)
{
struct vxlan_dev *vxlan = netdev_priv(dev);
struct ip_tunnel_info *info = skb_tunnel_info(skb);
+ const struct vxlan_config *cfg;
__be16 sport, dport;
- sport = udp_flow_src_port(dev_net(dev), skb, vxlan->cfg.port_min,
- vxlan->cfg.port_max, true);
- dport = info->key.tp_dst ? : vxlan->cfg.dst_port;
+ cfg = rcu_dereference(vxlan->cfg);
+
+ sport = udp_flow_src_port(dev_net(dev), skb, cfg->port_min,
+ cfg->port_max, true);
+ dport = info->key.tp_dst ? : cfg->dst_port;
if (ip_tunnel_info_af(info) == AF_INET) {
struct vxlan_sock *sock4 = rcu_dereference(vxlan->vn4_sock);
@@ -3363,6 +3409,14 @@ static void vxlan_offload_rx_ports(struct net_device *dev, bool push)
}
}
+static void vxlan_free_dev(struct net_device *dev)
+{
+ struct vxlan_dev *vxlan = netdev_priv(dev);
+ struct vxlan_config *cfg = rcu_dereference_protected(vxlan->cfg, 1);
+
+ kfree(cfg);
+}
+
/* Initialize the device structure. */
static void vxlan_setup(struct net_device *dev)
{
@@ -3371,6 +3425,8 @@ static void vxlan_setup(struct net_device *dev)
eth_hw_addr_random(dev);
ether_setup(dev);
+ dev->priv_destructor = vxlan_free_dev;
+
dev->needs_free_netdev = true;
SET_NETDEV_DEVTYPE(dev, &vxlan_type);
@@ -3651,21 +3707,22 @@ static struct vxlan_sock *vxlan_socket_create(struct net *net, bool ipv6,
static int __vxlan_sock_add(struct vxlan_dev *vxlan, bool ipv6)
{
- bool metadata = vxlan->cfg.flags & VXLAN_F_COLLECT_METADATA;
+ const struct vxlan_config *cfg = rtnl_dereference(vxlan->cfg);
+ bool metadata = cfg->flags & VXLAN_F_COLLECT_METADATA;
struct vxlan_sock *vs = NULL;
struct vxlan_dev_node *node;
int l3mdev_index = 0;
ASSERT_RTNL();
- if (vxlan->cfg.remote_ifindex)
+ if (cfg->remote_ifindex)
l3mdev_index = l3mdev_master_upper_ifindex_by_index(
- vxlan->net, vxlan->cfg.remote_ifindex);
+ vxlan->net, cfg->remote_ifindex);
- if (!vxlan->cfg.no_share) {
+ if (!cfg->no_share) {
rcu_read_lock();
vs = vxlan_find_sock(vxlan->net, ipv6 ? AF_INET6 : AF_INET,
- vxlan->cfg.dst_port, vxlan->cfg.flags,
+ cfg->dst_port, cfg->flags,
l3mdev_index);
if (vs && !refcount_inc_not_zero(&vs->refcnt)) {
rcu_read_unlock();
@@ -3675,7 +3732,7 @@ static int __vxlan_sock_add(struct vxlan_dev *vxlan, bool ipv6)
}
if (!vs)
vs = vxlan_socket_create(vxlan->net, ipv6,
- vxlan->cfg.dst_port, vxlan->cfg.flags,
+ cfg->dst_port, cfg->flags,
l3mdev_index);
if (IS_ERR(vs))
return PTR_ERR(vs);
@@ -3690,7 +3747,7 @@ static int __vxlan_sock_add(struct vxlan_dev *vxlan, bool ipv6)
node = &vxlan->hlist4;
}
- if (metadata && (vxlan->cfg.flags & VXLAN_F_VNIFILTER))
+ if (metadata && (cfg->flags & VXLAN_F_VNIFILTER))
vxlan_vs_add_vnigrp(vxlan, vs, ipv6);
else
vxlan_vs_add_dev(vs, vxlan, node);
@@ -3700,11 +3757,14 @@ static int __vxlan_sock_add(struct vxlan_dev *vxlan, bool ipv6)
static int vxlan_sock_add(struct vxlan_dev *vxlan)
{
- bool metadata = vxlan->cfg.flags & VXLAN_F_COLLECT_METADATA;
- bool ipv6 = vxlan->cfg.flags & VXLAN_F_IPV6 || metadata;
- bool ipv4 = !ipv6 || metadata;
+ const struct vxlan_config *cfg = rtnl_dereference(vxlan->cfg);
+ bool metadata, ipv6, ipv4;
int ret = 0;
+ metadata = cfg->flags & VXLAN_F_COLLECT_METADATA;
+ ipv6 = (cfg->flags & VXLAN_F_IPV6) || metadata;
+ ipv4 = !ipv6 || metadata;
+
RCU_INIT_POINTER(vxlan->vn4_sock, NULL);
#if IS_ENABLED(CONFIG_IPV6)
RCU_INIT_POINTER(vxlan->vn6_sock, NULL);
@@ -3728,22 +3788,27 @@ int vxlan_vni_in_use(struct net *src_net, struct vxlan_dev *vxlan,
struct vxlan_dev *tmp;
list_for_each_entry(tmp, &vn->vxlan_list, next) {
+ const struct vxlan_config *tmp_cfg;
+
if (tmp == vxlan)
continue;
- if (tmp->cfg.flags & VXLAN_F_VNIFILTER) {
+
+ tmp_cfg = rtnl_dereference(tmp->cfg);
+
+ if (tmp_cfg->flags & VXLAN_F_VNIFILTER) {
if (!vxlan_vnifilter_lookup(tmp, vni))
continue;
- } else if (tmp->cfg.vni != vni) {
+ } else if (tmp_cfg->vni != vni) {
continue;
}
- if (tmp->cfg.dst_port != conf->dst_port)
+ if (tmp_cfg->dst_port != conf->dst_port)
continue;
- if ((tmp->cfg.flags & (VXLAN_F_RCV_FLAGS | VXLAN_F_IPV6)) !=
+ if ((tmp_cfg->flags & (VXLAN_F_RCV_FLAGS | VXLAN_F_IPV6)) !=
(conf->flags & (VXLAN_F_RCV_FLAGS | VXLAN_F_IPV6)))
continue;
if ((conf->flags & VXLAN_F_IPV6_LINKLOCAL) &&
- tmp->cfg.remote_ifindex != conf->remote_ifindex)
+ tmp_cfg->remote_ifindex != conf->remote_ifindex)
continue;
return -EEXIST;
@@ -3905,7 +3970,7 @@ static int vxlan_config_validate(struct net *src_net, struct vxlan_config *conf,
}
static void vxlan_config_apply(struct net_device *dev,
- struct vxlan_config *conf,
+ struct vxlan_config *new_cfg,
struct net_device *lowerdev,
struct net *src_net,
bool changelink)
@@ -3913,8 +3978,9 @@ static void vxlan_config_apply(struct net_device *dev,
struct vxlan_dev *vxlan = netdev_priv(dev);
struct vxlan_rdst *dst = &vxlan->default_dst;
unsigned short needed_headroom = ETH_HLEN;
+ struct vxlan_config *old_cfg;
int max_mtu = ETH_MAX_MTU;
- u32 flags = conf->flags;
+ u32 flags = new_cfg->flags;
if (!changelink) {
if (flags & VXLAN_F_GPE)
@@ -3922,18 +3988,18 @@ static void vxlan_config_apply(struct net_device *dev,
else
vxlan_ether_setup(dev);
- if (conf->mtu)
- dev->mtu = conf->mtu;
+ if (new_cfg->mtu)
+ dev->mtu = new_cfg->mtu;
vxlan->net = src_net;
}
- dst->remote_vni = conf->vni;
+ dst->remote_vni = new_cfg->vni;
- memcpy(&dst->remote_ip, &conf->remote_ip, sizeof(conf->remote_ip));
+ memcpy(&dst->remote_ip, &new_cfg->remote_ip, sizeof(new_cfg->remote_ip));
if (lowerdev) {
- dst->remote_ifindex = conf->remote_ifindex;
+ dst->remote_ifindex = new_cfg->remote_ifindex;
netif_inherit_tso_max(dev, lowerdev);
@@ -3946,7 +4012,7 @@ static void vxlan_config_apply(struct net_device *dev,
if (max_mtu < ETH_MIN_MTU)
max_mtu = ETH_MIN_MTU;
- if (!changelink && !conf->mtu)
+ if (!changelink && !new_cfg->mtu)
dev->mtu = max_mtu;
}
@@ -3958,7 +4024,10 @@ static void vxlan_config_apply(struct net_device *dev,
needed_headroom += vxlan_headroom(flags);
dev->needed_headroom = needed_headroom;
- memcpy(&vxlan->cfg, conf, sizeof(*conf));
+ old_cfg = rtnl_dereference(vxlan->cfg);
+ rcu_assign_pointer(vxlan->cfg, new_cfg);
+ if (old_cfg)
+ kfree_rcu(old_cfg, rcu);
}
static int vxlan_dev_configure(struct net *src_net, struct net_device *dev,
@@ -3967,13 +4036,18 @@ static int vxlan_dev_configure(struct net *src_net, struct net_device *dev,
{
struct vxlan_dev *vxlan = netdev_priv(dev);
struct net_device *lowerdev;
+ struct vxlan_config *new_cfg;
int ret;
ret = vxlan_config_validate(src_net, conf, &lowerdev, vxlan, extack);
if (ret)
return ret;
- vxlan_config_apply(dev, conf, lowerdev, src_net, false);
+ new_cfg = kmemdup(conf, sizeof(*conf), GFP_KERNEL);
+ if (!new_cfg)
+ return -ENOMEM;
+
+ vxlan_config_apply(dev, new_cfg, lowerdev, src_net, false);
return 0;
}
@@ -3985,6 +4059,7 @@ static int __vxlan_dev_create(struct net *net, struct net_device *dev,
struct vxlan_net *vn = net_generic(net, vxlan_net_id);
struct vxlan_dev *vxlan = netdev_priv(dev);
struct net_device *remote_dev = NULL;
+ const struct vxlan_config *cfg;
struct vxlan_rdst *dst;
int err;
@@ -3993,6 +4068,8 @@ static int __vxlan_dev_create(struct net *net, struct net_device *dev,
if (err)
return err;
+ cfg = rtnl_dereference(vxlan->cfg);
+
dev->ethtool_ops = &vxlan_ethtool_ops;
err = register_netdevice(dev);
@@ -4024,7 +4101,7 @@ static int __vxlan_dev_create(struct net *net, struct net_device *dev,
&dst->remote_ip,
NUD_REACHABLE | NUD_PERMANENT,
NLM_F_EXCL | NLM_F_CREATE,
- vxlan->cfg.dst_port,
+ cfg->dst_port,
dst->remote_vni,
dst->remote_vni,
dst->remote_ifindex,
@@ -4088,8 +4165,12 @@ static int vxlan_nl2conf(struct nlattr *tb[], struct nlattr *data[],
memset(conf, 0, sizeof(*conf));
/* if changelink operation, start with old existing cfg */
- if (changelink)
- memcpy(conf, &vxlan->cfg, sizeof(*conf));
+ if (changelink) {
+ const struct vxlan_config *cfg = rtnl_dereference(vxlan->cfg);
+
+ if (cfg)
+ memcpy(conf, cfg, sizeof(*conf));
+ }
if (data[IFLA_VXLAN_ID]) {
__be32 vni = cpu_to_be32(nla_get_u32(data[IFLA_VXLAN_ID]));
@@ -4436,9 +4517,11 @@ static int vxlan_changelink(struct net_device *dev, struct nlattr *tb[],
struct netlink_ext_ack *extack)
{
struct vxlan_dev *vxlan = netdev_priv(dev);
+ const struct vxlan_config *cfg = rtnl_dereference(vxlan->cfg);
bool rem_ip_changed, change_igmp;
struct net_device *lowerdev;
struct vxlan_config conf;
+ struct vxlan_config *new_cfg;
struct vxlan_rdst *dst;
int err;
@@ -4452,13 +4535,19 @@ static int vxlan_changelink(struct net_device *dev, struct nlattr *tb[],
if (err)
return err;
+ new_cfg = kmemdup(&conf, sizeof(conf), GFP_KERNEL);
+ if (!new_cfg)
+ return -ENOMEM;
+
if (dst->remote_dev == lowerdev)
lowerdev = NULL;
err = netdev_adjacent_change_prepare(dst->remote_dev, lowerdev, dev,
extack);
- if (err)
+ if (err) {
+ kfree(new_cfg);
return err;
+ }
rem_ip_changed = !vxlan_addr_equal(&conf.remote_ip, &dst->remote_ip);
change_igmp = vxlan->dev->flags & IFF_UP &&
@@ -4473,7 +4562,7 @@ static int vxlan_changelink(struct net_device *dev, struct nlattr *tb[],
&conf.remote_ip,
NUD_REACHABLE | NUD_PERMANENT,
NLM_F_APPEND | NLM_F_CREATE,
- vxlan->cfg.dst_port,
+ cfg->dst_port,
conf.vni, conf.vni,
conf.remote_ifindex,
NTF_SELF, 0, true, extack);
@@ -4481,13 +4570,14 @@ static int vxlan_changelink(struct net_device *dev, struct nlattr *tb[],
spin_unlock_bh(&vxlan->hash_lock);
netdev_adjacent_change_abort(dst->remote_dev,
lowerdev, dev);
+ kfree(new_cfg);
return err;
}
}
if (!vxlan_addr_any(&dst->remote_ip))
__vxlan_fdb_delete(vxlan, all_zeros_mac,
dst->remote_ip,
- vxlan->cfg.dst_port,
+ cfg->dst_port,
dst->remote_vni,
dst->remote_vni,
dst->remote_ifindex,
@@ -4497,12 +4587,13 @@ static int vxlan_changelink(struct net_device *dev, struct nlattr *tb[],
/* If vni filtering device, also update fdb entries of
* all vnis that were using default remote ip
*/
- if (vxlan->cfg.flags & VXLAN_F_VNIFILTER) {
+ if (cfg->flags & VXLAN_F_VNIFILTER) {
err = vxlan_vnilist_update_group(vxlan, &dst->remote_ip,
&conf.remote_ip, extack);
if (err) {
netdev_adjacent_change_abort(dst->remote_dev,
lowerdev, dev);
+ kfree(new_cfg);
return err;
}
}
@@ -4511,13 +4602,13 @@ static int vxlan_changelink(struct net_device *dev, struct nlattr *tb[],
if (change_igmp && vxlan_addr_multicast(&dst->remote_ip))
err = vxlan_multicast_leave(vxlan);
- if (conf.age_interval != vxlan->cfg.age_interval)
+ if (conf.age_interval != cfg->age_interval)
mod_timer(&vxlan->age_timer, jiffies);
netdev_adjacent_change_commit(dst->remote_dev, lowerdev, dev);
if (lowerdev && lowerdev != dst->remote_dev)
dst->remote_dev = lowerdev;
- vxlan_config_apply(dev, &conf, lowerdev, vxlan->net, true);
+ vxlan_config_apply(dev, new_cfg, lowerdev, vxlan->net, true);
if (!err && change_igmp &&
vxlan_addr_multicast(&dst->remote_ip))
@@ -4580,11 +4671,16 @@ static size_t vxlan_get_size(const struct net_device *dev)
static int vxlan_fill_info(struct sk_buff *skb, const struct net_device *dev)
{
const struct vxlan_dev *vxlan = netdev_priv(dev);
- const struct vxlan_rdst *dst = &vxlan->default_dst;
- struct ifla_vxlan_port_range ports = {
- .low = htons(vxlan->cfg.port_min),
- .high = htons(vxlan->cfg.port_max),
- };
+ struct ifla_vxlan_port_range ports;
+ const struct vxlan_config *cfg;
+ const struct vxlan_rdst *dst;
+
+ dst = &vxlan->default_dst;
+
+ cfg = rtnl_dereference(vxlan->cfg);
+
+ ports.low = htons(cfg->port_min);
+ ports.high = htons(cfg->port_max);
if (nla_put_u32(skb, IFLA_VXLAN_ID, be32_to_cpu(dst->remote_vni)))
goto nla_put_failure;
@@ -4606,79 +4702,79 @@ static int vxlan_fill_info(struct sk_buff *skb, const struct net_device *dev)
if (dst->remote_ifindex && nla_put_u32(skb, IFLA_VXLAN_LINK, dst->remote_ifindex))
goto nla_put_failure;
- if (!vxlan_addr_any(&vxlan->cfg.saddr)) {
- if (vxlan->cfg.saddr.sa.sa_family == AF_INET) {
+ if (!vxlan_addr_any(&cfg->saddr)) {
+ if (cfg->saddr.sa.sa_family == AF_INET) {
if (nla_put_in_addr(skb, IFLA_VXLAN_LOCAL,
- vxlan->cfg.saddr.sin.sin_addr.s_addr))
+ cfg->saddr.sin.sin_addr.s_addr))
goto nla_put_failure;
#if IS_ENABLED(CONFIG_IPV6)
} else {
if (nla_put_in6_addr(skb, IFLA_VXLAN_LOCAL6,
- &vxlan->cfg.saddr.sin6.sin6_addr))
+ &cfg->saddr.sin6.sin6_addr))
goto nla_put_failure;
#endif
}
}
- if (nla_put_u8(skb, IFLA_VXLAN_TTL, vxlan->cfg.ttl) ||
+ if (nla_put_u8(skb, IFLA_VXLAN_TTL, cfg->ttl) ||
nla_put_u8(skb, IFLA_VXLAN_TTL_INHERIT,
- !!(vxlan->cfg.flags & VXLAN_F_TTL_INHERIT)) ||
- nla_put_u8(skb, IFLA_VXLAN_TOS, vxlan->cfg.tos) ||
- nla_put_u8(skb, IFLA_VXLAN_DF, vxlan->cfg.df) ||
- nla_put_be32(skb, IFLA_VXLAN_LABEL, vxlan->cfg.label) ||
- nla_put_u32(skb, IFLA_VXLAN_LABEL_POLICY, vxlan->cfg.label_policy) ||
+ !!(cfg->flags & VXLAN_F_TTL_INHERIT)) ||
+ nla_put_u8(skb, IFLA_VXLAN_TOS, cfg->tos) ||
+ nla_put_u8(skb, IFLA_VXLAN_DF, cfg->df) ||
+ nla_put_be32(skb, IFLA_VXLAN_LABEL, cfg->label) ||
+ nla_put_u32(skb, IFLA_VXLAN_LABEL_POLICY, cfg->label_policy) ||
nla_put_u8(skb, IFLA_VXLAN_LEARNING,
- !!(vxlan->cfg.flags & VXLAN_F_LEARN)) ||
+ !!(cfg->flags & VXLAN_F_LEARN)) ||
nla_put_u8(skb, IFLA_VXLAN_PROXY,
- !!(vxlan->cfg.flags & VXLAN_F_PROXY)) ||
+ !!(cfg->flags & VXLAN_F_PROXY)) ||
nla_put_u8(skb, IFLA_VXLAN_RSC,
- !!(vxlan->cfg.flags & VXLAN_F_RSC)) ||
+ !!(cfg->flags & VXLAN_F_RSC)) ||
nla_put_u8(skb, IFLA_VXLAN_L2MISS,
- !!(vxlan->cfg.flags & VXLAN_F_L2MISS)) ||
+ !!(cfg->flags & VXLAN_F_L2MISS)) ||
nla_put_u8(skb, IFLA_VXLAN_L3MISS,
- !!(vxlan->cfg.flags & VXLAN_F_L3MISS)) ||
+ !!(cfg->flags & VXLAN_F_L3MISS)) ||
nla_put_u8(skb, IFLA_VXLAN_COLLECT_METADATA,
- !!(vxlan->cfg.flags & VXLAN_F_COLLECT_METADATA)) ||
- nla_put_u32(skb, IFLA_VXLAN_AGEING, vxlan->cfg.age_interval) ||
- nla_put_u32(skb, IFLA_VXLAN_LIMIT, vxlan->cfg.addrmax) ||
- nla_put_be16(skb, IFLA_VXLAN_PORT, vxlan->cfg.dst_port) ||
+ !!(cfg->flags & VXLAN_F_COLLECT_METADATA)) ||
+ nla_put_u32(skb, IFLA_VXLAN_AGEING, cfg->age_interval) ||
+ nla_put_u32(skb, IFLA_VXLAN_LIMIT, cfg->addrmax) ||
+ nla_put_be16(skb, IFLA_VXLAN_PORT, cfg->dst_port) ||
nla_put_u8(skb, IFLA_VXLAN_UDP_CSUM,
- !(vxlan->cfg.flags & VXLAN_F_UDP_ZERO_CSUM_TX)) ||
+ !(cfg->flags & VXLAN_F_UDP_ZERO_CSUM_TX)) ||
nla_put_u8(skb, IFLA_VXLAN_UDP_ZERO_CSUM6_TX,
- !!(vxlan->cfg.flags & VXLAN_F_UDP_ZERO_CSUM6_TX)) ||
+ !!(cfg->flags & VXLAN_F_UDP_ZERO_CSUM6_TX)) ||
nla_put_u8(skb, IFLA_VXLAN_UDP_ZERO_CSUM6_RX,
- !!(vxlan->cfg.flags & VXLAN_F_UDP_ZERO_CSUM6_RX)) ||
+ !!(cfg->flags & VXLAN_F_UDP_ZERO_CSUM6_RX)) ||
nla_put_u8(skb, IFLA_VXLAN_REMCSUM_TX,
- !!(vxlan->cfg.flags & VXLAN_F_REMCSUM_TX)) ||
+ !!(cfg->flags & VXLAN_F_REMCSUM_TX)) ||
nla_put_u8(skb, IFLA_VXLAN_REMCSUM_RX,
- !!(vxlan->cfg.flags & VXLAN_F_REMCSUM_RX)) ||
+ !!(cfg->flags & VXLAN_F_REMCSUM_RX)) ||
nla_put_u8(skb, IFLA_VXLAN_LOCALBYPASS,
- !!(vxlan->cfg.flags & VXLAN_F_LOCALBYPASS)))
+ !!(cfg->flags & VXLAN_F_LOCALBYPASS)))
goto nla_put_failure;
if (nla_put(skb, IFLA_VXLAN_PORT_RANGE, sizeof(ports), &ports))
goto nla_put_failure;
- if (vxlan->cfg.flags & VXLAN_F_GBP &&
+ if (cfg->flags & VXLAN_F_GBP &&
nla_put_flag(skb, IFLA_VXLAN_GBP))
goto nla_put_failure;
- if (vxlan->cfg.flags & VXLAN_F_GPE &&
+ if (cfg->flags & VXLAN_F_GPE &&
nla_put_flag(skb, IFLA_VXLAN_GPE))
goto nla_put_failure;
- if (vxlan->cfg.flags & VXLAN_F_REMCSUM_NOPARTIAL &&
+ if (cfg->flags & VXLAN_F_REMCSUM_NOPARTIAL &&
nla_put_flag(skb, IFLA_VXLAN_REMCSUM_NOPARTIAL))
goto nla_put_failure;
- if (vxlan->cfg.flags & VXLAN_F_VNIFILTER &&
+ if (cfg->flags & VXLAN_F_VNIFILTER &&
nla_put_u8(skb, IFLA_VXLAN_VNIFILTER,
- !!(vxlan->cfg.flags & VXLAN_F_VNIFILTER)))
+ !!(cfg->flags & VXLAN_F_VNIFILTER)))
goto nla_put_failure;
if (nla_put(skb, IFLA_VXLAN_RESERVED_BITS,
- sizeof(vxlan->cfg.reserved_bits),
- &vxlan->cfg.reserved_bits))
+ sizeof(cfg->reserved_bits),
+ &cfg->reserved_bits))
goto nla_put_failure;
return 0;
diff --git a/drivers/net/vxlan/vxlan_mdb.c b/drivers/net/vxlan/vxlan_mdb.c
index c92217906d5ee90c92f3da50ca9a276793ed9aca..eb15bb052a6a874a61c9a3306e1132cca3a98fdf 100644
--- a/drivers/net/vxlan/vxlan_mdb.c
+++ b/drivers/net/vxlan/vxlan_mdb.c
@@ -164,7 +164,7 @@ static int vxlan_mdb_entry_info_fill(const struct vxlan_dev *vxlan,
const struct vxlan_mdb_entry *mdb_entry,
const struct vxlan_mdb_remote *remote)
{
- const struct vxlan_config *cfg = &vxlan->cfg;
+ const struct vxlan_config *cfg = rtnl_dereference(vxlan->cfg);
struct vxlan_rdst *rd = rtnl_dereference(remote->rd);
struct br_mdb_entry e;
struct nlattr *nest;
@@ -605,7 +605,9 @@ static int vxlan_mdb_config_init(struct vxlan_mdb_config *cfg,
{
struct br_mdb_entry *entry = nla_data(tb[MDBA_SET_ENTRY]);
struct vxlan_dev *vxlan = netdev_priv(dev);
- const struct vxlan_config *vcfg = &vxlan->cfg;
+ const struct vxlan_config *vcfg;
+
+ vcfg = rtnl_dereference(vxlan->cfg);
memset(cfg, 0, sizeof(*cfg));
cfg->vxlan = vxlan;
@@ -940,12 +942,12 @@ vxlan_mdb_nlmsg_remote_size(const struct vxlan_dev *vxlan,
const struct vxlan_mdb_entry *mdb_entry,
const struct vxlan_mdb_remote *remote)
{
- const struct vxlan_config *cfg = &vxlan->cfg;
+ const struct vxlan_config *cfg = rtnl_dereference(vxlan->cfg);
const struct vxlan_mdb_entry_key *group = &mdb_entry->key;
struct vxlan_rdst *rd = rtnl_dereference(remote->rd);
size_t nlmsg_size;
- /* MDBA_MDB_ENTRY_INFO */
+ /* MDBA_MDB_ENTRY_INFO */
nlmsg_size = nla_total_size(sizeof(struct br_mdb_entry)) +
/* MDBA_MDB_EATTR_TIMER */
nla_total_size(sizeof(u32));
@@ -1205,7 +1207,7 @@ vxlan_mdb_entry_get(struct vxlan_dev *vxlan,
goto err_free_entry;
if (hlist_is_singular_node(&mdb_entry->mdb_node, &vxlan->mdb_list))
- vxlan->cfg.flags |= VXLAN_F_MDB;
+ rtnl_dereference(vxlan->cfg)->flags |= VXLAN_F_MDB;
return mdb_entry;
@@ -1222,7 +1224,7 @@ static void vxlan_mdb_entry_put(struct vxlan_dev *vxlan,
return;
if (hlist_is_singular_node(&mdb_entry->mdb_node, &vxlan->mdb_list))
- vxlan->cfg.flags &= ~VXLAN_F_MDB;
+ rtnl_dereference(vxlan->cfg)->flags &= ~VXLAN_F_MDB;
rhashtable_remove_fast(&vxlan->mdb_tbl, &mdb_entry->rhnode,
vxlan_mdb_rht_params);
@@ -1610,7 +1612,7 @@ struct vxlan_mdb_entry *vxlan_mdb_entry_skb_get(struct vxlan_dev *vxlan,
struct sk_buff *skb,
__be32 src_vni)
{
- const struct vxlan_config *cfg = &vxlan->cfg;
+ const struct vxlan_config *cfg;
struct vxlan_mdb_entry *mdb_entry;
struct vxlan_mdb_entry_key group;
@@ -1618,6 +1620,10 @@ struct vxlan_mdb_entry *vxlan_mdb_entry_skb_get(struct vxlan_dev *vxlan,
is_broadcast_ether_addr(eth_hdr(skb)->h_dest))
return NULL;
+ cfg = rcu_dereference(vxlan->cfg);
+ if (!cfg)
+ return NULL;
+
/* When not in collect metadata mode, 'src_vni' is zero, but MDB
* entries are stored with the VNI of the VXLAN device.
*/
@@ -1691,8 +1697,11 @@ netdev_tx_t vxlan_mdb_xmit(struct vxlan_dev *vxlan,
struct sk_buff *skb)
{
struct vxlan_mdb_remote *remote, *fremote = NULL;
+ const struct vxlan_config *cfg;
__be32 src_vni = mdb_entry->key.vni;
+ cfg = rcu_dereference(vxlan->cfg);
+
list_for_each_entry_rcu(remote, &mdb_entry->remotes, list) {
struct sk_buff *skb1;
@@ -1708,12 +1717,12 @@ netdev_tx_t vxlan_mdb_xmit(struct vxlan_dev *vxlan,
skb1 = skb_clone(skb, GFP_ATOMIC);
if (skb1)
- vxlan_xmit_one(skb1, vxlan->dev, &vxlan->cfg, src_vni,
+ vxlan_xmit_one(skb1, vxlan->dev, cfg, src_vni,
rcu_dereference(remote->rd), false);
}
if (fremote)
- vxlan_xmit_one(skb, vxlan->dev, &vxlan->cfg, src_vni,
+ vxlan_xmit_one(skb, vxlan->dev, cfg, src_vni,
rcu_dereference(fremote->rd), false);
else
kfree_skb_reason(skb, SKB_DROP_REASON_NO_TX_TARGET);
@@ -1744,7 +1753,7 @@ void vxlan_mdb_fini(struct vxlan_dev *vxlan)
struct vxlan_mdb_flush_desc desc = {};
vxlan_mdb_flush(vxlan, &desc);
- WARN_ON_ONCE(vxlan->cfg.flags & VXLAN_F_MDB);
+ WARN_ON_ONCE(rtnl_dereference(vxlan->cfg)->flags & VXLAN_F_MDB);
rhashtable_free_and_destroy(&vxlan->mdb_tbl, vxlan_mdb_check_empty,
NULL);
}
diff --git a/drivers/net/vxlan/vxlan_multicast.c b/drivers/net/vxlan/vxlan_multicast.c
index 3b75b48dc726df40cebb233095a8a046ee274c30..e2cf10da274f1b608d8bb5020d2b87ebfedeff46 100644
--- a/drivers/net/vxlan/vxlan_multicast.c
+++ b/drivers/net/vxlan/vxlan_multicast.c
@@ -147,6 +147,8 @@ bool vxlan_group_used(struct vxlan_net *vn, struct vxlan_dev *dev,
#endif
list_for_each_entry(vxlan, &vn->vxlan_list, next) {
+ const struct vxlan_config *cfg;
+
if (!netif_running(vxlan->dev) || vxlan == dev)
continue;
@@ -158,7 +160,9 @@ bool vxlan_group_used(struct vxlan_net *vn, struct vxlan_dev *dev,
rtnl_dereference(vxlan->vn6_sock) != sock6)
continue;
#endif
- if (vxlan->cfg.flags & VXLAN_F_VNIFILTER) {
+ cfg = rtnl_dereference(vxlan->cfg);
+
+ if (cfg->flags & VXLAN_F_VNIFILTER) {
if (!vxlan_group_used_by_vnifilter(vxlan, ip, ifindex))
continue;
} else {
@@ -233,6 +237,7 @@ static int vxlan_multicast_leave_vnigrp(struct vxlan_dev *vxlan)
int vxlan_multicast_join(struct vxlan_dev *vxlan)
{
+ const struct vxlan_config *cfg = rtnl_dereference(vxlan->cfg);
int ret = 0;
if (vxlan_addr_multicast(&vxlan->default_dst.remote_ip)) {
@@ -244,7 +249,7 @@ int vxlan_multicast_join(struct vxlan_dev *vxlan)
return ret;
}
- if (vxlan->cfg.flags & VXLAN_F_VNIFILTER)
+ if (cfg->flags & VXLAN_F_VNIFILTER)
return vxlan_multicast_join_vnigrp(vxlan);
return 0;
@@ -252,6 +257,7 @@ int vxlan_multicast_join(struct vxlan_dev *vxlan)
int vxlan_multicast_leave(struct vxlan_dev *vxlan)
{
+ const struct vxlan_config *cfg = rtnl_dereference(vxlan->cfg);
struct vxlan_net *vn = net_generic(vxlan->net, vxlan_net_id);
int ret = 0;
@@ -263,7 +269,7 @@ int vxlan_multicast_leave(struct vxlan_dev *vxlan)
return ret;
}
- if (vxlan->cfg.flags & VXLAN_F_VNIFILTER)
+ if (cfg->flags & VXLAN_F_VNIFILTER)
return vxlan_multicast_leave_vnigrp(vxlan);
return 0;
diff --git a/drivers/net/vxlan/vxlan_vnifilter.c b/drivers/net/vxlan/vxlan_vnifilter.c
index 3674dec5a59ae4cdb94319c8789826fbb9f0104c..f3dda671b4e45677944fd1f2db2afc453061e3a5 100644
--- a/drivers/net/vxlan/vxlan_vnifilter.c
+++ b/drivers/net/vxlan/vxlan_vnifilter.c
@@ -178,7 +178,7 @@ void vxlan_vnifilter_count(struct vxlan_dev *vxlan,
{
struct vxlan_vni_node *vnode;
- if (!cfg || !(cfg->flags & VXLAN_F_VNIFILTER))
+ if (!(cfg->flags & VXLAN_F_VNIFILTER))
return;
if (vninode) {
@@ -337,13 +337,15 @@ static int vxlan_vnifilter_dump_dev(const struct net_device *dev,
struct vxlan_vni_node *tmp, *v, *vbegin = NULL, *vend = NULL;
struct vxlan_dev *vxlan = netdev_priv(dev);
struct tunnel_msg *new_tmsg, *tmsg;
+ const struct vxlan_config *cfg;
int idx = 0, s_idx = cb->args[1];
struct vxlan_vni_group *vg;
struct nlmsghdr *nlh;
bool dump_stats;
int err = 0;
- if (!(vxlan->cfg.flags & VXLAN_F_VNIFILTER))
+ cfg = rcu_dereference(vxlan->cfg);
+ if (!(cfg->flags & VXLAN_F_VNIFILTER))
return -EINVAL;
/* RCU needed because of the vni locking rules (rcu || rtnl) */
@@ -478,6 +480,7 @@ static int vxlan_update_default_fdb_entry(struct vxlan_dev *vxlan, __be32 vni,
union vxlan_addr *remote_ip,
struct netlink_ext_ack *extack)
{
+ const struct vxlan_config *cfg = rtnl_dereference(vxlan->cfg);
struct vxlan_rdst *dst = &vxlan->default_dst;
int err = 0;
@@ -487,7 +490,7 @@ static int vxlan_update_default_fdb_entry(struct vxlan_dev *vxlan, __be32 vni,
remote_ip,
NUD_REACHABLE | NUD_PERMANENT,
NLM_F_APPEND | NLM_F_CREATE,
- vxlan->cfg.dst_port,
+ cfg->dst_port,
vni,
vni,
dst->remote_ifindex,
@@ -501,7 +504,7 @@ static int vxlan_update_default_fdb_entry(struct vxlan_dev *vxlan, __be32 vni,
if (old_remote_ip && !vxlan_addr_any(old_remote_ip)) {
__vxlan_fdb_delete(vxlan, all_zeros_mac,
*old_remote_ip,
- vxlan->cfg.dst_port,
+ cfg->dst_port,
vni, vni,
dst->remote_ifindex,
true);
@@ -615,6 +618,7 @@ static void vxlan_vni_delete_group(struct vxlan_dev *vxlan,
struct vxlan_vni_node *vninode)
{
struct vxlan_net *vn = net_generic(vxlan->net, vxlan_net_id);
+ const struct vxlan_config *cfg = rtnl_dereference(vxlan->cfg);
struct vxlan_rdst *dst = &vxlan->default_dst;
/* if per vni remote_ip not present, delete the
@@ -626,7 +630,7 @@ static void vxlan_vni_delete_group(struct vxlan_dev *vxlan,
__vxlan_fdb_delete(vxlan, all_zeros_mac,
(vxlan_addr_any(&vninode->remote_ip) ?
dst->remote_ip : vninode->remote_ip),
- vxlan->cfg.dst_port,
+ cfg->dst_port,
vninode->vni, vninode->vni,
dst->remote_ifindex,
true);
@@ -726,6 +730,7 @@ static int vxlan_vni_add(struct vxlan_dev *vxlan,
u32 vni, union vxlan_addr *group,
struct netlink_ext_ack *extack)
{
+ const struct vxlan_config *cfg = rtnl_dereference(vxlan->cfg);
struct vxlan_vni_node *vninode;
__be32 v = cpu_to_be32(vni);
bool changed = false;
@@ -734,7 +739,7 @@ static int vxlan_vni_add(struct vxlan_dev *vxlan,
if (vxlan_vnifilter_lookup(vxlan, v))
return vxlan_vni_update(vxlan, vg, v, group, &changed, extack);
- err = vxlan_vni_in_use(vxlan->net, vxlan, &vxlan->cfg, v);
+ err = vxlan_vni_in_use(vxlan->net, vxlan, cfg, v);
if (err) {
NL_SET_ERR_MSG(extack, "VNI in use");
return err;
@@ -943,6 +948,7 @@ static int vxlan_vnifilter_process(struct sk_buff *skb, struct nlmsghdr *nlh,
struct netlink_ext_ack *extack)
{
struct net *net = sock_net(skb->sk);
+ const struct vxlan_config *cfg;
struct tunnel_msg *tmsg;
struct vxlan_dev *vxlan;
struct net_device *dev;
@@ -967,8 +973,9 @@ static int vxlan_vnifilter_process(struct sk_buff *skb, struct nlmsghdr *nlh,
}
vxlan = netdev_priv(dev);
+ cfg = rtnl_dereference(vxlan->cfg);
- if (!(vxlan->cfg.flags & VXLAN_F_VNIFILTER))
+ if (!(cfg->flags & VXLAN_F_VNIFILTER))
return -EOPNOTSUPP;
nlmsg_for_each_attr_type(attr, VXLAN_VNIFILTER_ENTRY, nlh,
diff --git a/include/net/vxlan.h b/include/net/vxlan.h
index dfba89695efcfa46cbc56f005763e1086ce67b23..7321768b0f2c1e81467d90153c422ee576aaefa9 100644
--- a/include/net/vxlan.h
+++ b/include/net/vxlan.h
@@ -229,6 +229,7 @@ struct vxlan_config {
bool no_share;
enum ifla_vxlan_df df;
struct vxlanhdr reserved_bits;
+ struct rcu_head rcu;
};
enum {
@@ -301,7 +302,7 @@ struct vxlan_dev {
unsigned int addrcnt;
struct gro_cells gro_cells;
- struct vxlan_config cfg;
+ struct vxlan_config __rcu *cfg;
struct vxlan_vni_group __rcu *vnigrp;
diff --git a/net/openvswitch/vport-vxlan.c b/net/openvswitch/vport-vxlan.c
index b0a084864a858453715d8e51b3e95cc7b2d9370f..9b1101f6ce1e1ceee3032ce94b0886846e6e1f84 100644
--- a/net/openvswitch/vport-vxlan.c
+++ b/net/openvswitch/vport-vxlan.c
@@ -22,27 +22,40 @@ static struct vport_ops ovs_vxlan_netdev_vport_ops;
static int vxlan_get_options(const struct vport *vport, struct sk_buff *skb)
{
struct vxlan_dev *vxlan = netdev_priv(vport->dev);
- const struct vxlan_config *cfg = &vxlan->cfg;
- __be16 dst_port = cfg->dst_port;
+ const struct vxlan_config *cfg;
+ __be16 dst_port;
+ int err = 0;
- if (nla_put_u16(skb, OVS_TUNNEL_ATTR_DST_PORT, ntohs(dst_port)))
- return -EMSGSIZE;
+ rcu_read_lock();
+ cfg = rcu_dereference(vxlan->cfg);
+ dst_port = cfg->dst_port;
+
+ if (nla_put_u16(skb, OVS_TUNNEL_ATTR_DST_PORT, ntohs(dst_port))) {
+ err = -EMSGSIZE;
+ goto out;
+ }
if (cfg->flags & VXLAN_F_GBP) {
struct nlattr *exts;
exts = nla_nest_start_noflag(skb, OVS_TUNNEL_ATTR_EXTENSION);
- if (!exts)
- return -EMSGSIZE;
+ if (!exts) {
+ err = -EMSGSIZE;
+ goto out;
+ }
if (cfg->flags & VXLAN_F_GBP &&
- nla_put_flag(skb, OVS_VXLAN_EXT_GBP))
- return -EMSGSIZE;
+ nla_put_flag(skb, OVS_VXLAN_EXT_GBP)) {
+ err = -EMSGSIZE;
+ goto out;
+ }
nla_nest_end(skb, exts);
}
- return 0;
+out:
+ rcu_read_unlock();
+ return err;
}
static const struct nla_policy exts_policy[OVS_VXLAN_EXT_MAX + 1] = {
--
2.55.0.795.g602f6c329a-goog
^ permalink raw reply related [flat|nested] 10+ messages in thread
* [PATCH net-next 3/3] vxlan: no longer rely on RTNL in vxlan_fill_info()
2026-07-08 16:04 [PATCH net-next 0/3] vxlan: RCU protect vxlan_config and enable RTNL-less fill_info Eric Dumazet
2026-07-08 16:04 ` [PATCH net-next 1/3] vxlan: pass vxlan_config pointer to helper functions Eric Dumazet
2026-07-08 16:04 ` [PATCH net-next 2/3] vxlan: convert configuration to RCU protection Eric Dumazet
@ 2026-07-08 16:04 ` Eric Dumazet
2026-07-09 4:55 ` Pavan Chebbi
2026-07-10 14:20 ` [PATCH net-next 0/3] vxlan: RCU protect vxlan_config and enable RTNL-less fill_info Paolo Abeni
3 siblings, 1 reply; 10+ messages in thread
From: Eric Dumazet @ 2026-07-08 16:04 UTC (permalink / raw)
To: David S . Miller, Jakub Kicinski, Paolo Abeni
Cc: Simon Horman, Kuniyuki Iwashima, Ido Schimmel, Andrew Lunn,
netdev, eric.dumazet, Eric Dumazet
Now that vxlan->cfg is RCU-protected, we can update vxlan_fill_info()
to run under RCU read lock instead of relying on RTNL.
This completes the transition to RTNL-less link info dumping for VXLAN.
Signed-off-by: Eric Dumazet <edumazet@google.com>
---
drivers/net/vxlan/vxlan_core.c | 13 +++++++++++--
1 file changed, 11 insertions(+), 2 deletions(-)
diff --git a/drivers/net/vxlan/vxlan_core.c b/drivers/net/vxlan/vxlan_core.c
index 96ac74fb1eb064d4e280af8ffa68cff37aacbca7..86b853d7739639b93b6468bc64a217c562b4804c 100644
--- a/drivers/net/vxlan/vxlan_core.c
+++ b/drivers/net/vxlan/vxlan_core.c
@@ -4674,10 +4674,16 @@ static int vxlan_fill_info(struct sk_buff *skb, const struct net_device *dev)
struct ifla_vxlan_port_range ports;
const struct vxlan_config *cfg;
const struct vxlan_rdst *dst;
+ int err = 0;
dst = &vxlan->default_dst;
- cfg = rtnl_dereference(vxlan->cfg);
+ rcu_read_lock();
+ cfg = rcu_dereference(vxlan->cfg);
+ if (!cfg) {
+ err = -ENODEV;
+ goto out;
+ }
ports.low = htons(cfg->port_min);
ports.high = htons(cfg->port_max);
@@ -4777,9 +4783,12 @@ static int vxlan_fill_info(struct sk_buff *skb, const struct net_device *dev)
&cfg->reserved_bits))
goto nla_put_failure;
- return 0;
+out:
+ rcu_read_unlock();
+ return err;
nla_put_failure:
+ rcu_read_unlock();
return -EMSGSIZE;
}
--
2.55.0.795.g602f6c329a-goog
^ permalink raw reply related [flat|nested] 10+ messages in thread
* Re: [PATCH net-next 1/3] vxlan: pass vxlan_config pointer to helper functions
2026-07-08 16:04 ` [PATCH net-next 1/3] vxlan: pass vxlan_config pointer to helper functions Eric Dumazet
@ 2026-07-09 4:53 ` Pavan Chebbi
0 siblings, 0 replies; 10+ messages in thread
From: Pavan Chebbi @ 2026-07-09 4:53 UTC (permalink / raw)
To: Eric Dumazet
Cc: David S . Miller, Jakub Kicinski, Paolo Abeni, Simon Horman,
Kuniyuki Iwashima, Ido Schimmel, Andrew Lunn, netdev,
eric.dumazet
[-- Attachment #1: Type: text/plain, Size: 953 bytes --]
On Wed, Jul 8, 2026 at 9:55 PM Eric Dumazet <edumazet@google.com> wrote:
>
> In preparation for converting vxlan->cfg to an RCU-protected pointer,
> refactor internal helper functions in the RX, TX, MDB, VNIFILTER, and
> OVS paths to accept a pointer to struct vxlan_config (or pass flags/
> saddr_family where appropriate) rather than directly accessing
> vxlan->cfg.
<-->
> void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev,
> + const struct vxlan_config *cfg,
> __be32 default_vni, struct vxlan_rdst *rdst, bool did_rsc)
> {
> + unsigned int pkt_len = skb->len;
> + __be16 src_port = 0, dst_port;
> + struct dst_entry *ndst = NULL;
> + enum skb_drop_reason reason;
> + struct vxlan_dev *vxlan;
nit: Can this be pushed few lines below for the reverse xmas tree order..
otherwise LGTM.
Reviewed-by: Pavan Chebbi <pavan.chebbi@broadcom.com>
[-- Attachment #2: S/MIME Cryptographic Signature --]
[-- Type: application/pkcs7-signature, Size: 5469 bytes --]
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH net-next 2/3] vxlan: convert configuration to RCU protection
2026-07-08 16:04 ` [PATCH net-next 2/3] vxlan: convert configuration to RCU protection Eric Dumazet
@ 2026-07-09 4:54 ` Pavan Chebbi
0 siblings, 0 replies; 10+ messages in thread
From: Pavan Chebbi @ 2026-07-09 4:54 UTC (permalink / raw)
To: Eric Dumazet
Cc: David S . Miller, Jakub Kicinski, Paolo Abeni, Simon Horman,
Kuniyuki Iwashima, Ido Schimmel, Andrew Lunn, netdev,
eric.dumazet
[-- Attachment #1: Type: text/plain, Size: 1138 bytes --]
On Wed, Jul 8, 2026 at 9:57 PM Eric Dumazet <edumazet@google.com> wrote:
>
> In order to allow lockless readers in future patches, dynamically allocate
> 'struct vxlan_config' and convert 'vxlan->cfg' to an RCU protected pointer.
>
> Updating configuration via vxlan_changelink() or __vxlan_dev_create()
> now allocates a new struct vxlan_config, initializes it, and uses
> rcu_assign_pointer() to publish it, freeing the previous config with
> kfree_rcu().
>
> Signed-off-by: Eric Dumazet <edumazet@google.com>
> ---
> .../mellanox/mlxsw/spectrum_nve_vxlan.c | 14 +-
> .../mellanox/mlxsw/spectrum_switchdev.c | 57 ++-
> drivers/net/vxlan/vxlan_core.c | 336 +++++++++++-------
> drivers/net/vxlan/vxlan_mdb.c | 29 +-
> drivers/net/vxlan/vxlan_multicast.c | 12 +-
> drivers/net/vxlan/vxlan_vnifilter.c | 21 +-
> include/net/vxlan.h | 3 +-
> net/openvswitch/vport-vxlan.c | 31 +-
> 8 files changed, 331 insertions(+), 172 deletions(-)
>
Reviewed-by: Pavan Chebbi <pavan.chebbi@broadcom.com>
[-- Attachment #2: S/MIME Cryptographic Signature --]
[-- Type: application/pkcs7-signature, Size: 5469 bytes --]
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH net-next 3/3] vxlan: no longer rely on RTNL in vxlan_fill_info()
2026-07-08 16:04 ` [PATCH net-next 3/3] vxlan: no longer rely on RTNL in vxlan_fill_info() Eric Dumazet
@ 2026-07-09 4:55 ` Pavan Chebbi
0 siblings, 0 replies; 10+ messages in thread
From: Pavan Chebbi @ 2026-07-09 4:55 UTC (permalink / raw)
To: Eric Dumazet
Cc: David S . Miller, Jakub Kicinski, Paolo Abeni, Simon Horman,
Kuniyuki Iwashima, Ido Schimmel, Andrew Lunn, netdev,
eric.dumazet
[-- Attachment #1: Type: text/plain, Size: 519 bytes --]
On Wed, Jul 8, 2026 at 10:01 PM Eric Dumazet <edumazet@google.com> wrote:
>
> Now that vxlan->cfg is RCU-protected, we can update vxlan_fill_info()
> to run under RCU read lock instead of relying on RTNL.
>
> This completes the transition to RTNL-less link info dumping for VXLAN.
>
> Signed-off-by: Eric Dumazet <edumazet@google.com>
> ---
> drivers/net/vxlan/vxlan_core.c | 13 +++++++++++--
> 1 file changed, 11 insertions(+), 2 deletions(-)
>
Reviewed-by: Pavan Chebbi <pavan.chebbi@broadcom.com>
[-- Attachment #2: S/MIME Cryptographic Signature --]
[-- Type: application/pkcs7-signature, Size: 5469 bytes --]
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH net-next 0/3] vxlan: RCU protect vxlan_config and enable RTNL-less fill_info
2026-07-08 16:04 [PATCH net-next 0/3] vxlan: RCU protect vxlan_config and enable RTNL-less fill_info Eric Dumazet
` (2 preceding siblings ...)
2026-07-08 16:04 ` [PATCH net-next 3/3] vxlan: no longer rely on RTNL in vxlan_fill_info() Eric Dumazet
@ 2026-07-10 14:20 ` Paolo Abeni
2026-07-10 14:22 ` Paolo Abeni
3 siblings, 1 reply; 10+ messages in thread
From: Paolo Abeni @ 2026-07-10 14:20 UTC (permalink / raw)
To: Eric Dumazet, David S . Miller, Jakub Kicinski
Cc: Simon Horman, Kuniyuki Iwashima, Ido Schimmel, Andrew Lunn,
netdev, eric.dumazet
On 7/8/26 6:04 PM, Eric Dumazet wrote:
> This patch series converts VXLAN configuration (`struct vxlan_config`)
> to be RCU-protected, allowing lockless readers and enabling RTNL-less
> `vxlan_fill_info()` netlink link info dumping.
>
> Work has been split into three distinct parts:
>
> 1. Refactor helper functions in RX, TX, MDB, VNIFILTER, and OVS paths to
> accept a `const struct vxlan_config *cfg` pointer (or pass specific
> fields such as `flags`/`saddr_family`) rather than accessing
> `vxlan->cfg` directly.
>
> 2. Convert `vxlan->cfg` from an embedded struct into an RCU-protected
> pointer (`struct vxlan_config __rcu *cfg`), dynamically allocated on
> device creation or changelink, and freed via `kfree_rcu()`. Update
> offload drivers (`mlxsw`) and OVS (`vport-vxlan.c`) accordingly.
>
> 3. Update `vxlan_fill_info()` to run under `rcu_read_lock()` instead of
> relying on RTNL lock, completing the transition to lockless link info
> dumping for VXLAN devices.
I *suspect* this is triggering RCU splat in forwarding self-tests:
[ 1955.481960][T11074] =============================
[ 1955.482076][T11074] WARNING: suspicious RCU usage
[ 1955.482190][T11074] 7.2.0-rc2-virtme #1 Not tainted
[ 1955.482304][T11074] -----------------------------
[ 1955.482886][T11074] drivers/net/vxlan/vxlan_core.c:1872 suspicious rcu_dereference_check() usage!
[ 1955.483087][T11074]
[ 1955.483087][T11074] other info that might help us debug this:
[ 1955.483087][T11074]
[ 1955.483312][T11074]
[ 1955.483312][T11074] rcu_scheduler_active = 2, debug_locks = 1
[ 1955.483489][T11074] 1 lock held by arping/11074:
[ 1955.483623][T11074] #0: ffffffffbad91d60 (rcu_read_lock_bh){....}-{1:3}, at: __dev_queue_xmit+0x204/0x1b90
[ 1955.483836][T11074]
[ 1955.483836][T11074] stack backtrace:
[ 1955.483977][T11074] CPU: 0 UID: 0 PID: 11074 Comm: arping Not tainted 7.2.0-rc2-virtme #1 PREEMPT(full)
[ 1955.483980][T11074] Hardware name: Bochs Bochs, BIOS Bochs 01/01/2011
[ 1955.483982][T11074] Call Trace:
[ 1955.483983][T11074] <TASK>
[ 1955.483984][T11074] dump_stack_lvl+0x6f/0xa0
[ 1955.483990][T11074] lockdep_rcu_suspicious.cold+0x37/0x89
[ 1955.483995][T11074] arp_reduce.isra.0+0x9b9/0x1080 [vxlan]
[ 1955.484003][T11074] ? mark_usage+0x61/0x170
[ 1955.484007][T11074] ? __lock_acquire+0x518/0xc20
[ 1955.484010][T11074] ? vxlan_nl2conf+0x5b30/0x5b30 [vxlan]
[ 1955.484015][T11074] ? lock_acquire.part.0+0xbc/0x260
[ 1955.484017][T11074] ? find_held_lock+0x2b/0x80
[ 1955.484020][T11074] ? __lock_release.isra.0+0x6b/0x1a0
[ 1955.484025][T11074] ? vxlan_xmit+0x128b/0x1660 [vxlan]
[ 1955.484029][T11074] vxlan_xmit+0x128b/0x1660 [vxlan]
[ 1955.484034][T11074] ? rcu_lockdep_current_cpu_online+0x39/0x1b0
[ 1955.484039][T11074] dev_hard_start_xmit+0xf4/0x620
[ 1955.484042][T11074] ? validate_xmit_skb+0x72/0x9a0
[ 1955.484045][T11074] __dev_queue_xmit+0xc1a/0x1b90
[ 1955.484047][T11074] ? _copy_from_iter+0x1bb/0x1810
[ 1955.484052][T11074] ? __alloc_skb+0x342/0x5f0
[ 1955.484055][T11074] ? _copy_from_iter_flushcache+0x1970/0x1970
[ 1955.484057][T11074] ? ref_tracker_alloc+0x2e7/0x430
[ 1955.484060][T11074] ? netdev_core_pick_tx+0x2c0/0x2c0
[ 1955.484064][T11074] ? packet_parse_headers+0x325/0x830
[ 1955.484067][T11074] ? __asan_memset+0x27/0x50
[ 1955.484070][T11074] ? packet_parse_headers+0x325/0x830
[ 1955.484073][T11074] ? skb_copy_datagram_from_iter+0xf3/0x6a0
[ 1955.484075][T11074] ? packet_release+0xc70/0xc70
[ 1955.484079][T11074] packet_snd+0xfad/0x1990
[ 1955.484085][T11074] ? tpacket_snd+0x19e0/0x19e0
[ 1955.484087][T11074] ? __might_fault+0x97/0x140
[ 1955.484093][T11074] ? __might_fault+0x97/0x140
[ 1955.484095][T11074] ? __might_fault+0x97/0x140
[ 1955.484098][T11074] __sys_sendto+0x2aa/0x400
[ 1955.484101][T11074] ? __ia32_sys_getpeername+0xd0/0xd0
[ 1955.484110][T11074] ? do_user_addr_fault+0x325/0xe30
[ 1955.484116][T11074] __x64_sys_sendto+0xe4/0x1f0
[ 1955.484117][T11074] ? trace_irq_enable.constprop.0+0x9b/0x160
[ 1955.484121][T11074] ? lockdep_hardirqs_on+0x8c/0x130
[ 1955.484123][T11074] ? do_syscall_64+0x82/0x590
[ 1955.484126][T11074] do_syscall_64+0x117/0x590
[ 1955.484127][T11074] ? trace_hardirqs_off+0xd/0x30
[ 1955.484129][T11074] ? exc_page_fault+0xee/0x100
[ 1955.484132][T11074] entry_SYSCALL_64_after_hwframe+0x4b/0x53
[ 1955.484134][T11074] RIP: 0033:0x7f138c3ec54e
[ 1955.484138][T11074] Code: 4d 89 d8 e8 b4 bd 00 00 4c 8b 5d f8 41 8b 93 08 03 00 00 59 5e 48 83 f8 fc 74 11 c9 c3 0f 1f 80 00 00 00 00 48 8b 45 10 0f 05 <c9> c3 83 e2 39 83 fa 08 75 e7 e8 03 ff ff ff 0f 1f 00 f3 0f 1e fa
[ 1955.484139][T11074] RSP: 002b:00007ffc648f74e0 EFLAGS: 00000202 ORIG_RAX: 000000000000002c
[ 1955.484142][T11074] RAX: ffffffffffffffda RBX: 000000000000001c RCX: 00007f138c3ec54e
[ 1955.484143][T11074] RDX: 000000000000001c RSI: 00007ffc648f7560 RDI: 0000000000000005
[ 1955.484144][T11074] RBP: 00007ffc648f74f0 R08: 00007ffc648f79e0 R09: 0000000000000014
[ 1955.484145][T11074] R10: 0000000000000000 R11: 0000000000000202 R12: 0000000000000000
[ 1955.484146][T11074] R13: 00007ffc648f7910 R14: ffffffffffffffe8 R15: 0000000000000000
full log at:
https://netdev-ctrl.bots.linux.dev/logs/vmksft/net-dbg/results/728082/83-test-vxlan-nh-sh/stderr
Tentatively setting the series to 'changes requested'
/P
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH net-next 0/3] vxlan: RCU protect vxlan_config and enable RTNL-less fill_info
2026-07-10 14:20 ` [PATCH net-next 0/3] vxlan: RCU protect vxlan_config and enable RTNL-less fill_info Paolo Abeni
@ 2026-07-10 14:22 ` Paolo Abeni
2026-07-10 15:03 ` Eric Dumazet
0 siblings, 1 reply; 10+ messages in thread
From: Paolo Abeni @ 2026-07-10 14:22 UTC (permalink / raw)
To: Eric Dumazet, David S . Miller, Jakub Kicinski
Cc: Simon Horman, Kuniyuki Iwashima, Ido Schimmel, Andrew Lunn,
netdev, eric.dumazet
On 7/10/26 4:20 PM, Paolo Abeni wrote:
> On 7/8/26 6:04 PM, Eric Dumazet wrote:
>> This patch series converts VXLAN configuration (`struct vxlan_config`)
>> to be RCU-protected, allowing lockless readers and enabling RTNL-less
>> `vxlan_fill_info()` netlink link info dumping.
>>
>> Work has been split into three distinct parts:
>>
>> 1. Refactor helper functions in RX, TX, MDB, VNIFILTER, and OVS paths to
>> accept a `const struct vxlan_config *cfg` pointer (or pass specific
>> fields such as `flags`/`saddr_family`) rather than accessing
>> `vxlan->cfg` directly.
>>
>> 2. Convert `vxlan->cfg` from an embedded struct into an RCU-protected
>> pointer (`struct vxlan_config __rcu *cfg`), dynamically allocated on
>> device creation or changelink, and freed via `kfree_rcu()`. Update
>> offload drivers (`mlxsw`) and OVS (`vport-vxlan.c`) accordingly.
>>
>> 3. Update `vxlan_fill_info()` to run under `rcu_read_lock()` instead of
>> relying on RTNL lock, completing the transition to lockless link info
>> dumping for VXLAN devices.
>
> I *suspect* this is triggering RCU splat in forwarding self-tests:
>
> [ 1955.481960][T11074] =============================
oops, sashiko already spotted it:
https://sashiko.dev/#/patchset/20260708160411.1355008-1-edumazet%40google.com
/P
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH net-next 0/3] vxlan: RCU protect vxlan_config and enable RTNL-less fill_info
2026-07-10 14:22 ` Paolo Abeni
@ 2026-07-10 15:03 ` Eric Dumazet
0 siblings, 0 replies; 10+ messages in thread
From: Eric Dumazet @ 2026-07-10 15:03 UTC (permalink / raw)
To: Paolo Abeni
Cc: David S . Miller, Jakub Kicinski, Simon Horman, Kuniyuki Iwashima,
Ido Schimmel, Andrew Lunn, netdev, eric.dumazet
On Fri, Jul 10, 2026 at 4:22 PM Paolo Abeni <pabeni@redhat.com> wrote:
>
> On 7/10/26 4:20 PM, Paolo Abeni wrote:
> > On 7/8/26 6:04 PM, Eric Dumazet wrote:
> >> This patch series converts VXLAN configuration (`struct vxlan_config`)
> >> to be RCU-protected, allowing lockless readers and enabling RTNL-less
> >> `vxlan_fill_info()` netlink link info dumping.
> >>
> >> Work has been split into three distinct parts:
> >>
> >> 1. Refactor helper functions in RX, TX, MDB, VNIFILTER, and OVS paths to
> >> accept a `const struct vxlan_config *cfg` pointer (or pass specific
> >> fields such as `flags`/`saddr_family`) rather than accessing
> >> `vxlan->cfg` directly.
> >>
> >> 2. Convert `vxlan->cfg` from an embedded struct into an RCU-protected
> >> pointer (`struct vxlan_config __rcu *cfg`), dynamically allocated on
> >> device creation or changelink, and freed via `kfree_rcu()`. Update
> >> offload drivers (`mlxsw`) and OVS (`vport-vxlan.c`) accordingly.
> >>
> >> 3. Update `vxlan_fill_info()` to run under `rcu_read_lock()` instead of
> >> relying on RTNL lock, completing the transition to lockless link info
> >> dumping for VXLAN devices.
> >
> > I *suspect* this is triggering RCU splat in forwarding self-tests:
> >
> > [ 1955.481960][T11074] =============================
> oops, sashiko already spotted it:
>
> https://sashiko.dev/#/patchset/20260708160411.1355008-1-edumazet%40google.com
LOCKDEP inablility to understand rcu_read_lock() and
rcu_read_lock_bh() is quite something.
rcu_bh has disappeared a long time ago (it is kept for historical reasons only).
Perhaps we should finally replace it with standard rcu, to avoid
stacking rcu_read_lock() and rcu_read_lock_bh().
^ permalink raw reply [flat|nested] 10+ messages in thread
end of thread, other threads:[~2026-07-10 15:03 UTC | newest]
Thread overview: 10+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-08 16:04 [PATCH net-next 0/3] vxlan: RCU protect vxlan_config and enable RTNL-less fill_info Eric Dumazet
2026-07-08 16:04 ` [PATCH net-next 1/3] vxlan: pass vxlan_config pointer to helper functions Eric Dumazet
2026-07-09 4:53 ` Pavan Chebbi
2026-07-08 16:04 ` [PATCH net-next 2/3] vxlan: convert configuration to RCU protection Eric Dumazet
2026-07-09 4:54 ` Pavan Chebbi
2026-07-08 16:04 ` [PATCH net-next 3/3] vxlan: no longer rely on RTNL in vxlan_fill_info() Eric Dumazet
2026-07-09 4:55 ` Pavan Chebbi
2026-07-10 14:20 ` [PATCH net-next 0/3] vxlan: RCU protect vxlan_config and enable RTNL-less fill_info Paolo Abeni
2026-07-10 14:22 ` Paolo Abeni
2026-07-10 15:03 ` Eric Dumazet
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox